berkeley_library-tind 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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