berkeley_library-tind 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/build.yml +18 -0
  3. data/.gitignore +388 -0
  4. data/.idea/inspectionProfiles/Project_Default.xml +20 -0
  5. data/.idea/misc.xml +4 -0
  6. data/.idea/modules.xml +8 -0
  7. data/.idea/tind.iml +138 -0
  8. data/.idea/vcs.xml +6 -0
  9. data/.rubocop.yml +334 -0
  10. data/.ruby-version +1 -0
  11. data/.simplecov +8 -0
  12. data/.yardopts +1 -0
  13. data/CHANGES.md +58 -0
  14. data/Dockerfile +57 -0
  15. data/Gemfile +3 -0
  16. data/Jenkinsfile +18 -0
  17. data/LICENSE.md +21 -0
  18. data/README.md +73 -0
  19. data/Rakefile +20 -0
  20. data/berkeley_library-tind.gemspec +50 -0
  21. data/bin/tind-export +14 -0
  22. data/docker-compose.yml +15 -0
  23. data/lib/berkeley_library/tind.rb +3 -0
  24. data/lib/berkeley_library/tind/api.rb +1 -0
  25. data/lib/berkeley_library/tind/api/api.rb +132 -0
  26. data/lib/berkeley_library/tind/api/api_exception.rb +131 -0
  27. data/lib/berkeley_library/tind/api/collection.rb +82 -0
  28. data/lib/berkeley_library/tind/api/date_range.rb +67 -0
  29. data/lib/berkeley_library/tind/api/format.rb +32 -0
  30. data/lib/berkeley_library/tind/api/search.rb +100 -0
  31. data/lib/berkeley_library/tind/config.rb +103 -0
  32. data/lib/berkeley_library/tind/export.rb +1 -0
  33. data/lib/berkeley_library/tind/export/column.rb +54 -0
  34. data/lib/berkeley_library/tind/export/column_group.rb +144 -0
  35. data/lib/berkeley_library/tind/export/column_group_list.rb +131 -0
  36. data/lib/berkeley_library/tind/export/column_width_calculator.rb +76 -0
  37. data/lib/berkeley_library/tind/export/config.rb +154 -0
  38. data/lib/berkeley_library/tind/export/csv_exporter.rb +29 -0
  39. data/lib/berkeley_library/tind/export/export.rb +47 -0
  40. data/lib/berkeley_library/tind/export/export_command.rb +168 -0
  41. data/lib/berkeley_library/tind/export/export_exception.rb +8 -0
  42. data/lib/berkeley_library/tind/export/export_format.rb +67 -0
  43. data/lib/berkeley_library/tind/export/exporter.rb +105 -0
  44. data/lib/berkeley_library/tind/export/filter.rb +52 -0
  45. data/lib/berkeley_library/tind/export/no_results_error.rb +7 -0
  46. data/lib/berkeley_library/tind/export/ods_exporter.rb +138 -0
  47. data/lib/berkeley_library/tind/export/row.rb +24 -0
  48. data/lib/berkeley_library/tind/export/row_metrics.rb +18 -0
  49. data/lib/berkeley_library/tind/export/table.rb +175 -0
  50. data/lib/berkeley_library/tind/export/table_metrics.rb +116 -0
  51. data/lib/berkeley_library/tind/marc.rb +1 -0
  52. data/lib/berkeley_library/tind/marc/xml_reader.rb +144 -0
  53. data/lib/berkeley_library/tind/module_info.rb +14 -0
  54. data/lib/berkeley_library/util/arrays.rb +178 -0
  55. data/lib/berkeley_library/util/logging.rb +1 -0
  56. data/lib/berkeley_library/util/ods/spreadsheet.rb +170 -0
  57. data/lib/berkeley_library/util/ods/xml/content_doc.rb +26 -0
  58. data/lib/berkeley_library/util/ods/xml/document_node.rb +57 -0
  59. data/lib/berkeley_library/util/ods/xml/element_node.rb +106 -0
  60. data/lib/berkeley_library/util/ods/xml/loext/table_protection.rb +26 -0
  61. data/lib/berkeley_library/util/ods/xml/manifest/file_entry.rb +42 -0
  62. data/lib/berkeley_library/util/ods/xml/manifest/manifest.rb +73 -0
  63. data/lib/berkeley_library/util/ods/xml/manifest_doc.rb +26 -0
  64. data/lib/berkeley_library/util/ods/xml/namespace.rb +46 -0
  65. data/lib/berkeley_library/util/ods/xml/office/automatic_styles.rb +181 -0
  66. data/lib/berkeley_library/util/ods/xml/office/body.rb +17 -0
  67. data/lib/berkeley_library/util/ods/xml/office/document_content.rb +98 -0
  68. data/lib/berkeley_library/util/ods/xml/office/document_styles.rb +39 -0
  69. data/lib/berkeley_library/util/ods/xml/office/font_face_decls.rb +30 -0
  70. data/lib/berkeley_library/util/ods/xml/office/scripts.rb +17 -0
  71. data/lib/berkeley_library/util/ods/xml/office/spreadsheet.rb +37 -0
  72. data/lib/berkeley_library/util/ods/xml/office/styles.rb +39 -0
  73. data/lib/berkeley_library/util/ods/xml/style/cell_style.rb +58 -0
  74. data/lib/berkeley_library/util/ods/xml/style/column_style.rb +36 -0
  75. data/lib/berkeley_library/util/ods/xml/style/default_style.rb +31 -0
  76. data/lib/berkeley_library/util/ods/xml/style/family.rb +85 -0
  77. data/lib/berkeley_library/util/ods/xml/style/font_face.rb +46 -0
  78. data/lib/berkeley_library/util/ods/xml/style/paragraph_properties.rb +30 -0
  79. data/lib/berkeley_library/util/ods/xml/style/row_style.rb +37 -0
  80. data/lib/berkeley_library/util/ods/xml/style/style.rb +44 -0
  81. data/lib/berkeley_library/util/ods/xml/style/table_cell_properties.rb +40 -0
  82. data/lib/berkeley_library/util/ods/xml/style/table_column_properties.rb +30 -0
  83. data/lib/berkeley_library/util/ods/xml/style/table_properties.rb +25 -0
  84. data/lib/berkeley_library/util/ods/xml/style/table_row_properties.rb +28 -0
  85. data/lib/berkeley_library/util/ods/xml/style/table_style.rb +27 -0
  86. data/lib/berkeley_library/util/ods/xml/style/text_properties.rb +52 -0
  87. data/lib/berkeley_library/util/ods/xml/styles_doc.rb +26 -0
  88. data/lib/berkeley_library/util/ods/xml/table/named_expressions.rb +17 -0
  89. data/lib/berkeley_library/util/ods/xml/table/repeatable.rb +38 -0
  90. data/lib/berkeley_library/util/ods/xml/table/table.rb +193 -0
  91. data/lib/berkeley_library/util/ods/xml/table/table_cell.rb +46 -0
  92. data/lib/berkeley_library/util/ods/xml/table/table_column.rb +43 -0
  93. data/lib/berkeley_library/util/ods/xml/table/table_row.rb +136 -0
  94. data/lib/berkeley_library/util/ods/xml/text/p.rb +118 -0
  95. data/lib/berkeley_library/util/paths.rb +111 -0
  96. data/lib/berkeley_library/util/stringios.rb +30 -0
  97. data/lib/berkeley_library/util/strings.rb +42 -0
  98. data/lib/berkeley_library/util/sys_exits.rb +15 -0
  99. data/lib/berkeley_library/util/times.rb +22 -0
  100. data/lib/berkeley_library/util/uris.rb +44 -0
  101. data/lib/berkeley_library/util/uris/appender.rb +162 -0
  102. data/lib/berkeley_library/util/uris/requester.rb +62 -0
  103. data/lib/berkeley_library/util/uris/validator.rb +32 -0
  104. data/rakelib/bundle.rake +8 -0
  105. data/rakelib/coverage.rake +11 -0
  106. data/rakelib/gem.rake +54 -0
  107. data/rakelib/rubocop.rake +18 -0
  108. data/rakelib/spec.rake +2 -0
  109. data/spec/.rubocop.yml +40 -0
  110. data/spec/berkeley_library/tind/api/api_exception_spec.rb +91 -0
  111. data/spec/berkeley_library/tind/api/api_spec.rb +143 -0
  112. data/spec/berkeley_library/tind/api/collection_spec.rb +74 -0
  113. data/spec/berkeley_library/tind/api/date_range_spec.rb +110 -0
  114. data/spec/berkeley_library/tind/api/format_spec.rb +54 -0
  115. data/spec/berkeley_library/tind/api/search_spec.rb +364 -0
  116. data/spec/berkeley_library/tind/config_spec.rb +86 -0
  117. data/spec/berkeley_library/tind/export/column_group_spec.rb +29 -0
  118. data/spec/berkeley_library/tind/export/column_spec.rb +43 -0
  119. data/spec/berkeley_library/tind/export/config_spec.rb +206 -0
  120. data/spec/berkeley_library/tind/export/export_command_spec.rb +169 -0
  121. data/spec/berkeley_library/tind/export/export_format_spec.rb +59 -0
  122. data/spec/berkeley_library/tind/export/export_matcher.rb +112 -0
  123. data/spec/berkeley_library/tind/export/export_spec.rb +150 -0
  124. data/spec/berkeley_library/tind/export/exporter_spec.rb +125 -0
  125. data/spec/berkeley_library/tind/export/row_spec.rb +118 -0
  126. data/spec/berkeley_library/tind/export/table_spec.rb +322 -0
  127. data/spec/berkeley_library/tind/marc/xml_reader_spec.rb +93 -0
  128. data/spec/berkeley_library/util/arrays_spec.rb +340 -0
  129. data/spec/berkeley_library/util/ods/spreadsheet_spec.rb +124 -0
  130. data/spec/berkeley_library/util/ods/xml/content_doc_spec.rb +121 -0
  131. data/spec/berkeley_library/util/ods/xml/manifest/file_entry_spec.rb +27 -0
  132. data/spec/berkeley_library/util/ods/xml/manifest/manifest_spec.rb +33 -0
  133. data/spec/berkeley_library/util/ods/xml/office/document_content_spec.rb +60 -0
  134. data/spec/berkeley_library/util/ods/xml/style/automatic_styles_spec.rb +37 -0
  135. data/spec/berkeley_library/util/ods/xml/style/family_spec.rb +57 -0
  136. data/spec/berkeley_library/util/ods/xml/table/table_row_spec.rb +179 -0
  137. data/spec/berkeley_library/util/ods/xml/table/table_spec.rb +218 -0
  138. data/spec/berkeley_library/util/paths_spec.rb +90 -0
  139. data/spec/berkeley_library/util/stringios_spec.rb +34 -0
  140. data/spec/berkeley_library/util/strings_spec.rb +27 -0
  141. data/spec/berkeley_library/util/times_spec.rb +39 -0
  142. data/spec/berkeley_library/util/uris_spec.rb +118 -0
  143. data/spec/data/collection-names.txt +438 -0
  144. data/spec/data/collections.json +4827 -0
  145. data/spec/data/disjoint-records.xml +187 -0
  146. data/spec/data/record-184453.xml +58 -0
  147. data/spec/data/record-184458.xml +63 -0
  148. data/spec/data/record-187888.xml +78 -0
  149. data/spec/data/records-api-search-cjk-p1.xml +6381 -0
  150. data/spec/data/records-api-search-cjk-p2.xml +5 -0
  151. data/spec/data/records-api-search-p1.xml +4506 -0
  152. data/spec/data/records-api-search-p2.xml +4509 -0
  153. data/spec/data/records-api-search-p3.xml +4506 -0
  154. data/spec/data/records-api-search-p4.xml +4509 -0
  155. data/spec/data/records-api-search-p5.xml +4506 -0
  156. data/spec/data/records-api-search-p6.xml +2436 -0
  157. data/spec/data/records-api-search-p7.xml +5 -0
  158. data/spec/data/records-api-search.xml +234 -0
  159. data/spec/data/records-manual-search.xml +547 -0
  160. data/spec/spec_helper.rb +30 -0
  161. data/test/profile/table_from_records_profile.rb +46 -0
  162. metadata +585 -0
