orcid 0.1.1 → 0.8.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 +4 -4
- data/.hound.yml +2 -1
- data/CONTRIBUTING.md +213 -0
- data/README.md +2 -5
- data/app/controllers/orcid/profile_requests_controller.rb +11 -1
- data/app/models/orcid/profile_connection.rb +12 -2
- data/app/models/orcid/profile_status.rb +67 -0
- data/app/services/orcid/remote/profile_creation_service.rb +15 -0
- data/app/services/orcid/remote/profile_query_service/response_parser.rb +53 -0
- data/app/services/orcid/remote/profile_query_service.rb +4 -23
- data/app/views/orcid/profile_connections/_authenticated_connection.html.erb +5 -0
- data/app/views/orcid/profile_connections/_options_to_connect_orcid_profile.html.erb +20 -0
- data/app/views/orcid/profile_connections/_orcid_connector.html.erb +15 -18
- data/app/views/orcid/profile_connections/_pending_connection.html.erb +11 -0
- data/app/views/orcid/profile_connections/_profile_request_pending.html.erb +5 -0
- data/config/locales/orcid.en.yml +1 -1
- data/lib/orcid/configuration/provider.rb +8 -0
- data/lib/orcid/version.rb +1 -1
- data/lib/orcid.rb +4 -0
- data/spec/controllers/orcid/profile_requests_controller_spec.rb +21 -9
- data/spec/features/public_api_query_spec.rb +6 -0
- data/spec/fixtures/orcid-remote-profile_query_service-response_parser/multiple-responses-without-valid-response.json +258 -0
- data/spec/fixtures/orcid-remote-profile_query_service-response_parser/single-response-with-orcid-valid-profile.json +71 -0
- data/spec/lib/orcid/configuration/provider_spec.rb +3 -0
- data/spec/lib/orcid_spec.rb +8 -0
- data/spec/models/orcid/profile_connection_spec.rb +39 -17
- data/spec/models/orcid/profile_status_spec.rb +73 -0
- data/spec/routing/orcid/profile_request_routing_spec.rb +15 -0
- data/spec/services/orcid/remote/profile_creation_service_spec.rb +23 -3
- data/spec/services/orcid/remote/profile_query_service/response_parser_spec.rb +43 -0
- data/spec/services/orcid/remote/profile_query_service_spec.rb +7 -89
- data/spec/views/orcid/profile_connections/_authenticated_connection.html.erb_spec.rb +20 -0
- data/spec/views/orcid/profile_connections/_options_to_connect_orcid_profile.html.erb_spec.rb +26 -0
- data/spec/views/orcid/profile_connections/_orcid_connector.html.erb_spec.rb +65 -0
- data/spec/views/orcid/profile_connections/_pending_connection.html.erb_spec.rb +22 -0
- data/spec/views/orcid/profile_connections/_profile_request_pending.html.erb_spec.rb +24 -0
- metadata +29 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cdbf8eaf74801c8b3c9d2325d362d14e7f7e3c8b
|
4
|
+
data.tar.gz: dc2b890ba3958416489f364983885bf6a08a0f85
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 18825277ac23c713d06fad1ead3df6565bf1273fcb77cb53e01839cd7a5e443c7fad3f17cc4eefa1c70a73d4a69d7a03e5c1b5999d187a421a40c2ed0f8fd7ed
|
7
|
+
data.tar.gz: 3737282f7a406c4ca10cae3f2d51b315d512278c834680c20c57821f140864d2e0188a888a6a89eb0ff1a6f3f5df9ec0cf93cb9354d493121f19683062a6aa3e
|
data/.hound.yml
CHANGED
@@ -187,6 +187,7 @@ Documentation:
|
|
187
187
|
|
188
188
|
DotPosition:
|
189
189
|
Description: 'Checks the position of the dot in multi-line method calls.'
|
190
|
+
EnforcedStyle: trailing
|
190
191
|
Enabled: true
|
191
192
|
|
192
193
|
DoubleNegation:
|
@@ -235,7 +236,7 @@ EvenOdd:
|
|
235
236
|
|
236
237
|
FileName:
|
237
238
|
Description: 'Use snake_case for source file names.'
|
238
|
-
Enabled:
|
239
|
+
Enabled: false
|
239
240
|
|
240
241
|
FlipFlop:
|
241
242
|
Description: 'Checks for flip flops'
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,213 @@
|
|
1
|
+
We want your help to make our project great.
|
2
|
+
There are a few guidelines that we need contributors to follow so that we can have a chance of keeping on top of things.
|
3
|
+
|
4
|
+
* [The Hydra Way](#the-hydra-way)
|
5
|
+
* [Reporting Issues](#reporting-issues)
|
6
|
+
* [What is a well written issue?](#what-is-a-well-written-issue)
|
7
|
+
* [Making Changes](#making-changes)
|
8
|
+
* [Where to Engage for Help](#where-to-engage-for-help)
|
9
|
+
* [Submitting Changes](#submitting-changes)
|
10
|
+
* [Contributor License Agreement](#contributor-license-agreement)
|
11
|
+
* [Coding Guidelines](#coding-guidelines)
|
12
|
+
* [Stating Your Intent](#stating-your-intent)
|
13
|
+
* [Writing Your Specs](#writing-your-specs)
|
14
|
+
* [Writing Your Code](#writing-your-code)
|
15
|
+
* [Ruby File Structure](#ruby-file-structure)
|
16
|
+
* [Well Written Commit Messages](#well-written-commit-messages)
|
17
|
+
* [Hooks into other Subsystems](#hooks-into-other-subsystems)
|
18
|
+
* [Reviewing Changes](#reviewing-changes)
|
19
|
+
* [Responsibilities of a Reviewer](#responsibilities-of-a-reviewer)
|
20
|
+
* [Responsibilities of the Submitter](#responsibilities-of-the-submitter)
|
21
|
+
* [Merging Changes](#merging-changes)
|
22
|
+
|
23
|
+
# The Hydra Way
|
24
|
+
|
25
|
+
We strive to…
|
26
|
+
|
27
|
+
* Provide a [harassment-free community experience](https://wiki.duraspace.org/display/hydra/Anti-Harassment+Policy)
|
28
|
+
* Adhere to our [Hierarchy of Promises](https://wiki.duraspace.org/display/hydra/Hydra+Stack+-+The+Hierarchy+of+Promises)
|
29
|
+
* Operate under [Lazy Consensus](http://rave.apache.org/docs/governance/lazyConsensus.html)
|
30
|
+
* Encourage community participation as guided by [our community principles](https://wiki.duraspace.org/display/hydra/Hydra+Community+Principles)
|
31
|
+
|
32
|
+
# Reporting Issues
|
33
|
+
|
34
|
+
Submit a [well written issue](#what-is-a-well-written-issue) to [Github's issue tracker](./issues).
|
35
|
+
This will require a [GitHub account](https://github.com/signup/free) *(its free)*.
|
36
|
+
|
37
|
+
## What is a well written issue?
|
38
|
+
|
39
|
+
* Provide a descriptive summary
|
40
|
+
* Reference the version in which you encountered the problem
|
41
|
+
* Explain the expected behavior
|
42
|
+
* Explain the actual behavior
|
43
|
+
* Provide steps to reproduce the actual behavior
|
44
|
+
|
45
|
+
# Making Changes
|
46
|
+
|
47
|
+
Hydra is an open source project, released under the [APACHE 2 license](LICENSE).
|
48
|
+
You are free to clone or [fork the repository](https://help.github.com/articles/fork-a-repo) and modify the code as you see fit.
|
49
|
+
|
50
|
+
## Where to Engage for Help
|
51
|
+
|
52
|
+
This gem is part of ProjectHydra, so you can [connect via the usual ProjectHydra channels](https://wiki.duraspace.org/pages/viewpage.action?pageId=43910187).
|
53
|
+
|
54
|
+
# Submitting Changes
|
55
|
+
|
56
|
+
## Contributor License Agreement
|
57
|
+
|
58
|
+
**Note: You can submit a pull request before you've signed the Contributor License Agreement, but we won't merge your changes until we have your CLA on file.**
|
59
|
+
|
60
|
+
Before any [ProjectHydra project](https://github.com/projecthydra) can accept your contributions we must have a [Contributor License Agreement on file](https://wiki.duraspace.org/display/hydra/Hydra+Project+Intellectual+Property+Licensing+and+Ownership).
|
61
|
+
|
62
|
+
All code contributors must have an Individual Contributor License Agreement (iCLA) on file with the Hydra Project Steering Group.
|
63
|
+
If the contributor works for an institution, the institution must have a Corporate Contributor License Agreement (cCLA) on file.
|
64
|
+
|
65
|
+
[More on the Contributor License Agreements](https://wiki.duraspace.org/display/hydra/Hydra+Project+Intellectual+Property+Licensing+and+Ownership)
|
66
|
+
|
67
|
+
## Coding Guidelines
|
68
|
+
|
69
|
+
This project is using [HoundCI](https://houndci.com) to help support our agreed upon style guide.
|
70
|
+
The style guide is a work in progress, and is declared in the project's `./hound.yml` file.
|
71
|
+
|
72
|
+
Hound is a Github integration tool that essentially runs [rubocop](http://rubygems.org/gems/rubocop).
|
73
|
+
|
74
|
+
> Automatic Ruby code style checking tool. Aims to enforce the community-driven Ruby Style Guide.
|
75
|
+
|
76
|
+
If you want to run `rubocop` with our style guide, first `gem install rubocop` then inside the project:
|
77
|
+
|
78
|
+
```bash
|
79
|
+
$ rubocop ./path/to/file ./or/path/to/directory -c ./.hound.yml
|
80
|
+
```
|
81
|
+
**Can I break these guidelines?** Yes.
|
82
|
+
But you may need to convince the person merging your changes.
|
83
|
+
|
84
|
+
### Stating Your Intent
|
85
|
+
|
86
|
+
Think of your written test as a statement of intent.
|
87
|
+
The statement of intent can then be used when asking for help or clarity;
|
88
|
+
Either from another developer or a stakeholder.
|
89
|
+
Someone helping you can then see both what you are trying to do and how you are doing it;
|
90
|
+
And that helper may know of a "better" way to do it.
|
91
|
+
|
92
|
+
### Writing Your Specs
|
93
|
+
|
94
|
+
Your code changes should include supporting tests.
|
95
|
+
|
96
|
+
Before you begin writing code, think about the test that will verify the code you plan to write.
|
97
|
+
A [well written story with Gherkin syntax](http://pivotallabs.com/well-formed-stories/) can help formulate the pre-conditions (Given), invocation (When), and post-conditions (Then).
|
98
|
+
|
99
|
+
*This is the first step of Test Driven Development, and something that we encourage.*
|
100
|
+
|
101
|
+
Now write that test; It should be your guidepost for any changes you plan on making.
|
102
|
+
The test you just wrote should be executable code;
|
103
|
+
If you are uncomfortable with where to put the executable code, a well formed Gherkin-syntax story is a suitable proxy.
|
104
|
+
|
105
|
+
### Writing Your Code
|
106
|
+
|
107
|
+
We are going to do our best to follow [Sandi Metz' Rules for Developers](http://robots.thoughtbot.com/post/50655960596/sandi-metz-rules-for-developers)
|
108
|
+
|
109
|
+
> Here are the rules:
|
110
|
+
>
|
111
|
+
> * Classes can be no longer than one hundred lines of code.
|
112
|
+
> * Methods can be no longer than five lines of code.
|
113
|
+
> * Pass no more than four parameters into a method. Hash options are parameters.
|
114
|
+
> * Controllers can instantiate only one object. Therefore, views can only know about one instance variable and views should only send messages to that object (`@object.collaborator.value` is not allowed).
|
115
|
+
|
116
|
+
## Well Written Commit Messages
|
117
|
+
|
118
|
+
**TL;DR**
|
119
|
+
|
120
|
+
* First line is 50 characters or less
|
121
|
+
* The message body explains what the code changes are about
|
122
|
+
* [Reference any Github issues](https://help.github.com/articles/writing-on-github#references) on new lines.
|
123
|
+
* It is helpful if you [close a related issue via the commit message](https://help.github.com/articles/closing-issues-via-commit-messages)
|
124
|
+
|
125
|
+
### Terse Example
|
126
|
+
|
127
|
+
```
|
128
|
+
Removing Document title validation
|
129
|
+
|
130
|
+
We thought we wanted title validation but that was too complicated.
|
131
|
+
|
132
|
+
Closes #12
|
133
|
+
```
|
134
|
+
|
135
|
+
### Verbose Example
|
136
|
+
|
137
|
+
```
|
138
|
+
Present tense short summary (50 characters or less)
|
139
|
+
|
140
|
+
More detailed description, if necessary. It should be wrapped to 72
|
141
|
+
characters. Try to be as descriptive as you can, even if you think that
|
142
|
+
the commit content is obvious, it may not be obvious to others. You
|
143
|
+
should add such description also if it's already present in bug tracker,
|
144
|
+
it should not be necessary to visit a webpage to check the history.
|
145
|
+
|
146
|
+
Description can have multiple paragraphs and you can use code examples
|
147
|
+
inside, just indent it with 4 spaces:
|
148
|
+
|
149
|
+
class PostsController
|
150
|
+
def index
|
151
|
+
respond_with Post.limit(10)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
You can also add bullet points:
|
156
|
+
|
157
|
+
- you can use dashes or asterisks
|
158
|
+
|
159
|
+
- also, try to indent next line of a point for readability, if it's too
|
160
|
+
long to fit in 72 characters
|
161
|
+
```
|
162
|
+
|
163
|
+
> When appropriate, please squash your commits into logical units of work.
|
164
|
+
> This simplifies future cherry picks, and also keeps the git log clean.
|
165
|
+
|
166
|
+
### Hooks into other Subsystems
|
167
|
+
|
168
|
+
**[ci skip]**: If your commit does not need to go through the Continuous Integration server, add `[ci skip]` to your commit message.
|
169
|
+
This is used for updates to the documentation and stylesheet changes.
|
170
|
+
|
171
|
+
# Reviewing Changes
|
172
|
+
|
173
|
+
The review process is a conversation between the submitter and the team as a whole.
|
174
|
+
Please feel free to bring other people into the conversation as necessary.
|
175
|
+
|
176
|
+
As either the submitter or reviewer, feel free to assign the Pull Request to a repository contributor.
|
177
|
+
This is a way of indicating that you want that repository contributor to review the change.
|
178
|
+
|
179
|
+
When you do assign someone to the Pull Request, please make sure to add a comment stating why you assigned it to them.
|
180
|
+
|
181
|
+
## Responsibilities of a Reviewer
|
182
|
+
|
183
|
+
As a reviewer, it is important that the pull request:
|
184
|
+
|
185
|
+
* Has a [well written commit message](#well-written-commit-messages)
|
186
|
+
* Has [well written code](#coding-guidelines)
|
187
|
+
* All tests pass in the test suite
|
188
|
+
* Any questions regarding the pull request are answered
|
189
|
+
* Adjudicate if the Pull Request should be squashed into a smaller number of commits
|
190
|
+
|
191
|
+
## Responsibilities of the Submitter
|
192
|
+
|
193
|
+
**As the submitter**, you should be responsive to the review process:
|
194
|
+
|
195
|
+
* answering questions
|
196
|
+
* making refinements
|
197
|
+
* providing clarification
|
198
|
+
* rebasing your commits.
|
199
|
+
|
200
|
+
*If your changes are gridlocked please notify [@jeremyf](https://github.com/jeremyf) via a comment on the pull request.*
|
201
|
+
|
202
|
+
# Merging Changes
|
203
|
+
|
204
|
+
*If a pull request has received at least one Thumbs Up, but has still not been merged, please notify [@jeremyf](https://github.com/jeremyf) via a comment on the pull request.*
|
205
|
+
|
206
|
+
**As the submitter,** you should not merge your own pull request.
|
207
|
+
That is bad form.
|
208
|
+
|
209
|
+
**As the reviewer,** if you are comfortable merge the pull request.
|
210
|
+
Otherwise feel free to assign the pull request to another contributor for final merging.
|
211
|
+
|
212
|
+
**As the merger,** once you have merged the pull request, go ahead and delete the pull request's topic branch.
|
213
|
+
You are now on the hook for any breaking of the build.
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Orcid [](http://badge.fury.io/rb/orcid) [](http://badge.fury.io/rb/orcid) [](https://travis-ci.org/projecthydra-labs/orcid)
|
2
2
|
|
3
3
|
A [Rails Engine](https://guides.rubyonrails.org/engines.html) for integrating with [Orcid](https://orcid.org).
|
4
4
|
|
@@ -9,6 +9,7 @@ To fully interact with the Orcid remote services, you will need to [register you
|
|
9
9
|
* [Registering for an ORCID application profile](#registering-for-an-orcid-application-profile)
|
10
10
|
* [Setting up your own ORCIDs in the ORCID Development Sandbox](#setting-up-your-own-orcids-in-the-orcid-development-sandbox)
|
11
11
|
* [Running the tests](#running-the-tests)
|
12
|
+
* [Contributing to this gem](./CONTRIBUTING.md)
|
12
13
|
|
13
14
|
## Installation
|
14
15
|
|
@@ -120,7 +121,3 @@ Run the online tests with
|
|
120
121
|
```console
|
121
122
|
$ rake spec:online
|
122
123
|
```
|
123
|
-
|
124
|
-
## TODO Items
|
125
|
-
|
126
|
-
* When searching for your profile, expose Name and associated DOI as query parameters.
|
@@ -25,7 +25,17 @@ module Orcid
|
|
25
25
|
return false if redirecting_because_user_has_existing_profile_request
|
26
26
|
assign_attributes(new_profile_request)
|
27
27
|
create_profile_request(new_profile_request)
|
28
|
-
|
28
|
+
# As a named singular resource, url_for(profile_request) treates the
|
29
|
+
# input profile_request as the :format paramter. When you run
|
30
|
+
# `$ rake app:routes` in this gem, there is not a named route for:
|
31
|
+
# GET /profile_request(.:format) orcid/profile_requests#show
|
32
|
+
#
|
33
|
+
# Thus we need to pass the location.
|
34
|
+
if new_profile_request.valid?
|
35
|
+
respond_with(orcid, location: orcid.profile_request_path)
|
36
|
+
else
|
37
|
+
respond_with(orcid, new_profile_request)
|
38
|
+
end
|
29
39
|
end
|
30
40
|
|
31
41
|
protected
|
@@ -7,8 +7,9 @@ module Orcid
|
|
7
7
|
include ActiveModel::Conversion
|
8
8
|
extend ActiveModel::Naming
|
9
9
|
|
10
|
+
# See: http://support.orcid.org/knowledgebase/articles/132354-tutorial-searching-with-the-api
|
10
11
|
class_attribute :available_query_attribute_names
|
11
|
-
self.available_query_attribute_names = [:email, :text]
|
12
|
+
self.available_query_attribute_names = [:email, :text, :digital_object_ids]
|
12
13
|
|
13
14
|
available_query_attribute_names.each do |attribute_name|
|
14
15
|
attribute attribute_name
|
@@ -68,7 +69,16 @@ module Orcid
|
|
68
69
|
private :query_requested?
|
69
70
|
|
70
71
|
def query_attributes
|
71
|
-
|
72
|
+
available_query_attribute_names.each_with_object({}) do |name, mem|
|
73
|
+
orcid_formatted_name = convert_attribute_name_to_orcid_format(name)
|
74
|
+
mem[orcid_formatted_name] = attributes.fetch(name)
|
75
|
+
mem
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def convert_attribute_name_to_orcid_format(name)
|
80
|
+
name.to_s.gsub(/_+/, '-')
|
72
81
|
end
|
82
|
+
private :convert_attribute_name_to_orcid_format
|
73
83
|
end
|
74
84
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Orcid
|
2
|
+
# ProfileStatus.status
|
3
|
+
# **:authenticated_connection** - User has authenticated against the Orcid
|
4
|
+
# remote system
|
5
|
+
# **:pending_connection** - User has indicated there is a connection, but has
|
6
|
+
# not authenticated against the Orcid remote system
|
7
|
+
# **:profile_request_pending** - User has requested a profile be created on
|
8
|
+
# their behalf
|
9
|
+
# **:unknown** - None of the above
|
10
|
+
class ProfileStatus
|
11
|
+
def self.for(user, collaborators = {}, &block)
|
12
|
+
new(user, collaborators, &block).status
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :user, :profile_finder, :request_finder, :callback_handler
|
16
|
+
|
17
|
+
def initialize(user, collaborators = {})
|
18
|
+
@user = user
|
19
|
+
@profile_finder = collaborators.fetch(:profile_finder) { default_profile_finder }
|
20
|
+
@request_finder = collaborators.fetch(:request_finder) { default_request_finder }
|
21
|
+
@callback_handler = collaborators.fetch(:callback_handler) { default_callback_handler }
|
22
|
+
yield(callback_handler) if block_given?
|
23
|
+
end
|
24
|
+
|
25
|
+
def status
|
26
|
+
return callback(:unknown) if user.nil?
|
27
|
+
profile = profile_finder.call(user)
|
28
|
+
if profile
|
29
|
+
if profile.verified_authentication?
|
30
|
+
return callback(:authenticated_connection, profile)
|
31
|
+
else
|
32
|
+
return callback(:pending_connection, profile)
|
33
|
+
end
|
34
|
+
else
|
35
|
+
request = request_finder.call(user)
|
36
|
+
if request
|
37
|
+
return callback(:profile_request_pending, request)
|
38
|
+
else
|
39
|
+
return callback(:unknown)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
return callback(:unknown)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def callback(name, *args)
|
48
|
+
callback_handler.call(name, *args)
|
49
|
+
name
|
50
|
+
end
|
51
|
+
|
52
|
+
def default_callback_handler
|
53
|
+
require 'orcid/named_callbacks'
|
54
|
+
NamedCallbacks.new
|
55
|
+
end
|
56
|
+
|
57
|
+
def default_profile_finder
|
58
|
+
require 'orcid'
|
59
|
+
Orcid.method(:profile_for)
|
60
|
+
end
|
61
|
+
|
62
|
+
def default_request_finder
|
63
|
+
require 'orcid/profile_request'
|
64
|
+
ProfileRequest.method(:find_by_user)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'orcid/remote/service'
|
2
|
+
require 'oauth2/error'
|
3
|
+
require 'nokogiri'
|
2
4
|
module Orcid
|
3
5
|
module Remote
|
4
6
|
# Responsible for minting a new ORCID for the given payload.
|
@@ -20,6 +22,8 @@ module Orcid
|
|
20
22
|
def call(payload)
|
21
23
|
response = deliver(payload)
|
22
24
|
parse(response)
|
25
|
+
rescue ::OAuth2::Error => e
|
26
|
+
parse_exception(e)
|
23
27
|
end
|
24
28
|
|
25
29
|
protected
|
@@ -40,6 +44,17 @@ module Orcid
|
|
40
44
|
end
|
41
45
|
end
|
42
46
|
|
47
|
+
def parse_exception(exception)
|
48
|
+
doc = Nokogiri::XML.parse(exception.response.body)
|
49
|
+
error_text = doc.css('error-desc').text
|
50
|
+
if error_text.to_s.size > 0
|
51
|
+
callback(:orcid_validation_error, error_text)
|
52
|
+
false
|
53
|
+
else
|
54
|
+
fail exception
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
43
58
|
def default_headers
|
44
59
|
{
|
45
60
|
'Accept' => 'application/xml',
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require_dependency 'orcid/remote/profile_query_service'
|
2
|
+
module Orcid
|
3
|
+
module Remote
|
4
|
+
class ProfileQueryService
|
5
|
+
class ResponseParser
|
6
|
+
|
7
|
+
# A convenience method to expose entry into the ResponseParser function
|
8
|
+
def self.call(document, collaborators = {})
|
9
|
+
new(collaborators).call(document)
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :response_builder, :logger
|
13
|
+
|
14
|
+
def initialize(collaborators = {})
|
15
|
+
@response_builder = collaborators.fetch(:response_builder) do
|
16
|
+
SearchResponse
|
17
|
+
end
|
18
|
+
@logger = collaborators.fetch(:logger) do
|
19
|
+
Rails.logger
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def call(document)
|
24
|
+
json = JSON.parse(document)
|
25
|
+
json.fetch('orcid-search-results').fetch('orcid-search-result')
|
26
|
+
.each_with_object([]) do |result, returning_value|
|
27
|
+
profile = result.fetch('orcid-profile')
|
28
|
+
begin
|
29
|
+
identifier = profile.fetch('orcid-identifier').fetch('path')
|
30
|
+
orcid_bio = profile.fetch('orcid-bio')
|
31
|
+
given_names = orcid_bio.fetch('personal-details').fetch('given-names').fetch('value')
|
32
|
+
family_name = orcid_bio.fetch('personal-details').fetch('family-name').fetch('value')
|
33
|
+
emails = []
|
34
|
+
contact_details = orcid_bio['contact-details']
|
35
|
+
if contact_details
|
36
|
+
emails = (contact_details['email'] || []).map {|email| email.fetch('value') }
|
37
|
+
end
|
38
|
+
label = "#{given_names} #{family_name}"
|
39
|
+
label << ' (' << emails.join(', ') << ')' if emails.any?
|
40
|
+
label << " [ORCID: #{identifier}]"
|
41
|
+
biography = ''
|
42
|
+
biography = orcid_bio['biography']['value'] if orcid_bio['biography']
|
43
|
+
returning_value << response_builder.new('id' => identifier, 'label' => label, 'biography' => biography)
|
44
|
+
rescue KeyError => e
|
45
|
+
logger.warn("Unexpected ORCID JSON Response, part of the response has been ignored.\tException Encountered:#{e.class}\t#{e}")
|
46
|
+
end
|
47
|
+
returning_value
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -9,11 +9,12 @@ module Orcid
|
|
9
9
|
end
|
10
10
|
|
11
11
|
attr_reader :token, :path, :headers, :response_builder, :query_builder
|
12
|
+
attr_reader :parser
|
12
13
|
def initialize(config = {}, &callbacks)
|
13
14
|
super(&callbacks)
|
14
15
|
@query_builder = config.fetch(:query_parameter_builder) { QueryParameterBuilder }
|
15
16
|
@token = config.fetch(:token) { default_token }
|
16
|
-
@
|
17
|
+
@parser = config.fetch(:parser) { ResponseParser }
|
17
18
|
@path = config.fetch(:path) { 'v1.1/search/orcid-bio/' }
|
18
19
|
@headers = config.fetch(:headers) { default_headers }
|
19
20
|
end
|
@@ -41,7 +42,7 @@ module Orcid
|
|
41
42
|
end
|
42
43
|
|
43
44
|
def issue_callbacks(search_results)
|
44
|
-
if search_results.any?
|
45
|
+
if Array(search_results).flatten.compact.any?
|
45
46
|
callback(:found, search_results)
|
46
47
|
else
|
47
48
|
callback(:not_found)
|
@@ -54,27 +55,7 @@ module Orcid
|
|
54
55
|
end
|
55
56
|
|
56
57
|
def parse(document)
|
57
|
-
|
58
|
-
|
59
|
-
json.fetch('orcid-search-results').fetch('orcid-search-result')
|
60
|
-
.each_with_object([]) do |result, returning_value|
|
61
|
-
profile = result.fetch('orcid-profile')
|
62
|
-
identifier = profile.fetch('orcid-identifier').fetch('path')
|
63
|
-
orcid_bio = profile.fetch('orcid-bio')
|
64
|
-
given_names = orcid_bio.fetch('personal-details').fetch('given-names').fetch('value')
|
65
|
-
family_name = orcid_bio.fetch('personal-details').fetch('family-name').fetch('value')
|
66
|
-
emails = []
|
67
|
-
contact_details = orcid_bio['contact-details']
|
68
|
-
if contact_details
|
69
|
-
emails = (contact_details['email'] || []).map {|email| email.fetch('value') }
|
70
|
-
end
|
71
|
-
label = "#{given_names} #{family_name}"
|
72
|
-
label << ' (' << emails.join(', ') << ')' if emails.any?
|
73
|
-
label << " [ORCID: #{identifier}]"
|
74
|
-
biography = ''
|
75
|
-
biography = orcid_bio['biography']['value'] if orcid_bio['biography']
|
76
|
-
returning_value << response_builder.new('id' => identifier, 'label' => label, 'biography' => biography)
|
77
|
-
end
|
58
|
+
parser.call(document)
|
78
59
|
end
|
79
60
|
|
80
61
|
end
|
@@ -0,0 +1,5 @@
|
|
1
|
+
<div class="authenticated-connection">
|
2
|
+
<p>
|
3
|
+
Your ORCID (<a class="orcid-profile-id" href="<%= Orcid.url_for_orcid_id(authenticated_connection.orcid_profile_id)%>" ><%= authenticated_connection.orcid_profile_id %></a>) has been authenticated for this application.
|
4
|
+
</p>
|
5
|
+
</div>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<% profile_connection = Orcid::ProfileConnection.new %>
|
2
|
+
<% default_search_text = '' unless defined?(default_search_text) %>
|
3
|
+
<div class="options-to-connect-orcid-profile">
|
4
|
+
<p>
|
5
|
+
<%= link_to t('orcid/profile_connection.look_up_your_existing_orcid', scope: 'helpers.label'), orcid.new_profile_connection_path(profile_connection:{text: default_search_text}) %>
|
6
|
+
</p>
|
7
|
+
<p>
|
8
|
+
<%= link_to t('orcid/profile_connection.create_an_orcid', scope: 'helpers.label'), orcid.new_profile_request_path %>
|
9
|
+
</p>
|
10
|
+
<p>
|
11
|
+
<%= form_for(profile_connection, as: :profile_connection, url: orcid.profile_connections_path, method: :post) do |f| %>
|
12
|
+
<%# Note the `scope: 'helpers.label'` option is the default for the label, but I want to call those specifically out %>
|
13
|
+
<%= f.label :orcid_profile_id, t('orcid/profile_connection.orcid_profile_id', scope: 'helpers.label') %>
|
14
|
+
<%= f.text_field :orcid_profile_id, class:"orcid-input" %>
|
15
|
+
<button type="submit" class="search-submit btn btn-primary" id="keyword-search-submit" tabindex="2">
|
16
|
+
<span class="orcid-connect"><%= t('orcid/profile_connection.connect_button_text', scope: 'helpers.label') %></span>
|
17
|
+
</button>
|
18
|
+
<% end %>
|
19
|
+
</p>
|
20
|
+
</div>
|
@@ -1,22 +1,19 @@
|
|
1
|
-
<%
|
2
|
-
<% default_search_text = '' unless defined?(default_search_text) %>
|
1
|
+
<% defined?(status_processor) || status_processor = Orcid::ProfileStatus.method(:for) %>
|
3
2
|
<div class='orcid-connector'>
|
4
3
|
<h3><i class="icon-user"></i> <%= link_to t('orcid.verbose_name'), 'http://orcid.org/' %></h3>
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
4
|
+
<% status_processor.call(current_user) do |on|%>
|
5
|
+
<% on.profile_request_pending do |pending_request| %>
|
6
|
+
<%= render partial: 'orcid/profile_connections/profile_request_pending', object: pending_request %>
|
7
|
+
<% end %>
|
8
|
+
<% on.authenticated_connection do |profile| %>
|
9
|
+
<%= render partial: 'orcid/profile_connections/authenticated_connection', object: profile %>
|
10
|
+
<% end %>
|
11
|
+
<% on.pending_connection do |profile| %>
|
12
|
+
<%= render partial: 'orcid/profile_connections/pending_connection', object: profile %>
|
13
|
+
<% end %>
|
14
|
+
<% on.unknown do %>
|
15
|
+
<% defined?(default_search_text) || default_search_text = '' %>
|
16
|
+
<%= render template: 'orcid/profile_connections/_options_to_connect_orcid_profile', locals: { default_search_text: default_search_text } %>
|
17
|
+
<% end %>
|
19
18
|
<% end %>
|
20
|
-
</p>
|
21
19
|
</div>
|
22
|
-
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<div class="pending-connection">
|
2
|
+
<p>
|
3
|
+
You have an ORCID (<a class="orcid-profile-id" href="<%= Orcid.url_for_orcid_id(pending_connection.orcid_profile_id)%>" ><%= pending_connection.orcid_profile_id %></a>).
|
4
|
+
</p>
|
5
|
+
<p>However, your ORCID has not been verified by this system. There are a few possibilities:
|
6
|
+
<ul>
|
7
|
+
<li>You may not have claimed your ORCID. <a class="find-out-more" href="http://support.orcid.org/knowledgebase/articles/164480-creating-claiming-new-records">Find out more about claiming your ORCID.</a></li>
|
8
|
+
<li>You have claimed your ORCID, but have not used it to <%= link_to('sign into this application', user_omniauth_authorize_path(provider: 'orcid'), class: 'signin-via-orcid') %>.</li>
|
9
|
+
</ul>
|
10
|
+
</p>
|
11
|
+
</div>
|
data/config/locales/orcid.en.yml
CHANGED
@@ -30,7 +30,7 @@ en:
|
|
30
30
|
messages:
|
31
31
|
profile_connection_not_found: "Unable to find an existing Orcid Profile connection."
|
32
32
|
verified_profile_connection_exists: "You have already connected and verified your Orcid Profile (%{orcid_profile_id})."
|
33
|
-
verbose_name: Open Researcher and Contributor ID (ORCID)
|
33
|
+
verbose_name: Open Researcher and Contributor ID (ORCID)
|
34
34
|
helpers:
|
35
35
|
label:
|
36
36
|
orcid/profile_connection:
|
@@ -40,6 +40,14 @@ module Orcid
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
+
attr_writer :host_url
|
44
|
+
def host_url
|
45
|
+
@host_url ||= store.fetch('ORCID_HOST_URL') do
|
46
|
+
uri = URI.parse(signin_via_json_url)
|
47
|
+
"#{uri.scheme}://#{uri.host}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
43
51
|
attr_writer :authorize_url
|
44
52
|
def authorize_url
|
45
53
|
@authorize_url ||= store.fetch('ORCID_AUTHORIZE_URL') do
|
data/lib/orcid/version.rb
CHANGED
data/lib/orcid.rb
CHANGED
@@ -71,6 +71,10 @@ module Orcid
|
|
71
71
|
object.run
|
72
72
|
end
|
73
73
|
|
74
|
+
def url_for_orcid_id(orcid_profile_id)
|
75
|
+
File.join(provider.host_url, orcid_profile_id)
|
76
|
+
end
|
77
|
+
|
74
78
|
def oauth_client
|
75
79
|
# passing the site: option as Orcid's Sandbox has an invalid certificate
|
76
80
|
# for the api.sandbox.orcid.org
|