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.
- checksums.yaml +7 -0
- data/.github/workflows/build.yml +18 -0
- data/.gitignore +388 -0
- data/.idea/inspectionProfiles/Project_Default.xml +20 -0
- data/.idea/misc.xml +4 -0
- data/.idea/modules.xml +8 -0
- data/.idea/tind.iml +138 -0
- data/.idea/vcs.xml +6 -0
- data/.rubocop.yml +334 -0
- data/.ruby-version +1 -0
- data/.simplecov +8 -0
- data/.yardopts +1 -0
- data/CHANGES.md +58 -0
- data/Dockerfile +57 -0
- data/Gemfile +3 -0
- data/Jenkinsfile +18 -0
- data/LICENSE.md +21 -0
- data/README.md +73 -0
- data/Rakefile +20 -0
- data/berkeley_library-tind.gemspec +50 -0
- data/bin/tind-export +14 -0
- data/docker-compose.yml +15 -0
- data/lib/berkeley_library/tind.rb +3 -0
- data/lib/berkeley_library/tind/api.rb +1 -0
- data/lib/berkeley_library/tind/api/api.rb +132 -0
- data/lib/berkeley_library/tind/api/api_exception.rb +131 -0
- data/lib/berkeley_library/tind/api/collection.rb +82 -0
- data/lib/berkeley_library/tind/api/date_range.rb +67 -0
- data/lib/berkeley_library/tind/api/format.rb +32 -0
- data/lib/berkeley_library/tind/api/search.rb +100 -0
- data/lib/berkeley_library/tind/config.rb +103 -0
- data/lib/berkeley_library/tind/export.rb +1 -0
- data/lib/berkeley_library/tind/export/column.rb +54 -0
- data/lib/berkeley_library/tind/export/column_group.rb +144 -0
- data/lib/berkeley_library/tind/export/column_group_list.rb +131 -0
- data/lib/berkeley_library/tind/export/column_width_calculator.rb +76 -0
- data/lib/berkeley_library/tind/export/config.rb +154 -0
- data/lib/berkeley_library/tind/export/csv_exporter.rb +29 -0
- data/lib/berkeley_library/tind/export/export.rb +47 -0
- data/lib/berkeley_library/tind/export/export_command.rb +168 -0
- data/lib/berkeley_library/tind/export/export_exception.rb +8 -0
- data/lib/berkeley_library/tind/export/export_format.rb +67 -0
- data/lib/berkeley_library/tind/export/exporter.rb +105 -0
- data/lib/berkeley_library/tind/export/filter.rb +52 -0
- data/lib/berkeley_library/tind/export/no_results_error.rb +7 -0
- data/lib/berkeley_library/tind/export/ods_exporter.rb +138 -0
- data/lib/berkeley_library/tind/export/row.rb +24 -0
- data/lib/berkeley_library/tind/export/row_metrics.rb +18 -0
- data/lib/berkeley_library/tind/export/table.rb +175 -0
- data/lib/berkeley_library/tind/export/table_metrics.rb +116 -0
- data/lib/berkeley_library/tind/marc.rb +1 -0
- data/lib/berkeley_library/tind/marc/xml_reader.rb +144 -0
- data/lib/berkeley_library/tind/module_info.rb +14 -0
- data/lib/berkeley_library/util/arrays.rb +178 -0
- data/lib/berkeley_library/util/logging.rb +1 -0
- data/lib/berkeley_library/util/ods/spreadsheet.rb +170 -0
- data/lib/berkeley_library/util/ods/xml/content_doc.rb +26 -0
- data/lib/berkeley_library/util/ods/xml/document_node.rb +57 -0
- data/lib/berkeley_library/util/ods/xml/element_node.rb +106 -0
- data/lib/berkeley_library/util/ods/xml/loext/table_protection.rb +26 -0
- data/lib/berkeley_library/util/ods/xml/manifest/file_entry.rb +42 -0
- data/lib/berkeley_library/util/ods/xml/manifest/manifest.rb +73 -0
- data/lib/berkeley_library/util/ods/xml/manifest_doc.rb +26 -0
- data/lib/berkeley_library/util/ods/xml/namespace.rb +46 -0
- data/lib/berkeley_library/util/ods/xml/office/automatic_styles.rb +181 -0
- data/lib/berkeley_library/util/ods/xml/office/body.rb +17 -0
- data/lib/berkeley_library/util/ods/xml/office/document_content.rb +98 -0
- data/lib/berkeley_library/util/ods/xml/office/document_styles.rb +39 -0
- data/lib/berkeley_library/util/ods/xml/office/font_face_decls.rb +30 -0
- data/lib/berkeley_library/util/ods/xml/office/scripts.rb +17 -0
- data/lib/berkeley_library/util/ods/xml/office/spreadsheet.rb +37 -0
- data/lib/berkeley_library/util/ods/xml/office/styles.rb +39 -0
- data/lib/berkeley_library/util/ods/xml/style/cell_style.rb +58 -0
- data/lib/berkeley_library/util/ods/xml/style/column_style.rb +36 -0
- data/lib/berkeley_library/util/ods/xml/style/default_style.rb +31 -0
- data/lib/berkeley_library/util/ods/xml/style/family.rb +85 -0
- data/lib/berkeley_library/util/ods/xml/style/font_face.rb +46 -0
- data/lib/berkeley_library/util/ods/xml/style/paragraph_properties.rb +30 -0
- data/lib/berkeley_library/util/ods/xml/style/row_style.rb +37 -0
- data/lib/berkeley_library/util/ods/xml/style/style.rb +44 -0
- data/lib/berkeley_library/util/ods/xml/style/table_cell_properties.rb +40 -0
- data/lib/berkeley_library/util/ods/xml/style/table_column_properties.rb +30 -0
- data/lib/berkeley_library/util/ods/xml/style/table_properties.rb +25 -0
- data/lib/berkeley_library/util/ods/xml/style/table_row_properties.rb +28 -0
- data/lib/berkeley_library/util/ods/xml/style/table_style.rb +27 -0
- data/lib/berkeley_library/util/ods/xml/style/text_properties.rb +52 -0
- data/lib/berkeley_library/util/ods/xml/styles_doc.rb +26 -0
- data/lib/berkeley_library/util/ods/xml/table/named_expressions.rb +17 -0
- data/lib/berkeley_library/util/ods/xml/table/repeatable.rb +38 -0
- data/lib/berkeley_library/util/ods/xml/table/table.rb +193 -0
- data/lib/berkeley_library/util/ods/xml/table/table_cell.rb +46 -0
- data/lib/berkeley_library/util/ods/xml/table/table_column.rb +43 -0
- data/lib/berkeley_library/util/ods/xml/table/table_row.rb +136 -0
- data/lib/berkeley_library/util/ods/xml/text/p.rb +118 -0
- data/lib/berkeley_library/util/paths.rb +111 -0
- data/lib/berkeley_library/util/stringios.rb +30 -0
- data/lib/berkeley_library/util/strings.rb +42 -0
- data/lib/berkeley_library/util/sys_exits.rb +15 -0
- data/lib/berkeley_library/util/times.rb +22 -0
- data/lib/berkeley_library/util/uris.rb +44 -0
- data/lib/berkeley_library/util/uris/appender.rb +162 -0
- data/lib/berkeley_library/util/uris/requester.rb +62 -0
- data/lib/berkeley_library/util/uris/validator.rb +32 -0
- data/rakelib/bundle.rake +8 -0
- data/rakelib/coverage.rake +11 -0
- data/rakelib/gem.rake +54 -0
- data/rakelib/rubocop.rake +18 -0
- data/rakelib/spec.rake +2 -0
- data/spec/.rubocop.yml +40 -0
- data/spec/berkeley_library/tind/api/api_exception_spec.rb +91 -0
- data/spec/berkeley_library/tind/api/api_spec.rb +143 -0
- data/spec/berkeley_library/tind/api/collection_spec.rb +74 -0
- data/spec/berkeley_library/tind/api/date_range_spec.rb +110 -0
- data/spec/berkeley_library/tind/api/format_spec.rb +54 -0
- data/spec/berkeley_library/tind/api/search_spec.rb +364 -0
- data/spec/berkeley_library/tind/config_spec.rb +86 -0
- data/spec/berkeley_library/tind/export/column_group_spec.rb +29 -0
- data/spec/berkeley_library/tind/export/column_spec.rb +43 -0
- data/spec/berkeley_library/tind/export/config_spec.rb +206 -0
- data/spec/berkeley_library/tind/export/export_command_spec.rb +169 -0
- data/spec/berkeley_library/tind/export/export_format_spec.rb +59 -0
- data/spec/berkeley_library/tind/export/export_matcher.rb +112 -0
- data/spec/berkeley_library/tind/export/export_spec.rb +150 -0
- data/spec/berkeley_library/tind/export/exporter_spec.rb +125 -0
- data/spec/berkeley_library/tind/export/row_spec.rb +118 -0
- data/spec/berkeley_library/tind/export/table_spec.rb +322 -0
- data/spec/berkeley_library/tind/marc/xml_reader_spec.rb +93 -0
- data/spec/berkeley_library/util/arrays_spec.rb +340 -0
- data/spec/berkeley_library/util/ods/spreadsheet_spec.rb +124 -0
- data/spec/berkeley_library/util/ods/xml/content_doc_spec.rb +121 -0
- data/spec/berkeley_library/util/ods/xml/manifest/file_entry_spec.rb +27 -0
- data/spec/berkeley_library/util/ods/xml/manifest/manifest_spec.rb +33 -0
- data/spec/berkeley_library/util/ods/xml/office/document_content_spec.rb +60 -0
- data/spec/berkeley_library/util/ods/xml/style/automatic_styles_spec.rb +37 -0
- data/spec/berkeley_library/util/ods/xml/style/family_spec.rb +57 -0
- data/spec/berkeley_library/util/ods/xml/table/table_row_spec.rb +179 -0
- data/spec/berkeley_library/util/ods/xml/table/table_spec.rb +218 -0
- data/spec/berkeley_library/util/paths_spec.rb +90 -0
- data/spec/berkeley_library/util/stringios_spec.rb +34 -0
- data/spec/berkeley_library/util/strings_spec.rb +27 -0
- data/spec/berkeley_library/util/times_spec.rb +39 -0
- data/spec/berkeley_library/util/uris_spec.rb +118 -0
- data/spec/data/collection-names.txt +438 -0
- data/spec/data/collections.json +4827 -0
- data/spec/data/disjoint-records.xml +187 -0
- data/spec/data/record-184453.xml +58 -0
- data/spec/data/record-184458.xml +63 -0
- data/spec/data/record-187888.xml +78 -0
- data/spec/data/records-api-search-cjk-p1.xml +6381 -0
- data/spec/data/records-api-search-cjk-p2.xml +5 -0
- data/spec/data/records-api-search-p1.xml +4506 -0
- data/spec/data/records-api-search-p2.xml +4509 -0
- data/spec/data/records-api-search-p3.xml +4506 -0
- data/spec/data/records-api-search-p4.xml +4509 -0
- data/spec/data/records-api-search-p5.xml +4506 -0
- data/spec/data/records-api-search-p6.xml +2436 -0
- data/spec/data/records-api-search-p7.xml +5 -0
- data/spec/data/records-api-search.xml +234 -0
- data/spec/data/records-manual-search.xml +547 -0
- data/spec/spec_helper.rb +30 -0
- data/test/profile/table_from_records_profile.rb +46 -0
- metadata +585 -0
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.7
|
data/.simplecov
ADDED
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
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
|
+
[](https://github.com/BerkeleyLibrary/tind/actions/workflows/build.yml)
|
|
4
|
+
[](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!
|
data/docker-compose.yml
ADDED
|
@@ -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 @@
|
|
|
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
|