@@ -0,0 +1,32 @@
1
+ require 'uri'
2
+
3
+ module BerkeleyLibrary
4
+ module Util
5
+ module URIs
6
+ module Validator
7
+ class << self
8
+
9
+ # Returns the specified URL as a URI.
10
+ # @param url [String, URI] the URL.
11
+ # @return [URI] the URI.
12
+ # @raise [URI::InvalidURIError] if `url` cannot be parsed as a URI.
13
+ def uri_or_nil(url)
14
+ return unless url
15
+
16
+ # noinspection RubyYardReturnMatch
17
+ url.is_a?(URI) ? url : URI.parse(url.to_s)
18
+ end
19
+
20
+ # Returns the specified URL as a string.
21
+ # @param url [String, URI] the URL.
22
+ # @return [String] the URL.
23
+ # @raise [URI::InvalidURIError] if `url` cannot be parsed as a URI.
24
+ def url_str_or_nil(url)
25
+ uri = Validator.uri_or_nil(url)
26
+ uri.to_s if uri
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,8 @@
1
+ namespace :bundle do
2
+ desc 'Updates the ruby-advisory-db then runs bundle-audit'
3
+ task :audit do
4
+ require 'bundler/audit/cli'
5
+ Bundler::Audit::CLI.start ['update']
6
+ Bundler::Audit::CLI.start %w[check --ignore CVE-2015-9284]
7
+ end
8
+ end
@@ -0,0 +1,11 @@
1
+ require 'ci/reporter/rake/rspec'
2
+
3
+ # Configure CI::Reporter report generation
4
+ ENV['GENERATE_REPORTS'] ||= 'true'
5
+ ENV['CI_REPORTS'] = 'artifacts/rspec'
6
+
7
+ desc 'Run all specs in spec directory, with coverage'
8
+ task coverage: ['ci:setup:rspec'] do
9
+ ENV['COVERAGE'] ||= 'true'
10
+ Rake::Task[:spec].invoke
11
+ end
data/rakelib/gem.rake ADDED
@@ -0,0 +1,54 @@
1
+ require 'rubygems/gem_runner'
2
+ require 'berkeley_library/tind/module_info'
3
+
4
+ gem_root_module = BerkeleyLibrary::TIND
5
+
6
+ class << gem_root_module
7
+ def project_root
8
+ @project_root ||= File.expand_path('..', __dir__)
9
+ end
10
+
11
+ def artifacts_dir
12
+ return project_root unless ENV['CI']
13
+
14
+ @artifacts_dir ||= File.join(project_root, 'artifacts')
15
+ end
16
+
17
+ def gemspec_file
18
+ @gemspec_file ||= begin
19
+ gemspec_files = Dir.glob(File.expand_path('*.gemspec', project_root))
20
+ raise ArgumentError, "Too many .gemspecs: #{gemspec_files.join(', ')}" if gemspec_files.size > 1
21
+ raise ArgumentError, 'No .gemspec file found' if gemspec_files.empty?
22
+
23
+ gemspec_files[0]
24
+ end
25
+ end
26
+
27
+ def gemspec_basename
28
+ File.basename(gemspec_file)
29
+ end
30
+
31
+ def output_file
32
+ @output_file ||= begin
33
+ gem_name = File.basename(gemspec_file, '.*')
34
+ version = self::ModuleInfo::VERSION
35
+ basename = "#{gem_name}-#{version}.gem"
36
+ File.join(artifacts_dir, basename)
37
+ end
38
+ end
39
+
40
+ def output_file_relative
41
+ return File.basename(output_file) unless ENV['CI']
42
+
43
+ @output_file_relative ||= begin
44
+ artifacts_dir_relative = File.basename(artifacts_dir)
45
+ File.join(artifacts_dir_relative, File.basename(output_file))
46
+ end
47
+ end
48
+ end
49
+
50
+ desc "Build #{gem_root_module.gemspec_basename} as #{gem_root_module.output_file_relative}"
51
+ task :gem do
52
+ args = ['build', gem_root_module.gemspec_file, "--output=#{gem_root_module.output_file}"]
53
+ Gem::GemRunner.new.run(args)
54
+ end
@@ -0,0 +1,18 @@
1
+ require 'rubocop'
2
+ require 'rubocop/rake_task'
3
+
4
+ desc 'Run rubocop with HTML output'
5
+ RuboCop::RakeTask.new(:rubocop) do |cop|
6
+ output = ENV['RUBOCOP_OUTPUT'] || 'artifacts/rubocop/index.html'
7
+ puts "Writing RuboCop inspection report to #{output}"
8
+
9
+ cop.verbose = false
10
+ cop.formatters = ['html']
11
+ cop.options = ['--out', output]
12
+ end
13
+
14
+ desc 'Run RuboCop with auto-correct, and output results to console'
15
+ task :ra do
16
+ # b/c we want console output, we can't just use `rubocop:auto_correct`
17
+ RuboCop::CLI.new.run(['--safe-auto-correct'])
18
+ end
data/rakelib/spec.rake ADDED
@@ -0,0 +1,2 @@
1
+ require 'rspec/core/rake_task'
2
+ RSpec::Core::RakeTask.new(:spec)
data/spec/.rubocop.yml ADDED
@@ -0,0 +1,40 @@
1
+ inherit_from: ../.rubocop.yml
2
+
3
+ AllCops:
4
+ # Exclude generated files
5
+ Exclude:
6
+ - 'suite/**/*'
7
+
8
+ Style/ClassAndModuleChildren:
9
+ Enabled: false
10
+
11
+ Style/MultilineBlockChain:
12
+ Enabled: false
13
+
14
+ Style/ParallelAssignment:
15
+ Enabled: false
16
+
17
+ Layout/LineLength:
18
+ Enabled: false
19
+
20
+ Metrics/AbcSize:
21
+ Enabled: false
22
+
23
+ Metrics/BlockLength:
24
+ Enabled: false
25
+
26
+ Metrics/ClassLength:
27
+ Enabled: false
28
+
29
+ Metrics/ModuleLength:
30
+ Enabled: false
31
+
32
+ Metrics/MethodLength:
33
+ Enabled: false
34
+
35
+ ############################################################
36
+ # Added in Rubocop 0.89
37
+
38
+ # Sometimes we're testing the operator
39
+ Lint/BinaryOperatorWithIdenticalOperands:
40
+ Enabled: false
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+ require 'rest-client'
3
+
4
+ module BerkeleyLibrary
5
+ module TIND
6
+ module API
7
+ describe APIException do
8
+ describe :wrap do
9
+ it 'rejects nil' do
10
+ expect { APIException.wrap(nil) }.to raise_error(ArgumentError)
11
+ end
12
+
13
+ describe 'RestClient::RequestFailed' do
14
+ it 'extracts the status info and response' do
15
+ url = 'https://example.org'
16
+ params = { 'foo' => 'bar' }
17
+ expected_body = 'oops'
18
+ stub_request(:get, url).with(query: params).to_return(status: 500, body: expected_body)
19
+
20
+ expected_msg = 'the wrapper message'
21
+ expect do
22
+ RestClient.get(url, params: params)
23
+ rescue StandardError => e
24
+ raise APIException.wrap(e, url: url, params: params, detail: expected_msg)
25
+ end.to raise_error do |e|
26
+ expect(e).to be_a(APIException)
27
+ expect(e.cause).to be_a(RestClient::RequestFailed)
28
+ expect(e.status_code).to eq(500)
29
+ expect(e.status_message).to eq('500 Internal Server Error')
30
+ expect(e.body).to eq(expected_body)
31
+ expect(e.response).to be(e.cause.response)
32
+ expect(e.url).to eq(url)
33
+ expect(e.params).to eq(params)
34
+ end
35
+ end
36
+
37
+ it 'works with or without parameters' do
38
+ url = 'https://example.org'
39
+ expected_body = 'oops'
40
+ stub_request(:get, url).to_return(status: 500, body: expected_body)
41
+
42
+ expected_msg = 'the wrapper message'
43
+ expect do
44
+ RestClient.get(url)
45
+ rescue StandardError => e
46
+ raise APIException.wrap(e, url: url, detail: expected_msg)
47
+ end.to raise_error do |e|
48
+ expect(e).to be_a(APIException)
49
+ expect(e.cause).to be_a(RestClient::RequestFailed)
50
+ expect(e.status_code).to eq(500)
51
+ expect(e.status_message).to eq('500 Internal Server Error')
52
+ expect(e.body).to eq(expected_body)
53
+ expect(e.response).to be(e.cause.response)
54
+ expect(e.url).to eq(url)
55
+ expect(e.params).to be_nil
56
+ end
57
+ end
58
+ end
59
+
60
+ describe 'any exception' do
61
+ it 'wraps an exception' do
62
+ expected_msg = 'the message'
63
+ expect do
64
+
65
+ raise expected_msg
66
+ rescue StandardError => e
67
+ raise APIException.wrap(e)
68
+ end.to raise_error do |e|
69
+ expect(e).to be_a(APIException)
70
+ expect(e.message).to eq(expected_msg)
71
+ end
72
+ end
73
+
74
+ it 'overrides the message' do
75
+ expected_msg = 'the message'
76
+ expect do
77
+
78
+ raise "not #{expected_msg}"
79
+ rescue StandardError => e
80
+ raise APIException.wrap(e, msg: expected_msg)
81
+ end.to raise_error do |e|
82
+ expect(e).to be_a(APIException)
83
+ expect(e.message).to eq(expected_msg)
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,143 @@
1
+ require 'spec_helper'
2
+ require 'webmock'
3
+
4
+ module BerkeleyLibrary
5
+ module TIND
6
+ RSpec.shared_examples 'a missing key' do |bad_key|
7
+ before(:each) do
8
+ expect(BerkeleyLibrary::TIND::Config).to receive(:api_key).and_return(bad_key)
9
+ end
10
+
11
+ it "raises #{API::APIKeyNotSet}" do
12
+ failure_message = -> { "#{API::APIKeyNotSet} not raised for API key #{bad_key.inspect}" }
13
+ expect { API.get('some-endpoint') }.to raise_error(API::APIKeyNotSet), failure_message
14
+ end
15
+ end
16
+
17
+ RSpec.shared_examples 'a missing base URL' do |bad_url|
18
+ before(:each) do
19
+ allow(BerkeleyLibrary::TIND::Config).to receive(:base_uri).and_return(bad_url)
20
+ end
21
+
22
+ it "raises #{API::APIKeyNotSet}" do
23
+ failure_message = -> { "#{API::BaseURINotSet} not raised for API key #{bad_url.inspect}" }
24
+ expect { API.get('some-endpoint') }.to raise_error(API::BaseURINotSet), failure_message
25
+ end
26
+ end
27
+
28
+ describe API do
29
+ let(:base_uri) { 'https://tind.example.org/' }
30
+ let(:api_key) { 'lorem-ipsum-dolor-sit-amet' }
31
+
32
+ describe :get do
33
+ describe 'with invalid API key' do
34
+ before(:each) do
35
+ allow(BerkeleyLibrary::TIND::Config).to receive(:base_uri).and_return(base_uri)
36
+ end
37
+
38
+ [nil, '', ' '].each do |bad_key|
39
+ describe "api_key = #{bad_key.inspect}" do
40
+ it_behaves_like 'a missing key', bad_key
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ describe 'bad TIND base URI' do
47
+ before(:each) do
48
+ allow(BerkeleyLibrary::TIND::Config).to receive(:api_key).and_return(api_key)
49
+ end
50
+
51
+ [nil, '', ' '].each do |bad_url|
52
+ describe "base_uri = #{bad_url.inspect}" do
53
+ it_behaves_like 'a missing base URL', bad_url
54
+ end
55
+ end
56
+ end
57
+
58
+ describe 'with valid config' do
59
+ before(:each) do
60
+ allow(BerkeleyLibrary::TIND::Config).to receive(:base_uri).and_return(base_uri)
61
+ allow(BerkeleyLibrary::TIND::Config).to receive(:api_key).and_return(api_key)
62
+ end
63
+
64
+ describe :get do
65
+ it "raises #{API::APIException} in the event of an invalid response" do
66
+ aggregate_failures 'responses' do
67
+ [207, 400, 401, 403, 404, 405, 418, 451, 500, 503].each do |code|
68
+ endpoint = "endpoint-#{code}"
69
+ url_str = API.uri_for(endpoint).to_s
70
+ stub_request(:get, url_str).to_return(status: code)
71
+
72
+ expect { API.get(endpoint) }.to raise_error(API::APIException) do |e|
73
+ expect(e.message).to include(code.to_s)
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ it 'logs the response body if an error occurs in handling' do
80
+ endpoint = 'test-endpoint'
81
+ url_str = API.uri_for(endpoint).to_s
82
+ body_text = 'the body'
83
+ stub_request(:get, url_str).to_return(status: 200, body: body_text)
84
+
85
+ logdev = StringIO.new
86
+ logger = BerkeleyLibrary::Logging::Loggers.new_readable_logger(logdev)
87
+ allow(BerkeleyLibrary::Logging).to receive(:logger).and_return(logger)
88
+
89
+ msg = 'the error message'
90
+ expect { API.get(endpoint) { |_| raise(StandardError, msg) } }.to raise_error(StandardError, msg)
91
+ expect(logdev.string).to include(body_text)
92
+ end
93
+
94
+ it 'sends a default user agent' do
95
+ expected_ua = Config::DEFAULT_USER_AGENT
96
+
97
+ endpoint = 'test-endpoint'
98
+ url_str = API.uri_for(endpoint).to_s
99
+ body_text = 'the body'
100
+ stub_request(:get, url_str).with(headers: { 'User-Agent' => expected_ua })
101
+ .to_return(status: 200, body: body_text)
102
+
103
+ result = API.get(endpoint)
104
+ expect(result).to eq(body_text)
105
+ end
106
+
107
+ it 'sends a configured user ageint' do
108
+ expected_ua = 'Help I am trapped in an HTTP request'
109
+ Config.user_agent = expected_ua
110
+
111
+ endpoint = 'test-endpoint'
112
+ url_str = API.uri_for(endpoint).to_s
113
+ body_text = 'the body'
114
+ stub_request(:get, url_str).with(headers: { 'User-Agent' => expected_ua })
115
+ .to_return(status: 200, body: body_text)
116
+
117
+ result = API.get(endpoint)
118
+ expect(result).to eq(body_text)
119
+ end
120
+ end
121
+ end
122
+
123
+ describe :format_request do
124
+ it 'formats a request' do
125
+ url = 'https://example.org/frob'
126
+ params = { foo: 'bar', 'qux' => 'baz' }
127
+ expected_url = 'https://example.org/frob?foo=bar&qux=baz'
128
+ expect(API.format_request(url, params)).to eq("GET #{expected_url}")
129
+ end
130
+
131
+ it 'works without parameters' do
132
+ url = 'https://example.org/frob'
133
+ expect(API.format_request(url)).to eq("GET #{url}")
134
+ end
135
+
136
+ it 'rejects garbage parameters' do
137
+ # noinspection RubyYardParamTypeMatch
138
+ expect { API.format_request('https://example.org', Object.new) }.to raise_error(ArgumentError)
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+ require 'webmock'
3
+
4
+ module BerkeleyLibrary
5
+ module TIND
6
+ module API
7
+ describe Collection do
8
+ let(:base_uri) { 'https://tind.example.org/' }
9
+ let(:api_key) { 'not-a-real-api-key' }
10
+
11
+ before(:each) do
12
+ @base_uri_orig = BerkeleyLibrary::TIND::Config.instance_variable_get(:@base_uri)
13
+ BerkeleyLibrary::TIND::Config.base_uri = base_uri
14
+
15
+ @api_key_orig = BerkeleyLibrary::TIND::Config.instance_variable_get(:@api_key)
16
+ BerkeleyLibrary::TIND::Config.api_key = api_key
17
+ end
18
+
19
+ after(:each) do
20
+ BerkeleyLibrary::TIND::Config.instance_variable_set(:@base_uri, @base_uri_orig)
21
+ BerkeleyLibrary::TIND::Config.instance_variable_set(:@api_key, @api_key_orig)
22
+ end
23
+
24
+ describe :all do
25
+ it 'reads the collections from the API' do
26
+ query_uri = BerkeleyLibrary::Util::URIs.append(base_uri, '/api/v1/collections?depth=100')
27
+ stub_request(:get, query_uri)
28
+ .with(headers: { 'Authorization' => 'Token not-a-real-api-key' })
29
+ .to_return(status: 200, body: File.read('spec/data/collections.json'))
30
+
31
+ all_collections = Collection.all
32
+ expect(all_collections.size).to eq(1)
33
+ expect(root_collection = all_collections.first).not_to be_nil
34
+ expect(root_collection.name).to eq('Digital Collections')
35
+
36
+ top_level_collections = root_collection.children
37
+ expect(top_level_collections.map(&:name)).to eq(['Bampfa', 'Bancroft Library', 'Berkeley Library', 'East Asian Library', 'Humanities & Social Sciences', 'Sciences'])
38
+
39
+ bancroft = top_level_collections.find { |c| c.name == 'Bancroft Library' }
40
+ examiner = bancroft.children.find { |c| c.name == 'SFExaminer' }
41
+ expect(examiner.name_en).to eq('San Francisco Examiner')
42
+ expect(examiner.size).to eq(15_564)
43
+ end
44
+
45
+ it 'returns an empty array in the event of an error' do
46
+ query_uri = BerkeleyLibrary::Util::URIs.append(base_uri, '/api/v1/collections?depth=100')
47
+ stub_request(:get, query_uri).to_return(status: 404)
48
+
49
+ expect(Collection.all).to eq([])
50
+ end
51
+
52
+ it 'raises an error if the API key is not set' do
53
+ BerkeleyLibrary::TIND::API.instance_variable_set(:@api_key, nil)
54
+ end
55
+ end
56
+
57
+ describe :each_collection do
58
+ it 'yields each collection' do
59
+ collections_json = File.read('spec/data/collections.json')
60
+ expected_names = File.readlines('spec/data/collection-names.txt', chomp: true)
61
+
62
+ query_uri = BerkeleyLibrary::Util::URIs.append(base_uri, '/api/v1/collections?depth=100')
63
+ stub_request(:get, query_uri)
64
+ .with(headers: { 'Authorization' => 'Token not-a-real-api-key' })
65
+ .to_return(status: 200, body: collections_json)
66
+
67
+ actual_names = Collection.each_collection.each_with_object([]) { |c, arr| arr << c.name }
68
+ expect(actual_names).to contain_exactly(*expected_names)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end