sul_orcid_client 0.1.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/.rspec +3 -0
- data/.rubocop/custom.yml +57 -0
- data/.rubocop.yml +16 -0
- data/.standard.yml +1 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +147 -0
- data/LICENSE +15 -0
- data/README.md +79 -0
- data/Rakefile +10 -0
- data/lib/sul_orcid_client/version.rb +5 -0
- data/lib/sul_orcid_client.rb +208 -0
- data/sul_orcid_client.gemspec +47 -0
- metadata +234 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 0b75e419476e1943481450cb767b3f580fb25a16c4ca96cde068cd1176000972
|
|
4
|
+
data.tar.gz: 784a82cb26eaa7f27137bf108a47e4c5d9584588e8c9b98dcabd2902674d7fc1
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 0e47f1d153ae2dd5afcc676daf545f541d3e4c05ca9e89b0233f8ef1ef9bab896bbc6db6cb8a93c3f311de2fcecf2a48cfeb24cd7eb96e68ac2c587af742401c
|
|
7
|
+
data.tar.gz: e7390bd10097109537407e1ce9550a58e85213a066b8bf41280e6bf8c23c23c00d7b1e27953ae17f096f6da3aa0c365cb6a62d9e2cba59bf906983691f29357b
|
data/.rspec
ADDED
data/.rubocop/custom.yml
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
TargetRubyVersion: 3.1
|
|
3
|
+
DisplayCopNames: true
|
|
4
|
+
SuggestExtensions: false
|
|
5
|
+
Exclude:
|
|
6
|
+
- bin/**
|
|
7
|
+
- vendor/bundle/**/*
|
|
8
|
+
|
|
9
|
+
# Per team developer playbook
|
|
10
|
+
RSpec/MultipleMemoizedHelpers:
|
|
11
|
+
Enabled: false
|
|
12
|
+
|
|
13
|
+
RSpec/BeEq: # new in 2.9.0
|
|
14
|
+
Enabled: true
|
|
15
|
+
RSpec/BeNil: # new in 2.9.0
|
|
16
|
+
Enabled: true
|
|
17
|
+
RSpec/ChangeByZero: # new in 2.11
|
|
18
|
+
Enabled: true
|
|
19
|
+
RSpec/ClassCheck: # new in 2.13
|
|
20
|
+
Enabled: true
|
|
21
|
+
RSpec/ExcessiveDocstringSpacing: # new in 2.5
|
|
22
|
+
Enabled: true
|
|
23
|
+
RSpec/IdenticalEqualityAssertion: # new in 2.4
|
|
24
|
+
Enabled: true
|
|
25
|
+
RSpec/NoExpectationExample: # new in 2.13
|
|
26
|
+
Enabled: true
|
|
27
|
+
RSpec/SortMetadata: # new in 2.14
|
|
28
|
+
Enabled: true
|
|
29
|
+
RSpec/SubjectDeclaration: # new in 2.5
|
|
30
|
+
Enabled: true
|
|
31
|
+
RSpec/VerifiedDoubleReference: # new in 2.10.0
|
|
32
|
+
Enabled: true
|
|
33
|
+
Capybara/NegationMatcher: # new in 2.14
|
|
34
|
+
Enabled: true
|
|
35
|
+
Capybara/SpecificActions: # new in 2.14
|
|
36
|
+
Enabled: true
|
|
37
|
+
Capybara/SpecificFinders: # new in 2.13
|
|
38
|
+
Enabled: true
|
|
39
|
+
Capybara/SpecificMatcher: # new in 2.12
|
|
40
|
+
Enabled: true
|
|
41
|
+
FactoryBot/ConsistentParenthesesStyle: # new in 2.14
|
|
42
|
+
Enabled: true
|
|
43
|
+
FactoryBot/SyntaxMethods: # new in 2.7
|
|
44
|
+
Enabled: true
|
|
45
|
+
RSpec/Rails/AvoidSetupHook: # new in 2.4
|
|
46
|
+
Enabled: true
|
|
47
|
+
RSpec/Rails/HaveHttpStatus: # new in 2.12
|
|
48
|
+
Enabled: true
|
|
49
|
+
RSpec/Rails/InferredSpecType: # new in 2.14
|
|
50
|
+
Enabled: true
|
|
51
|
+
|
|
52
|
+
RSpec/MultipleExpectations:
|
|
53
|
+
Enabled: false
|
|
54
|
+
RSpec/ExampleLength:
|
|
55
|
+
Enabled: false
|
|
56
|
+
|
|
57
|
+
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
inherit_mode:
|
|
2
|
+
merge:
|
|
3
|
+
- Exclude
|
|
4
|
+
|
|
5
|
+
require:
|
|
6
|
+
- standard
|
|
7
|
+
- rubocop-performance
|
|
8
|
+
- rubocop-rspec
|
|
9
|
+
|
|
10
|
+
inherit_gem:
|
|
11
|
+
standard: config/base.yml
|
|
12
|
+
standard-performance: config/base.yml
|
|
13
|
+
standard-custom: config/base.yml
|
|
14
|
+
|
|
15
|
+
inherit_from:
|
|
16
|
+
- .rubocop/custom.yml
|
data/.standard.yml
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
parallel: true
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
sul_orcid_client (0.1.0)
|
|
5
|
+
activesupport (>= 4.2, < 8)
|
|
6
|
+
faraday
|
|
7
|
+
faraday-retry
|
|
8
|
+
oauth2
|
|
9
|
+
zeitwerk
|
|
10
|
+
|
|
11
|
+
GEM
|
|
12
|
+
remote: https://rubygems.org/
|
|
13
|
+
specs:
|
|
14
|
+
activesupport (7.0.6)
|
|
15
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
16
|
+
i18n (>= 1.6, < 2)
|
|
17
|
+
minitest (>= 5.1)
|
|
18
|
+
tzinfo (~> 2.0)
|
|
19
|
+
addressable (2.8.4)
|
|
20
|
+
public_suffix (>= 2.0.2, < 6.0)
|
|
21
|
+
ast (2.4.2)
|
|
22
|
+
byebug (11.1.3)
|
|
23
|
+
concurrent-ruby (1.2.2)
|
|
24
|
+
crack (0.4.5)
|
|
25
|
+
rexml
|
|
26
|
+
diff-lcs (1.5.0)
|
|
27
|
+
docile (1.4.0)
|
|
28
|
+
faraday (2.7.10)
|
|
29
|
+
faraday-net_http (>= 2.0, < 3.1)
|
|
30
|
+
ruby2_keywords (>= 0.0.4)
|
|
31
|
+
faraday-net_http (3.0.2)
|
|
32
|
+
faraday-retry (2.2.0)
|
|
33
|
+
faraday (~> 2.0)
|
|
34
|
+
hashdiff (1.0.1)
|
|
35
|
+
hashie (5.0.0)
|
|
36
|
+
i18n (1.14.1)
|
|
37
|
+
concurrent-ruby (~> 1.0)
|
|
38
|
+
json (2.6.3)
|
|
39
|
+
jwt (2.7.1)
|
|
40
|
+
language_server-protocol (3.17.0.3)
|
|
41
|
+
lint_roller (1.1.0)
|
|
42
|
+
minitest (5.18.1)
|
|
43
|
+
multi_xml (0.6.0)
|
|
44
|
+
oauth2 (2.0.9)
|
|
45
|
+
faraday (>= 0.17.3, < 3.0)
|
|
46
|
+
jwt (>= 1.0, < 3.0)
|
|
47
|
+
multi_xml (~> 0.5)
|
|
48
|
+
rack (>= 1.2, < 4)
|
|
49
|
+
snaky_hash (~> 2.0)
|
|
50
|
+
version_gem (~> 1.1)
|
|
51
|
+
parallel (1.23.0)
|
|
52
|
+
parser (3.2.2.3)
|
|
53
|
+
ast (~> 2.4.1)
|
|
54
|
+
racc
|
|
55
|
+
public_suffix (5.0.3)
|
|
56
|
+
racc (1.7.1)
|
|
57
|
+
rack (3.0.8)
|
|
58
|
+
rainbow (3.1.1)
|
|
59
|
+
rake (13.0.6)
|
|
60
|
+
regexp_parser (2.8.1)
|
|
61
|
+
rexml (3.2.5)
|
|
62
|
+
rspec (3.12.0)
|
|
63
|
+
rspec-core (~> 3.12.0)
|
|
64
|
+
rspec-expectations (~> 3.12.0)
|
|
65
|
+
rspec-mocks (~> 3.12.0)
|
|
66
|
+
rspec-core (3.12.2)
|
|
67
|
+
rspec-support (~> 3.12.0)
|
|
68
|
+
rspec-expectations (3.12.3)
|
|
69
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
70
|
+
rspec-support (~> 3.12.0)
|
|
71
|
+
rspec-mocks (3.12.6)
|
|
72
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
73
|
+
rspec-support (~> 3.12.0)
|
|
74
|
+
rspec-support (3.12.1)
|
|
75
|
+
rubocop (1.52.1)
|
|
76
|
+
json (~> 2.3)
|
|
77
|
+
parallel (~> 1.10)
|
|
78
|
+
parser (>= 3.2.2.3)
|
|
79
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
80
|
+
regexp_parser (>= 1.8, < 3.0)
|
|
81
|
+
rexml (>= 3.2.5, < 4.0)
|
|
82
|
+
rubocop-ast (>= 1.28.0, < 2.0)
|
|
83
|
+
ruby-progressbar (~> 1.7)
|
|
84
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
|
85
|
+
rubocop-ast (1.29.0)
|
|
86
|
+
parser (>= 3.2.1.0)
|
|
87
|
+
rubocop-capybara (2.18.0)
|
|
88
|
+
rubocop (~> 1.41)
|
|
89
|
+
rubocop-factory_bot (2.23.1)
|
|
90
|
+
rubocop (~> 1.33)
|
|
91
|
+
rubocop-performance (1.18.0)
|
|
92
|
+
rubocop (>= 1.7.0, < 2.0)
|
|
93
|
+
rubocop-ast (>= 0.4.0)
|
|
94
|
+
rubocop-rspec (2.22.0)
|
|
95
|
+
rubocop (~> 1.33)
|
|
96
|
+
rubocop-capybara (~> 2.17)
|
|
97
|
+
rubocop-factory_bot (~> 2.22)
|
|
98
|
+
ruby-progressbar (1.13.0)
|
|
99
|
+
ruby2_keywords (0.0.5)
|
|
100
|
+
simplecov (0.22.0)
|
|
101
|
+
docile (~> 1.1)
|
|
102
|
+
simplecov-html (~> 0.11)
|
|
103
|
+
simplecov_json_formatter (~> 0.1)
|
|
104
|
+
simplecov-html (0.12.3)
|
|
105
|
+
simplecov_json_formatter (0.1.4)
|
|
106
|
+
snaky_hash (2.0.1)
|
|
107
|
+
hashie
|
|
108
|
+
version_gem (~> 1.1, >= 1.1.1)
|
|
109
|
+
standard (1.29.0)
|
|
110
|
+
language_server-protocol (~> 3.17.0.2)
|
|
111
|
+
lint_roller (~> 1.0)
|
|
112
|
+
rubocop (~> 1.52.0)
|
|
113
|
+
standard-custom (~> 1.0.0)
|
|
114
|
+
standard-performance (~> 1.1.0)
|
|
115
|
+
standard-custom (1.0.1)
|
|
116
|
+
lint_roller (~> 1.0)
|
|
117
|
+
standard-performance (1.1.1)
|
|
118
|
+
lint_roller (~> 1.1)
|
|
119
|
+
rubocop-performance (~> 1.18.0)
|
|
120
|
+
tzinfo (2.0.6)
|
|
121
|
+
concurrent-ruby (~> 1.0)
|
|
122
|
+
unicode-display_width (2.4.2)
|
|
123
|
+
vcr (6.2.0)
|
|
124
|
+
version_gem (1.1.3)
|
|
125
|
+
webmock (3.18.1)
|
|
126
|
+
addressable (>= 2.8.0)
|
|
127
|
+
crack (>= 0.3.2)
|
|
128
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
|
129
|
+
zeitwerk (2.6.8)
|
|
130
|
+
|
|
131
|
+
PLATFORMS
|
|
132
|
+
x86_64-darwin-22
|
|
133
|
+
x86_64-linux
|
|
134
|
+
|
|
135
|
+
DEPENDENCIES
|
|
136
|
+
byebug
|
|
137
|
+
rake (~> 13.0)
|
|
138
|
+
rspec (~> 3.0)
|
|
139
|
+
rubocop-rspec
|
|
140
|
+
simplecov
|
|
141
|
+
standard (~> 1.29.0)
|
|
142
|
+
sul_orcid_client!
|
|
143
|
+
vcr
|
|
144
|
+
webmock
|
|
145
|
+
|
|
146
|
+
BUNDLED WITH
|
|
147
|
+
2.4.14
|
data/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
Copyright (c) 2022 by The Board of Trustees of the Leland Stanford
|
|
3
|
+
Junior University. All rights reserved.
|
|
4
|
+
|
|
5
|
+
Licensed under the Apache License, Version 2.0 (the "License"); you
|
|
6
|
+
may not use this file except in compliance with the License. You
|
|
7
|
+
may obtain a copy of the License at
|
|
8
|
+
|
|
9
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
|
|
11
|
+
Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
14
|
+
implied. See the License for the specific language governing
|
|
15
|
+
permissions and limitations under the License.
|
data/README.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
[](https://badge.fury.io/rb/sul_orcid_client)
|
|
2
|
+
[](https://dl.circleci.com/status-badge/redirect/gh/sul-dlss/orcid_client/tree/main)
|
|
3
|
+
|
|
4
|
+
# sul_orcid_client
|
|
5
|
+
API client for accessing ORCID API.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Install the gem and add to the application's Gemfile by executing:
|
|
10
|
+
|
|
11
|
+
$ bundle add sul_orcid_client
|
|
12
|
+
|
|
13
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
|
14
|
+
|
|
15
|
+
$ gem install sul_orcid_client
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
For one-off requests:
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
require "sul_orcid_client"
|
|
23
|
+
|
|
24
|
+
# NOTE: The settings below live in the consumer, not in the gem.
|
|
25
|
+
client = SulOrcidClient.configure(
|
|
26
|
+
client_id: Settings.orcid.client_id,
|
|
27
|
+
client_secret: Settings.orcid.client_secret,
|
|
28
|
+
base_url: Settings.orcid.base_url
|
|
29
|
+
base_public_url: Settings.orcid.base_public_url
|
|
30
|
+
base_auth_url: Settings.orcid.base_auth_url
|
|
31
|
+
)
|
|
32
|
+
client.fetch_works(orcidid: 'https://sandbox.orcid.org/0000-0002-7262-6251')
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
You can also invoke methods directly on the client class, which is useful in a
|
|
36
|
+
Rails application environment where you might initialize the client in an
|
|
37
|
+
initializer and then invoke client methods in many other contexts where you want
|
|
38
|
+
to be sure configuration has already occurred, e.g.:
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
# config/initializers/sul_orcid_client.rb
|
|
42
|
+
SulOrcidClient.configure(
|
|
43
|
+
client_id: Settings.orcid.client_id,
|
|
44
|
+
client_secret: Settings.orcid.client_secret,
|
|
45
|
+
base_url: Settings.orcid.base_url
|
|
46
|
+
base_public_url: Settings.orcid.base_public_url
|
|
47
|
+
base_auth_url: Settings.orcid.base_auth_url
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# app/services/my_orcid_service.rb
|
|
51
|
+
# ...
|
|
52
|
+
def get_works
|
|
53
|
+
client.fetch_works(orcidid: 'https://sandbox.orcid.org/0000-0002-7262-6251')
|
|
54
|
+
|
|
55
|
+
end
|
|
56
|
+
# ...
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Development
|
|
60
|
+
|
|
61
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
62
|
+
|
|
63
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
64
|
+
|
|
65
|
+
## VCR Cassettes
|
|
66
|
+
|
|
67
|
+
VCR gem is used to record the results of the API calls for the tests. If you need to
|
|
68
|
+
record or re-create existing cassettes, you may need to adjust expectations in the tests
|
|
69
|
+
as the results coming back from the API may be different than when the cassettes were
|
|
70
|
+
recorded.
|
|
71
|
+
|
|
72
|
+
To record new cassettes:
|
|
73
|
+
1. Temporarily adjust the configuration at the top of `spec/sul_orcid_client_spec.rb` so it matches the Orcid sandbox environment.
|
|
74
|
+
2. Add your new spec with a new cassette name (or delete a cassette to re-create it).
|
|
75
|
+
3. Run just that new spec.
|
|
76
|
+
4. You should get a new cassette with the name you specified in the spec.
|
|
77
|
+
5. The cassette should have access tokens and secrets sanitized by the config in `spec_helper.rb`, but you can double check, EXCEPT for user access tokens in the user response. These should be sanitized manaully (e.g. "access_token":"8d13b8bb-XXXX-YYYY-b7d6-87aecd5a8975")
|
|
78
|
+
6. Set your configuration at the top of the spec back to the fake client_id and client_secret values.
|
|
79
|
+
7. Re-run all the specs - they should pass now without making real calls.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/core_ext/module/delegation"
|
|
4
|
+
require "active_support/core_ext/hash/indifferent_access"
|
|
5
|
+
|
|
6
|
+
require "faraday"
|
|
7
|
+
require "faraday/retry"
|
|
8
|
+
require "oauth2"
|
|
9
|
+
require "singleton"
|
|
10
|
+
require "zeitwerk"
|
|
11
|
+
|
|
12
|
+
# Load the gem's internal dependencies: use Zeitwerk instead of needing to manually require classes
|
|
13
|
+
Zeitwerk::Loader.for_gem.setup
|
|
14
|
+
|
|
15
|
+
# Client for interacting with ORCID API
|
|
16
|
+
class SulOrcidClient
|
|
17
|
+
include Singleton
|
|
18
|
+
|
|
19
|
+
class InvalidTokenError < StandardError; end
|
|
20
|
+
|
|
21
|
+
class << self
|
|
22
|
+
# @param client_id [String] the client identifier registered with Orcid
|
|
23
|
+
# @param client_secret [String] the client secret to authenticate with Orcid
|
|
24
|
+
# @param base_url [String] the base URL for the API
|
|
25
|
+
# @param base_public_url [String] the base public URL for the API
|
|
26
|
+
# @param base_auth_url [String] the base authorization URL for the API
|
|
27
|
+
def configure(client_id:, client_secret:, base_url:, base_public_url:, base_auth_url:)
|
|
28
|
+
instance.base_url = base_url
|
|
29
|
+
instance.base_public_url = base_public_url
|
|
30
|
+
instance.client_id = client_id
|
|
31
|
+
instance.client_secret = client_secret
|
|
32
|
+
instance.base_auth_url = base_auth_url
|
|
33
|
+
self
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
delegate :fetch_works, :fetch_work, :fetch_name, :search, :add_work, :delete_work, to: :instance
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
attr_accessor :base_url, :base_public_url, :base_auth_url, :client_id, :client_secret
|
|
40
|
+
|
|
41
|
+
# Fetch the works for a researcher.
|
|
42
|
+
# Model for the response: https://pub.orcid.org/v3.0/#!/Development_Public_API_v3.0/viewWorksv3
|
|
43
|
+
# @param [string] ORCID ID for the researcher
|
|
44
|
+
# @return [Hash]
|
|
45
|
+
def fetch_works(orcidid:)
|
|
46
|
+
get("/v3.0/#{base_orcidid(orcidid)}/works")
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Fetch the details for a work
|
|
50
|
+
def fetch_work(orcidid:, put_code:)
|
|
51
|
+
get("/v3.0/#{base_orcidid(orcidid)}/work/#{put_code}")
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Fetches the name for a user given an orcidid
|
|
55
|
+
def fetch_name(orcidid:)
|
|
56
|
+
match = /[0-9xX]{4}-[0-9xX]{4}-[0-9xX]{4}-[0-9xX]{4}/.match(orcidid)
|
|
57
|
+
raise "invalid orcidid provided" unless match
|
|
58
|
+
|
|
59
|
+
response = public_conn.get("/v3.0/#{match[0]&.upcase}/personal-details")
|
|
60
|
+
case response.status
|
|
61
|
+
when 200
|
|
62
|
+
resp_json = JSON.parse(response.body)
|
|
63
|
+
[resp_json.dig("name", "given-names", "value"),
|
|
64
|
+
resp_json.dig("name", "family-name", "value")]
|
|
65
|
+
else
|
|
66
|
+
raise "ORCID.org API returned #{response.status} (#{response.body}) for: #{orcidid}"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Run a generalized search query against ORCID
|
|
71
|
+
# see https://info.orcid.org/documentation/api-tutorials/api-tutorial-searching-the-orcid-registry
|
|
72
|
+
# @param [query] query to pass to ORCID
|
|
73
|
+
# @param [expanded] set to true or false (defaults to false) to indicate an expanded query results (see ORCID docs)
|
|
74
|
+
def search(query:, expanded: false)
|
|
75
|
+
if expanded
|
|
76
|
+
search_method = "expanded-search"
|
|
77
|
+
response_name = "expanded-result"
|
|
78
|
+
else
|
|
79
|
+
search_method = "search"
|
|
80
|
+
response_name = "result"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# this is the maximum number of rows ORCID allows in their response currently
|
|
84
|
+
max_num_returned = 1000
|
|
85
|
+
total_response = get("/v3.0/#{search_method}/?q=#{query}&rows=#{max_num_returned}")
|
|
86
|
+
num_results = total_response["num-found"]
|
|
87
|
+
|
|
88
|
+
return total_response if num_results <= max_num_returned
|
|
89
|
+
|
|
90
|
+
num_pages = (num_results / max_num_returned.to_f).ceil
|
|
91
|
+
|
|
92
|
+
# we already have page 1 of the results
|
|
93
|
+
(1..num_pages - 1).each do |page_num|
|
|
94
|
+
response = get("/v3.0/#{search_method}/?q=#{query}&start=#{(page_num * max_num_returned) + 1}&rows=#{max_num_returned}")
|
|
95
|
+
total_response[response_name] += response[response_name]
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
total_response
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Add a new work for a researcher.
|
|
102
|
+
# @param [string] ORCID ID for the researcher
|
|
103
|
+
# @param [Hash] work in correct data structure for ORCID work
|
|
104
|
+
# @param [string] access token
|
|
105
|
+
# @return [string] put-code
|
|
106
|
+
def add_work(orcidid:, work:, token:)
|
|
107
|
+
response = conn_with_token(token).post("/v3.0/#{base_orcidid(orcidid)}/work",
|
|
108
|
+
work.to_json,
|
|
109
|
+
"Content-Type" => "application/json")
|
|
110
|
+
|
|
111
|
+
case response.status
|
|
112
|
+
when 201
|
|
113
|
+
response["Location"].match(%r{work/(\d+)})[1]
|
|
114
|
+
when 401
|
|
115
|
+
raise InvalidTokenError,
|
|
116
|
+
"Invalid token for #{orcidid} - ORCID.org API returned #{response.status} (#{response.body})"
|
|
117
|
+
when 409
|
|
118
|
+
match = response.body.match(/put-code (\d+)\./)
|
|
119
|
+
raise "ORCID.org API returned a 409, but could not find put-code" unless match
|
|
120
|
+
|
|
121
|
+
match[1]
|
|
122
|
+
else
|
|
123
|
+
raise "ORCID.org API returned #{response.status} (#{response.body}) for: #{work.to_json}"
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Delete a work
|
|
128
|
+
# @param [string] ORCID ID for the researcher
|
|
129
|
+
# @param [string] put-code
|
|
130
|
+
# @param [string] access token
|
|
131
|
+
# @return [boolean] true if delete succeeded
|
|
132
|
+
def delete_work(orcidid:, put_code:, token:)
|
|
133
|
+
response = conn_with_token(token).delete("/v3.0/#{base_orcidid(orcidid)}/work/#{put_code}")
|
|
134
|
+
|
|
135
|
+
case response.status
|
|
136
|
+
when 204
|
|
137
|
+
true
|
|
138
|
+
when 404
|
|
139
|
+
false
|
|
140
|
+
else
|
|
141
|
+
raise "ORCID.org API returned #{response.status} when deleting #{put_code} for #{orcidid}"
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
private
|
|
146
|
+
|
|
147
|
+
def get(url)
|
|
148
|
+
response = conn.get(url)
|
|
149
|
+
raise "ORCID.org API returned #{response.status}" if response.status != 200
|
|
150
|
+
|
|
151
|
+
JSON.parse(response.body).with_indifferent_access
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def client_token
|
|
155
|
+
client = OAuth2::Client.new(client_id, client_secret, site: base_auth_url)
|
|
156
|
+
token = client.client_credentials.get_token({scope: "/read-public"})
|
|
157
|
+
token.token
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# @return [Faraday::Connection]
|
|
161
|
+
def conn
|
|
162
|
+
@conn ||= conn_with_token(client_token)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# @return [Faraday::Connection]
|
|
166
|
+
def public_conn
|
|
167
|
+
conn = Faraday.new(url: base_public_url) do |faraday|
|
|
168
|
+
faraday.request :retry, max: 5,
|
|
169
|
+
interval: 0.5,
|
|
170
|
+
interval_randomness: 0.5,
|
|
171
|
+
backoff_factor: 2
|
|
172
|
+
end
|
|
173
|
+
conn.options.timeout = 500
|
|
174
|
+
conn.options.open_timeout = 10
|
|
175
|
+
conn.headers = headers
|
|
176
|
+
conn
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# @return [Faraday::Connection]
|
|
180
|
+
def conn_with_token(token)
|
|
181
|
+
conn = Faraday.new(url: base_url) do |faraday|
|
|
182
|
+
faraday.request :retry, max: 3,
|
|
183
|
+
interval: 0.5,
|
|
184
|
+
interval_randomness: 0.5,
|
|
185
|
+
backoff_factor: 2
|
|
186
|
+
end
|
|
187
|
+
conn.options.timeout = 500
|
|
188
|
+
conn.options.open_timeout = 10
|
|
189
|
+
conn.headers = headers
|
|
190
|
+
conn.headers[:authorization] = "Bearer #{token}"
|
|
191
|
+
conn
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def headers
|
|
195
|
+
{
|
|
196
|
+
"Accept" => "application/json",
|
|
197
|
+
"User-Agent" => "stanford-library-sul-pub"
|
|
198
|
+
}
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Extract the ID part from an ORCID ID.
|
|
202
|
+
# For example, 0000-0003-3437-349X from https://sandbox.orcid.org/0000-0003-3437-349X.
|
|
203
|
+
# @param [string] orcidid
|
|
204
|
+
# @return [string] base of ORCID ID
|
|
205
|
+
def base_orcidid(orcidid)
|
|
206
|
+
orcidid[-19, 19]
|
|
207
|
+
end
|
|
208
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
lib = File.expand_path("lib", __dir__)
|
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
|
+
require "sul_orcid_client/version"
|
|
6
|
+
|
|
7
|
+
Gem::Specification.new do |spec|
|
|
8
|
+
spec.name = "sul_orcid_client"
|
|
9
|
+
spec.version = SulOrcidClient::VERSION
|
|
10
|
+
spec.authors = ["Peter Mangiafico", "Justin Littman"]
|
|
11
|
+
spec.email = ["pmangiafico@stanford.edu", "jlittman@stanford.edu"]
|
|
12
|
+
|
|
13
|
+
spec.summary = "Interface for interacting with the ORCID API."
|
|
14
|
+
spec.description = "This provides API interaction with the ORCID API"
|
|
15
|
+
spec.homepage = "https://github.com/sul-dlss/sul_orcid_client"
|
|
16
|
+
spec.required_ruby_version = ">= 3.2.0"
|
|
17
|
+
|
|
18
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
19
|
+
spec.metadata["source_code_uri"] = "https://github.com/sul-dlss/sul_orcid_client"
|
|
20
|
+
spec.metadata["changelog_uri"] = "https://github.com/sul-dlss/sul_orcid_client/releases"
|
|
21
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
|
22
|
+
|
|
23
|
+
# Specify which files should be added to the gem when it is released.
|
|
24
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
25
|
+
spec.files = Dir.chdir(__dir__) do
|
|
26
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
27
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
spec.bindir = "exe"
|
|
31
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
32
|
+
spec.require_paths = ["lib"]
|
|
33
|
+
|
|
34
|
+
spec.add_dependency "activesupport", ">= 4.2", "< 8"
|
|
35
|
+
spec.add_dependency "faraday"
|
|
36
|
+
spec.add_dependency "faraday-retry"
|
|
37
|
+
spec.add_dependency "oauth2"
|
|
38
|
+
spec.add_dependency "zeitwerk"
|
|
39
|
+
|
|
40
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
|
41
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
|
42
|
+
spec.add_development_dependency "rubocop-rspec"
|
|
43
|
+
spec.add_development_dependency "simplecov"
|
|
44
|
+
spec.add_development_dependency "standard", "~> 1.29.0" # Pinning until https://github.com/standardrb/standard-custom/pull/5
|
|
45
|
+
spec.add_development_dependency "vcr"
|
|
46
|
+
spec.add_development_dependency "webmock"
|
|
47
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: sul_orcid_client
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Peter Mangiafico
|
|
8
|
+
- Justin Littman
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: exe
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2023-07-13 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: activesupport
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
requirements:
|
|
18
|
+
- - ">="
|
|
19
|
+
- !ruby/object:Gem::Version
|
|
20
|
+
version: '4.2'
|
|
21
|
+
- - "<"
|
|
22
|
+
- !ruby/object:Gem::Version
|
|
23
|
+
version: '8'
|
|
24
|
+
type: :runtime
|
|
25
|
+
prerelease: false
|
|
26
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
27
|
+
requirements:
|
|
28
|
+
- - ">="
|
|
29
|
+
- !ruby/object:Gem::Version
|
|
30
|
+
version: '4.2'
|
|
31
|
+
- - "<"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '8'
|
|
34
|
+
- !ruby/object:Gem::Dependency
|
|
35
|
+
name: faraday
|
|
36
|
+
requirement: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
type: :runtime
|
|
42
|
+
prerelease: false
|
|
43
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
- !ruby/object:Gem::Dependency
|
|
49
|
+
name: faraday-retry
|
|
50
|
+
requirement: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
type: :runtime
|
|
56
|
+
prerelease: false
|
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
- !ruby/object:Gem::Dependency
|
|
63
|
+
name: oauth2
|
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
69
|
+
type: :runtime
|
|
70
|
+
prerelease: false
|
|
71
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
- !ruby/object:Gem::Dependency
|
|
77
|
+
name: zeitwerk
|
|
78
|
+
requirement: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
83
|
+
type: :runtime
|
|
84
|
+
prerelease: false
|
|
85
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
- !ruby/object:Gem::Dependency
|
|
91
|
+
name: rake
|
|
92
|
+
requirement: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '13.0'
|
|
97
|
+
type: :development
|
|
98
|
+
prerelease: false
|
|
99
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - "~>"
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '13.0'
|
|
104
|
+
- !ruby/object:Gem::Dependency
|
|
105
|
+
name: rspec
|
|
106
|
+
requirement: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - "~>"
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '3.0'
|
|
111
|
+
type: :development
|
|
112
|
+
prerelease: false
|
|
113
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - "~>"
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '3.0'
|
|
118
|
+
- !ruby/object:Gem::Dependency
|
|
119
|
+
name: rubocop-rspec
|
|
120
|
+
requirement: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - ">="
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '0'
|
|
125
|
+
type: :development
|
|
126
|
+
prerelease: false
|
|
127
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - ">="
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: '0'
|
|
132
|
+
- !ruby/object:Gem::Dependency
|
|
133
|
+
name: simplecov
|
|
134
|
+
requirement: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - ">="
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: '0'
|
|
139
|
+
type: :development
|
|
140
|
+
prerelease: false
|
|
141
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
142
|
+
requirements:
|
|
143
|
+
- - ">="
|
|
144
|
+
- !ruby/object:Gem::Version
|
|
145
|
+
version: '0'
|
|
146
|
+
- !ruby/object:Gem::Dependency
|
|
147
|
+
name: standard
|
|
148
|
+
requirement: !ruby/object:Gem::Requirement
|
|
149
|
+
requirements:
|
|
150
|
+
- - "~>"
|
|
151
|
+
- !ruby/object:Gem::Version
|
|
152
|
+
version: 1.29.0
|
|
153
|
+
type: :development
|
|
154
|
+
prerelease: false
|
|
155
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
156
|
+
requirements:
|
|
157
|
+
- - "~>"
|
|
158
|
+
- !ruby/object:Gem::Version
|
|
159
|
+
version: 1.29.0
|
|
160
|
+
- !ruby/object:Gem::Dependency
|
|
161
|
+
name: vcr
|
|
162
|
+
requirement: !ruby/object:Gem::Requirement
|
|
163
|
+
requirements:
|
|
164
|
+
- - ">="
|
|
165
|
+
- !ruby/object:Gem::Version
|
|
166
|
+
version: '0'
|
|
167
|
+
type: :development
|
|
168
|
+
prerelease: false
|
|
169
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
170
|
+
requirements:
|
|
171
|
+
- - ">="
|
|
172
|
+
- !ruby/object:Gem::Version
|
|
173
|
+
version: '0'
|
|
174
|
+
- !ruby/object:Gem::Dependency
|
|
175
|
+
name: webmock
|
|
176
|
+
requirement: !ruby/object:Gem::Requirement
|
|
177
|
+
requirements:
|
|
178
|
+
- - ">="
|
|
179
|
+
- !ruby/object:Gem::Version
|
|
180
|
+
version: '0'
|
|
181
|
+
type: :development
|
|
182
|
+
prerelease: false
|
|
183
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
184
|
+
requirements:
|
|
185
|
+
- - ">="
|
|
186
|
+
- !ruby/object:Gem::Version
|
|
187
|
+
version: '0'
|
|
188
|
+
description: This provides API interaction with the ORCID API
|
|
189
|
+
email:
|
|
190
|
+
- pmangiafico@stanford.edu
|
|
191
|
+
- jlittman@stanford.edu
|
|
192
|
+
executables: []
|
|
193
|
+
extensions: []
|
|
194
|
+
extra_rdoc_files: []
|
|
195
|
+
files:
|
|
196
|
+
- ".rspec"
|
|
197
|
+
- ".rubocop.yml"
|
|
198
|
+
- ".rubocop/custom.yml"
|
|
199
|
+
- ".standard.yml"
|
|
200
|
+
- Gemfile
|
|
201
|
+
- Gemfile.lock
|
|
202
|
+
- LICENSE
|
|
203
|
+
- README.md
|
|
204
|
+
- Rakefile
|
|
205
|
+
- lib/sul_orcid_client.rb
|
|
206
|
+
- lib/sul_orcid_client/version.rb
|
|
207
|
+
- sul_orcid_client.gemspec
|
|
208
|
+
homepage: https://github.com/sul-dlss/sul_orcid_client
|
|
209
|
+
licenses: []
|
|
210
|
+
metadata:
|
|
211
|
+
homepage_uri: https://github.com/sul-dlss/sul_orcid_client
|
|
212
|
+
source_code_uri: https://github.com/sul-dlss/sul_orcid_client
|
|
213
|
+
changelog_uri: https://github.com/sul-dlss/sul_orcid_client/releases
|
|
214
|
+
rubygems_mfa_required: 'true'
|
|
215
|
+
post_install_message:
|
|
216
|
+
rdoc_options: []
|
|
217
|
+
require_paths:
|
|
218
|
+
- lib
|
|
219
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
220
|
+
requirements:
|
|
221
|
+
- - ">="
|
|
222
|
+
- !ruby/object:Gem::Version
|
|
223
|
+
version: 3.2.0
|
|
224
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
225
|
+
requirements:
|
|
226
|
+
- - ">="
|
|
227
|
+
- !ruby/object:Gem::Version
|
|
228
|
+
version: '0'
|
|
229
|
+
requirements: []
|
|
230
|
+
rubygems_version: 3.4.14
|
|
231
|
+
signing_key:
|
|
232
|
+
specification_version: 4
|
|
233
|
+
summary: Interface for interacting with the ORCID API.
|
|
234
|
+
test_files: []
|