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
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.7
data/.simplecov ADDED
@@ -0,0 +1,8 @@
1
+ require 'simplecov-rcov'
2
+
3
+ SimpleCov.start 'rails' do
4
+ add_filter 'module_info.rb'
5
+ coverage_dir 'artifacts'
6
+ formatter SimpleCov::Formatter::RcovFormatter
7
+ minimum_coverage 100
8
+ end
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --no-private --protected lib/**/*.rb -m markdown
data/CHANGES.md ADDED
@@ -0,0 +1,58 @@
1
+ # 0.4.0 (2021-08-19)
2
+
3
+ - Rename to `BerkeleyLibrary::TIND` in prep for move to GitHub
4
+
5
+ # 0.3.3 (2021-08-05)
6
+
7
+ - Send a custom `User-Agent` header to deal with new TIND firewall rules.
8
+
9
+ # 0.3.2 (2021-07-29)
10
+
11
+ - Loosen `spec.required_ruby_version` to support Ruby 3.x
12
+
13
+ # 0.3.1 (2021-05-17)
14
+
15
+ - `API#get` now raises an `APIException` with a simulated '404 Not Found'
16
+ status if `UCBLIT::TIND::Config.base_uri` is not set, or is blank.
17
+
18
+ # 0.3.0 (2021-05-11)
19
+
20
+ - Extracts `MARCExtensions` into a separate gem,
21
+ [`ucblit-marc`](https://git.lib.berkeley.edu/lap/ucblit-marc/edit).
22
+
23
+ # 0.2.4 (2021-05-06)
24
+
25
+ - `API#get` now raises an `APIException` with a simulated '401 Unauthorized' status
26
+ if `UCBLIT::TIND::Config.api_key` is not set.
27
+
28
+ # 0.2.3 (2021-05-04)
29
+
30
+ - `UCBLIT::TIND::Export`:
31
+ - new method `#exporter_for` returns a `UCBLIT::TIND::Exporter` but doesn't
32
+ export immediately.
33
+ - `#export` now raises `NoResultsError` if no records are returned.
34
+
35
+ - `UCBLIT::TIND::Exporter` exposes an `any_results?` method that returns false if
36
+ there are no results to export.
37
+
38
+ # 0.2.2 (2021-05-03)
39
+
40
+ - `UCBLIT::TIND::API::Search` now gracefully returns an empty result when it gets the 500 Internal
41
+ Server Error that TIND thinks is an acceptable empty search result, instead of raising an exception.
42
+
43
+ # 0.2.1 (2021-04-02)
44
+
45
+ - `bin/tind-export` script now supports passing an environment file on the command line with the
46
+ `-e` option; `-e` with no arguments reads from `.env` in the current working directory.
47
+ - Table metrics (font size, line height, max column width etc.) can now be customized
48
+ with environment variables (see [`UCBLIT::TIND::Export::Config`](lib/berkeley_library/tind/export/config.rb))
49
+
50
+ # 0.2.0 (2021-03-31)
51
+
52
+ - Columns in exported OpenOffice / LibreOffice that should not be edited are now marked
53
+ in red but **not** protected.
54
+ - API key configuration has moved from `UCBLIT::TIND::API` to `UCBLIT::TIND::Config`.
55
+
56
+ # 0.1.0 (2021-03-12)
57
+
58
+ - Initial prerelease
data/Dockerfile ADDED
@@ -0,0 +1,57 @@
1
+ # =============================================================================
2
+ # Target: base
3
+
4
+ FROM ruby:2.7-alpine AS base
5
+
6
+ RUN apk --no-cache --update upgrade && \
7
+ apk --no-cache add \
8
+ bash \
9
+ ca-certificates \
10
+ git \
11
+ libc6-compat \
12
+ openssl \
13
+ tzdata \
14
+ xz-libs \
15
+ && rm -rf /var/cache/apk/*
16
+
17
+ WORKDIR /opt/app
18
+
19
+ # =============================================================================
20
+ # Target: development
21
+ #
22
+
23
+ FROM base AS development
24
+
25
+ # Install system packages needed to build gems with C extensions.
26
+ RUN apk --update --no-cache add \
27
+ build-base \
28
+ coreutils \
29
+ git \
30
+ && rm -rf /var/cache/apk/*
31
+
32
+ # The base image ships bundler 1.17.2, but we want something more recent
33
+ RUN gem install bundler -v 2.1.4
34
+
35
+ # Copy codebase to WORKDIR. Unlike application projects, for a gem project
36
+ # we need to do this before running `bundle install`, in order for the gem
37
+ # we're building to be able to "install" itself.
38
+ COPY . .
39
+
40
+ # Install gems.
41
+ RUN bundle install --path=/usr/local/bundle
42
+
43
+ # =============================================================================
44
+ # Target: production
45
+
46
+ FROM base AS production
47
+
48
+ # Copy the built codebase from the dev stage
49
+ COPY --from=development /opt/app /opt/app
50
+ COPY --from=development /usr/local/bundle /usr/local/bundle
51
+
52
+ # Sanity-check that everything was installed correctly and still runs in the
53
+ # slimmed-down production image.
54
+ RUN bundle config set deployment 'true'
55
+ RUN bundle install --local --path=/usr/local/bundle
56
+
57
+ CMD ['bundle', 'exec', 'rake']
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Jenkinsfile ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env groovy
2
+
3
+ dockerComposePipeline(
4
+ commands: [
5
+ 'bundle exec rake coverage',
6
+ 'bundle exec rake rubocop',
7
+ 'bundle exec rake bundle:audit',
8
+ 'bundle exec rake gem'
9
+ ],
10
+ artifacts: [
11
+ junit: 'artifacts/rspec/**/*.xml',
12
+ html : [
13
+ 'Code Coverage': 'artifacts/rcov',
14
+ 'RuboCop' : 'artifacts/rubocop'
15
+ ],
16
+ raw : ['artifacts/**/*.gem']
17
+ ]
18
+ )
data/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ # The MIT License (MIT)
2
+
3
+ Copyright © 2021 The Regents of the University of California
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a
6
+ copy of this software and associated documentation files (the “Software”),
7
+ to deal in the Software without restriction, including without limitation
8
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
9
+ and/or sell copies of the Software, and to permit persons to whom the
10
+ Software is furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
+ DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # BerkeleyLibrary::TIND
2
+
3
+ [![Build Status](https://github.com/BerkeleyLibrary/tind/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/BerkeleyLibrary/tind/actions/workflows/build.yml)
4
+ [![Gem Version](https://img.shields.io/gem/v/berkeley_library-tind.svg)](https://github.com/BerkeleyLibrary/tind/releases)
5
+
6
+ Utility gem for working with the TIND DA digital archive.
7
+
8
+ ## Configuration
9
+
10
+ To access the TIND API, you will need to set:
11
+
12
+ 1. the base URL for your TIND installation (e.g. `https://digicoll.lib.berkeley.edu/`)
13
+ 2. the TIND API key (see the "[API Token Generator](https://docs.tind.io/article/2xaplzx9cn-api-token-generator)"
14
+ article on [`docs.tind.io`](https://docs.tind.io). TIND's code and
15
+ docs are inconsistent in their use of "token" and "key". The UI calls
16
+ it a "key", so that's the term we use here.)
17
+
18
+ These can be set directly, via accessors in the `BerkeleyLibrary::TIND::Config` module;
19
+ if they are not set, a value will be read from the environment, and if no
20
+ value is present in the environment and Rails is loaded, from the Rails
21
+ application configuration (`Rails.application.config`).
22
+
23
+ | Value | Config | ENV | Rails |
24
+ | --- | --- | --- | --- |
25
+ | TIND base URI | `:base_uri` | `LIT_TIND_BASE_URL` | `:tind_base_uri` |
26
+ | API key | `:api_key` | `LIT_TIND_API_KEY` | `:tind_api_key` |
27
+
28
+ **Note:** The TIND base URI can be set either as a string or as a `URI`
29
+ object, but will always be returned as a `URI` object, and an invalid
30
+ string setting will raise `URI::InvalidURIError`.
31
+
32
+ ## Command-line tool: `tind-export`
33
+
34
+ The `tind-export` command allows you to list TIND collections, or to
35
+ export a TIND collection from the command line. (If the gem is installed,
36
+ `tind-export` should be in your `$PATH`. If you've cloned the gem source,
37
+ you can invoke it with `bin/tind-export` from the project root directory.)
38
+
39
+ Examples:
40
+
41
+ 1. list collections
42
+
43
+ ```sh
44
+ tind-export --list-collections
45
+ ```
46
+
47
+ 2. export a collection as an OpenOffice/LibreOffice spreadsheet
48
+
49
+ ```sh
50
+ tind-export -o lincoln-papers.ods 'Abraham Lincoln Papers'
51
+ ```
52
+
53
+ 3. export a collection as an OpenOffice/LibreOffice spreadsheet in exploded XML format,
54
+ where `lincoln-papers` is a directory
55
+
56
+ ```sh
57
+ tind-export -f ODS -o lincoln-papers 'Abraham Lincoln Papers'
58
+ ```
59
+
60
+ (Note that OpenOffice itself and many other tools get confused by the extra text
61
+ nodes in the pretty-printed files and won't read them properly; this feature
62
+ is mostly for debugging.)
63
+
64
+ 4. export a collection as CSV, to standard output
65
+
66
+ ```sh
67
+ tind-export -f CSV 'Abraham Lincoln Papers'
68
+ ```
69
+
70
+ For the full list of options, type `tind-export --help`. Note that you can set
71
+ the TIND base URL and API key either via the environment, as above, or as options
72
+ passed to the `tind-export` command. If both an explicit option and an environment
73
+ variable are set for either, the explicit option takes precedence.
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('Gemfile', __dir__)
2
+ require 'bundler/setup' # Set up gems listed in the Gemfile.
3
+
4
+ # ------------------------------------------------------------
5
+ # Application code
6
+
7
+ File.expand_path('lib', __dir__).tap do |lib|
8
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
9
+ end
10
+
11
+ # ------------------------------------------------------------
12
+ # CI
13
+
14
+ ENV['RAILS_ENV'] = 'test' if ENV['CI']
15
+
16
+ # ------------------------------------------------------------
17
+ # Custom tasks
18
+
19
+ desc 'Run tests, check test coverage, check code style, check for vulnerabilities, build gem'
20
+ task default: %i[coverage rubocop bundle:audit gem]
@@ -0,0 +1,50 @@
1
+ File.expand_path('lib', __dir__).tap do |lib|
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ end
4
+
5
+ ruby_version = '>= 2.7'
6
+
7
+ require 'berkeley_library/tind/module_info'
8
+
9
+ Gem::Specification.new do |spec|
10
+ spec.name = BerkeleyLibrary::TIND::ModuleInfo::NAME
11
+ spec.author = BerkeleyLibrary::TIND::ModuleInfo::AUTHOR
12
+ spec.email = BerkeleyLibrary::TIND::ModuleInfo::AUTHOR_EMAIL
13
+ spec.summary = BerkeleyLibrary::TIND::ModuleInfo::SUMMARY
14
+ spec.description = BerkeleyLibrary::TIND::ModuleInfo::DESCRIPTION
15
+ spec.license = BerkeleyLibrary::TIND::ModuleInfo::LICENSE
16
+ spec.version = BerkeleyLibrary::TIND::ModuleInfo::VERSION
17
+ spec.homepage = BerkeleyLibrary::TIND::ModuleInfo::HOMEPAGE
18
+
19
+ spec.files = `git ls-files -z`.split("\x0")
20
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
+ spec.require_paths = ['lib']
22
+ spec.executables << 'tind-export'
23
+
24
+ spec.required_ruby_version = ruby_version
25
+
26
+ spec.add_dependency 'berkeley_library-logging', '~> 0.2'
27
+ spec.add_dependency 'berkeley_library-marc', '~> 0.2'
28
+ spec.add_dependency 'ice_nine', '~> 0.11'
29
+ spec.add_dependency 'marc', '~> 1.0'
30
+ spec.add_dependency 'rchardet', '~> 1.8'
31
+ spec.add_dependency 'rest-client', '~> 2.1'
32
+ spec.add_dependency 'rubyzip', '~> 2.3'
33
+ spec.add_dependency 'typesafe_enum', '~> 0.3'
34
+
35
+ spec.add_development_dependency 'bundle-audit', '~> 0.1'
36
+ spec.add_development_dependency 'ci_reporter_rspec', '~> 1.0'
37
+ spec.add_development_dependency 'colorize', '~> 0.8'
38
+ spec.add_development_dependency 'dotenv', '~> 2.7'
39
+ spec.add_development_dependency 'listen', '>= 3.0.5', '< 3.2'
40
+ spec.add_development_dependency 'rake', '~> 13.0'
41
+ spec.add_development_dependency 'roo', '~> 2.8'
42
+ spec.add_development_dependency 'rspec', '~> 3.10'
43
+ spec.add_development_dependency 'rubocop', '= 1.11'
44
+ spec.add_development_dependency 'rubocop-rake', '~> 0.5'
45
+ spec.add_development_dependency 'rubocop-rspec', '~> 2.2'
46
+ spec.add_development_dependency 'ruby-prof', '~> 0.17.0'
47
+ spec.add_development_dependency 'simplecov', '~> 0.21'
48
+ spec.add_development_dependency 'simplecov-rcov', '~> 0.2'
49
+ spec.add_development_dependency 'webmock', '~> 3.12'
50
+ end
data/bin/tind-export ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ begin
4
+ # This should work if (1) the gem is installed, or
5
+ # (2) we're in development and used `bundle exec`
6
+ require 'berkeley_library/tind'
7
+ rescue LoadError
8
+ # If we're in development, `require 'bundler/setup'`
9
+ # is roughly equivalent to `bundle exec`
10
+ require 'bundler/setup'
11
+ require 'berkeley_library/tind'
12
+ end
13
+
14
+ BerkeleyLibrary::TIND::Export::ExportCommand.new(*ARGV).execute!
@@ -0,0 +1,15 @@
1
+ services:
2
+ gem:
3
+ build:
4
+ context: .
5
+ target: development
6
+ ports:
7
+ - target: 3000
8
+ published: 3000
9
+ restart: always
10
+ volumes:
11
+ # Note that this mounts the *entire* repo directory (including
12
+ # files ignored in .dockerignore when building the image)
13
+ - ./:/opt/app
14
+
15
+ version: "3.7"
@@ -0,0 +1,3 @@
1
+ require 'marc_extensions'
2
+
3
+ Dir.glob(File.expand_path('tind/*.rb', __dir__)).sort.each(&method(:require))
@@ -0,0 +1 @@
1
+ Dir.glob(File.expand_path('api/*.rb', __dir__)).sort.each(&method(:require))
@@ -0,0 +1,132 @@
1
+ require 'stringio'
2
+ require 'open-uri'
3
+
4
+ require 'berkeley_library/util/uris'
5
+ require 'berkeley_library/util/logging'
6
+ require 'berkeley_library/tind/config'
7
+ require 'berkeley_library/tind/api/api_exception'
8
+
9
+ module BerkeleyLibrary
10
+ module TIND
11
+ module API
12
+ class << self
13
+ include BerkeleyLibrary::Util
14
+ include BerkeleyLibrary::Logging
15
+
16
+ # Gets the TIND API key.
17
+ # @return [String, nil] the TIND API key, or `nil` if not set.
18
+ def api_key
19
+ BerkeleyLibrary::TIND::Config.api_key
20
+ end
21
+
22
+ # Gets the value to send in the User-Agent header
23
+ # @return [String] the user agent
24
+ def user_agent
25
+ BerkeleyLibrary::TIND::Config.user_agent
26
+ end
27
+
28
+ # Gets the API base URI.
29
+ # @return [URI] the API base URI
30
+ def api_base_uri
31
+ return if Config.blank?((base_uri = Config.base_uri))
32
+
33
+ URIs.append(base_uri, '/api/v1')
34
+ end
35
+
36
+ # Gets the URI for the specified API endpoint.
37
+ # @param endpoint [Symbol, String] the endpoint (e.g. `:search` or `:collection`)
38
+ # @return [URI] the URI for the specified endpoint
39
+ # @raise [API::BaseURINotSet] if the TIND base URI is not set
40
+ def uri_for(endpoint)
41
+ return if Config.blank?(api_base_uri)
42
+
43
+ URIs.append(api_base_uri, endpoint)
44
+ end
45
+
46
+ # Makes a GET request.
47
+ #
48
+ # @overload get(endpoint, **params)
49
+ # Makes a GET request to the specified endpoint with the specified parameters,
50
+ # and returns the response body as a string. Example:
51
+ #
52
+ # ```ruby
53
+ # marc_xml = API.get(:search, c: 'The Bancroft Library')
54
+ # XMLReader.new(marc_xml).each { |record| ... }
55
+ # ```
56
+ #
57
+ # @param endpoint [Symbol] the API endpoint, e.g. `:search` or `:collection`
58
+ # @param **params [Hash] the query parameters
59
+ # @return [String] the response body
60
+ # @overload get(endpoint, **params, &block)
61
+ # Makes a GET request to the specified endpoint with the specified parameters,
62
+ # and yields an `IO` that streams the response body. Example:
63
+ #
64
+ # ```ruby
65
+ # API.get(:search, c: 'The Bancroft Library') do |body|
66
+ # XMLReader.new(body).each { |record| ... }
67
+ # end
68
+ # ```
69
+ #
70
+ # @param endpoint [Symbol, String] the API endpoint, e.g. `:search` or `:collections`
71
+ # @param **params [Hash] the query parameters
72
+ # @yieldparam body [IO] the response body, as an IO stream
73
+ def get(endpoint, **params, &block)
74
+ endpoint_uri = uri_for(endpoint)
75
+ raise BaseURINotSet.new(endpoint, params) if Config.blank?(endpoint_uri)
76
+
77
+ logger.debug(format_request(endpoint_uri, params))
78
+
79
+ body = do_get(endpoint_uri, params)
80
+ return body unless block_given?
81
+
82
+ stream_response_body(body, &block)
83
+ end
84
+
85
+ # Returns a formatted string version of the request, suitable for
86
+ # logging or error messages.
87
+ #
88
+ # @param uri [URI, String] the URI
89
+ # @param params [Hash, nil] the query parameters
90
+ # @param method [String] the request method
91
+ def format_request(uri, params = nil, method = 'GET')
92
+ query_string = format_query(params)
93
+ uri = URIs.append(uri, '?', query_string) if query_string
94
+
95
+ "#{method} #{uri}"
96
+ end
97
+
98
+ def format_query(params)
99
+ return unless params
100
+ return URI.encode_www_form(params.to_hash) if params.respond_to?(:to_hash)
101
+
102
+ raise ArgumentError, "Argument #{params.inspect} does not appear to be a set of query parameters"
103
+ end
104
+
105
+ private
106
+
107
+ def do_get(endpoint_uri, params)
108
+ raise APIKeyNotSet.new(endpoint_uri, params) if Config.blank?(api_key)
109
+
110
+ begin
111
+ URIs.get(endpoint_uri, params, {
112
+ 'Authorization' => "Token #{api_key}",
113
+ 'User-Agent' => user_agent
114
+ })
115
+ rescue RestClient::RequestFailed => e
116
+ raise APIException.wrap(e, url: endpoint_uri, params: params)
117
+ end
118
+ end
119
+
120
+ # TODO: make real body streaming work
121
+ def stream_response_body(body)
122
+ yield StringIO.new(body)
123
+ rescue StandardError => e
124
+ # We don't log the full stack trace here as we assume the block will do that
125
+ logger.warn("Error yielding response body: #{e}: body was: #{body}")
126
+ raise
127
+ end
128
+ end
129
+
130
+ end
131
+ end
132
+ end