globus_client 0.1.0 → 0.2.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/.autoupdate/postupdate +19 -0
- data/.rspec +3 -0
- data/.rubocop/custom.yml +50 -0
- data/.rubocop.yml +15 -0
- data/.rubocop_todo.yml +7 -0
- data/.standard.yml +1 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +109 -0
- data/LICENSE +15 -0
- data/README.md +69 -0
- data/Rakefile +10 -0
- data/globus_client.gemspec +44 -0
- data/lib/globus/client/authenticator.rb +43 -0
- data/lib/globus/client/endpoint.rb +119 -0
- data/lib/globus/client/identity.rb +48 -0
- data/lib/globus/client/unexpected_response.rb +50 -0
- data/lib/globus/client/version.rb +7 -0
- data/lib/globus/client.rb +58 -0
- data/sig/globus_client.rbs +4 -0
- metadata +37 -14
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 77e44f70f4c6ee02299ac4718e8ed5d1c2c88d58c71a4ba2c3bfbe970e2bb5df
|
|
4
|
+
data.tar.gz: b74fbc9c11f7b9c9fdadac074fe8709711f0065d03ab01fcecb159374446bb9b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 26d0923724fc1301b344b190d347abd25a1c6e17c57214f424df46d3739fc64ead270e9ae8eb3eddecdfc6700d6c52bc935cda654aef8109a01b45a5d187ca39
|
|
7
|
+
data.tar.gz: a5c9e21ec3917f8c560ed34eaefb38af1253cddd582223e1986a78d165301443b689875a20a96f24ef1eb514aca19fbc7994af52c1f441b5db60d9428592cd0b
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/bin/bash --login
|
|
2
|
+
|
|
3
|
+
# This script is called by our weekly dependency update job in Jenkins after updating Ruby and other deps
|
|
4
|
+
|
|
5
|
+
# Switch to Ruby 3.1 for Globus::Client (3.0 is default in Jenkinsfile)
|
|
6
|
+
rvm use 3.1.2@globus_client --create &&
|
|
7
|
+
gem install bundler &&
|
|
8
|
+
bundle install --gemfile Gemfile
|
|
9
|
+
|
|
10
|
+
standardrb --fix > globus_client_standard.txt
|
|
11
|
+
|
|
12
|
+
retVal=$?
|
|
13
|
+
|
|
14
|
+
git commit -am "Update to latest standard style guide"
|
|
15
|
+
|
|
16
|
+
if [ $retVal -ne 0 ]; then
|
|
17
|
+
echo "ERROR UPDATING RUBY TO STANDARD STYLE (globus_client)"
|
|
18
|
+
cat globus_client_standard.txt
|
|
19
|
+
fi
|
data/.rspec
ADDED
data/.rubocop/custom.yml
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
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
|
+
RSpec/Capybara/NegationMatcher: # new in 2.14
|
|
34
|
+
Enabled: true
|
|
35
|
+
RSpec/Capybara/SpecificActions: # new in 2.14
|
|
36
|
+
Enabled: true
|
|
37
|
+
RSpec/Capybara/SpecificFinders: # new in 2.13
|
|
38
|
+
Enabled: true
|
|
39
|
+
RSpec/Capybara/SpecificMatcher: # new in 2.12
|
|
40
|
+
Enabled: true
|
|
41
|
+
RSpec/FactoryBot/ConsistentParenthesesStyle: # new in 2.14
|
|
42
|
+
Enabled: true
|
|
43
|
+
RSpec/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
|
data/.rubocop.yml
ADDED
data/.rubocop_todo.yml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# This configuration was generated by
|
|
2
|
+
# `rubocop --auto-gen-config`
|
|
3
|
+
# on 2022-10-27 22:53:23 UTC using RuboCop version 1.37.0.
|
|
4
|
+
# The point is for the user to remove these configuration records
|
|
5
|
+
# one by one as the offenses are removed from the code base.
|
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
data/.standard.yml
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
parallel: true
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
globus_client (0.1.0)
|
|
5
|
+
activesupport (>= 4.2, < 8)
|
|
6
|
+
faraday
|
|
7
|
+
zeitwerk
|
|
8
|
+
|
|
9
|
+
GEM
|
|
10
|
+
remote: https://rubygems.org/
|
|
11
|
+
specs:
|
|
12
|
+
activesupport (7.0.4)
|
|
13
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
14
|
+
i18n (>= 1.6, < 2)
|
|
15
|
+
minitest (>= 5.1)
|
|
16
|
+
tzinfo (~> 2.0)
|
|
17
|
+
addressable (2.8.1)
|
|
18
|
+
public_suffix (>= 2.0.2, < 6.0)
|
|
19
|
+
ast (2.4.2)
|
|
20
|
+
byebug (11.1.3)
|
|
21
|
+
concurrent-ruby (1.1.10)
|
|
22
|
+
crack (0.4.5)
|
|
23
|
+
rexml
|
|
24
|
+
diff-lcs (1.5.0)
|
|
25
|
+
docile (1.4.0)
|
|
26
|
+
faraday (2.7.1)
|
|
27
|
+
faraday-net_http (>= 2.0, < 3.1)
|
|
28
|
+
ruby2_keywords (>= 0.0.4)
|
|
29
|
+
faraday-net_http (3.0.2)
|
|
30
|
+
hashdiff (1.0.1)
|
|
31
|
+
i18n (1.12.0)
|
|
32
|
+
concurrent-ruby (~> 1.0)
|
|
33
|
+
json (2.6.2)
|
|
34
|
+
minitest (5.16.3)
|
|
35
|
+
parallel (1.22.1)
|
|
36
|
+
parser (3.1.2.1)
|
|
37
|
+
ast (~> 2.4.1)
|
|
38
|
+
public_suffix (5.0.0)
|
|
39
|
+
rainbow (3.1.1)
|
|
40
|
+
rake (13.0.6)
|
|
41
|
+
regexp_parser (2.6.1)
|
|
42
|
+
rexml (3.2.5)
|
|
43
|
+
rspec (3.12.0)
|
|
44
|
+
rspec-core (~> 3.12.0)
|
|
45
|
+
rspec-expectations (~> 3.12.0)
|
|
46
|
+
rspec-mocks (~> 3.12.0)
|
|
47
|
+
rspec-core (3.12.0)
|
|
48
|
+
rspec-support (~> 3.12.0)
|
|
49
|
+
rspec-expectations (3.12.0)
|
|
50
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
51
|
+
rspec-support (~> 3.12.0)
|
|
52
|
+
rspec-mocks (3.12.0)
|
|
53
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
54
|
+
rspec-support (~> 3.12.0)
|
|
55
|
+
rspec-support (3.12.0)
|
|
56
|
+
rubocop (1.39.0)
|
|
57
|
+
json (~> 2.3)
|
|
58
|
+
parallel (~> 1.10)
|
|
59
|
+
parser (>= 3.1.2.1)
|
|
60
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
61
|
+
regexp_parser (>= 1.8, < 3.0)
|
|
62
|
+
rexml (>= 3.2.5, < 4.0)
|
|
63
|
+
rubocop-ast (>= 1.23.0, < 2.0)
|
|
64
|
+
ruby-progressbar (~> 1.7)
|
|
65
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
|
66
|
+
rubocop-ast (1.23.0)
|
|
67
|
+
parser (>= 3.1.1.0)
|
|
68
|
+
rubocop-performance (1.15.0)
|
|
69
|
+
rubocop (>= 1.7.0, < 2.0)
|
|
70
|
+
rubocop-ast (>= 0.4.0)
|
|
71
|
+
rubocop-rspec (2.15.0)
|
|
72
|
+
rubocop (~> 1.33)
|
|
73
|
+
ruby-progressbar (1.11.0)
|
|
74
|
+
ruby2_keywords (0.0.5)
|
|
75
|
+
simplecov (0.21.2)
|
|
76
|
+
docile (~> 1.1)
|
|
77
|
+
simplecov-html (~> 0.11)
|
|
78
|
+
simplecov_json_formatter (~> 0.1)
|
|
79
|
+
simplecov-html (0.12.3)
|
|
80
|
+
simplecov_json_formatter (0.1.4)
|
|
81
|
+
standard (1.18.0)
|
|
82
|
+
rubocop (= 1.39.0)
|
|
83
|
+
rubocop-performance (= 1.15.0)
|
|
84
|
+
tzinfo (2.0.5)
|
|
85
|
+
concurrent-ruby (~> 1.0)
|
|
86
|
+
unicode-display_width (2.3.0)
|
|
87
|
+
webmock (3.18.1)
|
|
88
|
+
addressable (>= 2.8.0)
|
|
89
|
+
crack (>= 0.3.2)
|
|
90
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
|
91
|
+
zeitwerk (2.6.6)
|
|
92
|
+
|
|
93
|
+
PLATFORMS
|
|
94
|
+
x86_64-darwin-20
|
|
95
|
+
x86_64-darwin-21
|
|
96
|
+
x86_64-linux
|
|
97
|
+
|
|
98
|
+
DEPENDENCIES
|
|
99
|
+
byebug
|
|
100
|
+
globus_client!
|
|
101
|
+
rake (~> 13.0)
|
|
102
|
+
rspec (~> 3.0)
|
|
103
|
+
rubocop-rspec
|
|
104
|
+
simplecov
|
|
105
|
+
standard
|
|
106
|
+
webmock
|
|
107
|
+
|
|
108
|
+
BUNDLED WITH
|
|
109
|
+
2.3.19
|
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,69 @@
|
|
|
1
|
+
[](https://badge.fury.io/rb/globus_client)
|
|
2
|
+
[](https://circleci.com/gh/sul-dlss/globus_client)
|
|
3
|
+
[](https://codeclimate.com/github/sul-dlss/globus_client)
|
|
4
|
+
[](https://codeclimate.com/github/sul-dlss/globus_client/test_coverage)
|
|
5
|
+
|
|
6
|
+
# Globus::Client
|
|
7
|
+
|
|
8
|
+
Globus::Client is a Ruby gem that acts as a client to the RESTful HTTP APIs provided by the [Globus service](https://docs.globus.org/api/).
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
Install the gem and add to the application's Gemfile by executing:
|
|
13
|
+
|
|
14
|
+
$ bundle add globus_client
|
|
15
|
+
|
|
16
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
|
17
|
+
|
|
18
|
+
$ gem install globus_client
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
For one-off requests:
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
require 'globus/client'
|
|
26
|
+
|
|
27
|
+
# NOTE: The settings below live in the consumer, not in the gem.
|
|
28
|
+
client = Globus::Client.configure(
|
|
29
|
+
client_id: Settings.globus.client_id,
|
|
30
|
+
client_secret: Settings.globus.client_secret,
|
|
31
|
+
uploads_directory: Settings.globus.uploads_directory,
|
|
32
|
+
transfer_endpoint_id: Settings.globus.transfer_endpoint_id
|
|
33
|
+
)
|
|
34
|
+
client.mkdir(user_id: 'mjgiarlo', work_id: 1234, work_version: 1)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
You can also invoke methods directly on the client class, which is useful in a
|
|
38
|
+
Rails application environment where you might initialize the client in an
|
|
39
|
+
initializer and then invoke client methods in many other contexts where you want
|
|
40
|
+
to be sure configuration has already occurred, e.g.:
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
# config/initializers/globus_client.rb
|
|
44
|
+
Globus::Client.configure(
|
|
45
|
+
client_id: Settings.globus.client_id,
|
|
46
|
+
client_secret: Settings.globus.client_secret,
|
|
47
|
+
uploads_directory: Settings.globus.uploads_directory,
|
|
48
|
+
transfer_endpoint_id: Settings.globus.transfer_endpoint_id
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# app/services/my_globus_service.rb
|
|
52
|
+
# ...
|
|
53
|
+
def create_user_directory
|
|
54
|
+
Globus::Client.mkdir(user_id: 'mjgiarlo', work_id: 1234, work_version: 1)
|
|
55
|
+
end
|
|
56
|
+
# ...
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Development
|
|
60
|
+
|
|
61
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
62
|
+
`rake spec` to run the tests. You can also run `bin/console` for an interactive
|
|
63
|
+
prompt that will allow you to experiment.
|
|
64
|
+
|
|
65
|
+
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).
|
|
66
|
+
|
|
67
|
+
## Contributing
|
|
68
|
+
|
|
69
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/sul-dlss/globus_client.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
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 "globus/client/version"
|
|
6
|
+
|
|
7
|
+
Gem::Specification.new do |spec|
|
|
8
|
+
spec.name = "globus_client"
|
|
9
|
+
spec.version = Globus::Client::VERSION
|
|
10
|
+
spec.authors = ["Aaron Collier", "Laura Wrubel", "Mike Giarlo"]
|
|
11
|
+
spec.email = ["aaron.collier@stanford.edu", "lwrubel@stanford.edu", "mjgiarlo@stanford.edu"]
|
|
12
|
+
|
|
13
|
+
spec.summary = "Interface for interacting with the Globus API."
|
|
14
|
+
spec.description = "This provides API interaction with the Globus API"
|
|
15
|
+
spec.homepage = "https://github.com/sul-dlss/globus_client"
|
|
16
|
+
spec.required_ruby_version = ">= 2.6.0"
|
|
17
|
+
|
|
18
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
19
|
+
spec.metadata["source_code_uri"] = "https://github.com/sul-dlss/globus_client"
|
|
20
|
+
spec.metadata["changelog_uri"] = "https://github.com/sul-dlss/globus_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 "zeitwerk"
|
|
37
|
+
|
|
38
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
|
39
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
|
40
|
+
spec.add_development_dependency "standard"
|
|
41
|
+
spec.add_development_dependency "rubocop-rspec"
|
|
42
|
+
spec.add_development_dependency "simplecov"
|
|
43
|
+
spec.add_development_dependency "webmock"
|
|
44
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Globus
|
|
4
|
+
class Client
|
|
5
|
+
# The namespace for the "login" command
|
|
6
|
+
class Authenticator
|
|
7
|
+
def self.token(client_id, client_secret, auth_url)
|
|
8
|
+
new(client_id, client_secret, auth_url).token
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def initialize(client_id, client_secret, auth_url)
|
|
12
|
+
@client_id = client_id
|
|
13
|
+
@client_secret = client_secret
|
|
14
|
+
@auth_url = auth_url
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Request an access_token
|
|
18
|
+
def token
|
|
19
|
+
response = connection.post("/v2/oauth2/token", form_data)
|
|
20
|
+
|
|
21
|
+
JSON.parse(response.body)["access_token"]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
attr_reader :client_id, :client_secret, :auth_url
|
|
27
|
+
|
|
28
|
+
def connection
|
|
29
|
+
Faraday.new(url: auth_url)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def form_data
|
|
33
|
+
{
|
|
34
|
+
client_id:,
|
|
35
|
+
client_secret:,
|
|
36
|
+
encoding: "form",
|
|
37
|
+
grant_type: "client_credentials",
|
|
38
|
+
scope: "urn:globus:auth:scope:transfer.api.globus.org:all"
|
|
39
|
+
}
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Globus
|
|
4
|
+
class Client
|
|
5
|
+
# The namespace for endpoint API operations
|
|
6
|
+
class Endpoint
|
|
7
|
+
# @param config [#token, #uploads_directory, #transfer_endpoint_id, #transfer_url, #auth_url] configuration for the gem
|
|
8
|
+
# @param user_id [String] conventionally, we use the SUNet ID, not an email address
|
|
9
|
+
# @param work_id [#to_s] the identifier of the work (e.g., an H2 work)
|
|
10
|
+
# @param work_version [#to_s] the version of the work (e.g., an H2 version)
|
|
11
|
+
def initialize(config, user_id:, work_id:, work_version:)
|
|
12
|
+
@config = config
|
|
13
|
+
@user_id = user_id
|
|
14
|
+
@work_id = work_id
|
|
15
|
+
@work_version = work_version
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# This is a temporary method to show parsing of data returned.
|
|
19
|
+
def length
|
|
20
|
+
objects["total"]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Create a directory https://docs.globus.org/api/transfer/file_operations/#make_directory
|
|
24
|
+
def mkdir
|
|
25
|
+
# transfer API does not support recursive directory creation
|
|
26
|
+
paths.each do |path|
|
|
27
|
+
response = call_mkdir(path)
|
|
28
|
+
next if response.success?
|
|
29
|
+
|
|
30
|
+
# if directory already exists
|
|
31
|
+
if response.status == 502
|
|
32
|
+
error = JSON.parse(response.body)
|
|
33
|
+
next if error["code"] == "ExternalError.MkdirFailedExists"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
UnexpectedResponse.call(response)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Assign a user read/write permissions for a directory https://docs.globus.org/api/transfer/acl/#rest_access_create
|
|
41
|
+
def set_permissions
|
|
42
|
+
path = "#{config.uploads_directory}/#{user_id}/work#{work_id}/version#{work_version}/"
|
|
43
|
+
identity = Globus::Client::Identity.new(config)
|
|
44
|
+
id = identity.get_identity_id(user_id)
|
|
45
|
+
call_access(path:, id:, user_id:)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
attr_reader :config, :user_id, :work_id, :work_version
|
|
51
|
+
|
|
52
|
+
def connection
|
|
53
|
+
# Transfer API connection
|
|
54
|
+
Faraday.new(
|
|
55
|
+
url: config.transfer_url,
|
|
56
|
+
headers: {Authorization: "Bearer #{config.token}"}
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Builds up a path from a list of path elements. E.g., input would look like:
|
|
61
|
+
# ["mjgiarlo", "work123", "version1"]
|
|
62
|
+
# And this method returns:
|
|
63
|
+
# ["/uploads/mjgiarlo/", "/uploads/mjgiarlo/work123/", "/uploads/mjgiarlo/work123/version1/"]
|
|
64
|
+
def paths
|
|
65
|
+
path_segments.map.with_index do |_segment, index|
|
|
66
|
+
File.join(config.uploads_directory, path_segments.slice(..index)).concat("/")
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def path_segments
|
|
71
|
+
[user_id, "work#{work_id}", "version#{work_version}"]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def endpoint
|
|
75
|
+
"/v0.10/operation/endpoint/#{config.transfer_endpoint_id}"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# @return [Faraday::Response]
|
|
79
|
+
def call_mkdir(path)
|
|
80
|
+
connection.post("#{endpoint}/mkdir") do |req|
|
|
81
|
+
req.headers["Content-Type"] = "application/json"
|
|
82
|
+
req.body = {
|
|
83
|
+
DATA_TYPE: "mkdir",
|
|
84
|
+
path:
|
|
85
|
+
}.to_json
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Makes the API call to Globus to set permissions
|
|
90
|
+
# @param path [String] the directory on the globus endpoint
|
|
91
|
+
# @param id [String] globus identifier associated with the user_id email
|
|
92
|
+
# @param user_id [String] user_id, not email address
|
|
93
|
+
# @return [Faraday::Response]
|
|
94
|
+
def call_access(path:, id:, user_id:)
|
|
95
|
+
response = connection.post("#{endpoint}/access") do |req|
|
|
96
|
+
req.body = {
|
|
97
|
+
DATA_TYPE: "access",
|
|
98
|
+
principal_type: "identity",
|
|
99
|
+
principal: id,
|
|
100
|
+
path:,
|
|
101
|
+
permissions: "rw",
|
|
102
|
+
notify_email: "#{user_id}@stanford.edu"
|
|
103
|
+
}.to_json
|
|
104
|
+
req.headers["Content-Type"] = "application/json"
|
|
105
|
+
end
|
|
106
|
+
UnexpectedResponse.call(response) unless response.success?
|
|
107
|
+
|
|
108
|
+
response
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def objects
|
|
112
|
+
# List files at an endpoint https://docs.globus.org/api/transfer/file_operations/#list_directory_contents
|
|
113
|
+
response = connection.get("#{endpoint}/ls")
|
|
114
|
+
UnexpectedResponse.call(response) unless response.success?
|
|
115
|
+
JSON.parse(response.body)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Globus
|
|
4
|
+
class Client
|
|
5
|
+
# Lookup of a Globus identity ID
|
|
6
|
+
class Identity
|
|
7
|
+
def initialize(config)
|
|
8
|
+
@config = config
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def get_identity_id(sunetid)
|
|
12
|
+
@email = "#{sunetid}@stanford.edu"
|
|
13
|
+
|
|
14
|
+
response = lookup_identity
|
|
15
|
+
UnexpectedResponse.call(response) unless response.success?
|
|
16
|
+
|
|
17
|
+
data = JSON.parse(response.body)
|
|
18
|
+
extract_id(data)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
attr_reader :config
|
|
24
|
+
|
|
25
|
+
def connection
|
|
26
|
+
Faraday.new(url: config.auth_url)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def lookup_identity
|
|
30
|
+
id_endpoint = "/v2/api/identities"
|
|
31
|
+
connection.get(id_endpoint) do |req|
|
|
32
|
+
req.params["usernames"] = @email
|
|
33
|
+
req.headers["Authorization"] = "Bearer #{config.token}"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def extract_id(data)
|
|
38
|
+
identities = data["identities"]
|
|
39
|
+
# Select identity with "used" or "private" status
|
|
40
|
+
matching_users = identities.select { |id| id["username"] == @email }
|
|
41
|
+
active_users = matching_users.select { |user| (user["status"] == "used" || user["status"] == "private") }
|
|
42
|
+
raise "No matching active Globus user found for #{@email}." if active_users.empty?
|
|
43
|
+
|
|
44
|
+
active_users.first["id"]
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Globus
|
|
4
|
+
class Client
|
|
5
|
+
# Handles unexpected responses when communicating with Globus
|
|
6
|
+
class UnexpectedResponse
|
|
7
|
+
# Error raised when the Globus Auth or Transfer API returns a 400 error
|
|
8
|
+
class BadRequestError < StandardError; end
|
|
9
|
+
|
|
10
|
+
# Error raised by the Globus Auth API returns a 401 Unauthorized
|
|
11
|
+
class UnauthorizedError < StandardError; end
|
|
12
|
+
|
|
13
|
+
# Error raised when the Globus Auth or Transfer API returns a 403 Forbidden
|
|
14
|
+
class ForbiddenError < StandardError; end
|
|
15
|
+
|
|
16
|
+
# Error raised when the Globus Auth or Transfer API returns a 404 NotFound
|
|
17
|
+
class ResourceNotFound < StandardError; end
|
|
18
|
+
|
|
19
|
+
# Error raised when the Globus Transfer API returns a 502 Bad Gateway
|
|
20
|
+
class EndpointError < StandardError; end
|
|
21
|
+
|
|
22
|
+
# Error raised when the remote server returns a 503 Bad Gateway
|
|
23
|
+
class ServiceUnavailable < StandardError; end
|
|
24
|
+
|
|
25
|
+
# @param [Faraday::Response] response
|
|
26
|
+
# https://docs.globus.org/api/transfer/file_operations/#common_errors
|
|
27
|
+
# https://docs.globus.org/api/transfer/file_operations/#errors
|
|
28
|
+
# https://docs.globus.org/api/transfer/acl/#common_errors
|
|
29
|
+
# https://docs.globus.org/api/auth/reference/
|
|
30
|
+
def self.call(response)
|
|
31
|
+
case response.status
|
|
32
|
+
when 400
|
|
33
|
+
raise BadRequestError, "Invalid path or another error with the request: #{response.body}"
|
|
34
|
+
when 401
|
|
35
|
+
raise UnauthorizedError, "There was a problem with the access token: #{response.body} "
|
|
36
|
+
when 403
|
|
37
|
+
raise ForbiddenError, "The operation requires privileges which the client does not have: #{response.body}"
|
|
38
|
+
when 404
|
|
39
|
+
raise ResourceNotFound, "Endpoint ID not found or resource does not exist: #{response.body}"
|
|
40
|
+
when 502
|
|
41
|
+
raise EndpointError, "Other error with endpoint: #{response.body}"
|
|
42
|
+
when 503
|
|
43
|
+
raise ServiceUnavailable, "The service is down for maintenance."
|
|
44
|
+
else
|
|
45
|
+
raise StandardError, "Unexpected response: #{response.status} #{response.body}"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/core_ext/module/delegation"
|
|
4
|
+
require "faraday"
|
|
5
|
+
require "ostruct"
|
|
6
|
+
require "singleton"
|
|
7
|
+
require "zeitwerk"
|
|
8
|
+
|
|
9
|
+
# Load the gem's internal dependencies
|
|
10
|
+
loader = Zeitwerk::Loader.new
|
|
11
|
+
loader.inflector = Zeitwerk::GemInflector.new(__FILE__)
|
|
12
|
+
loader.push_dir(File.absolute_path("#{__FILE__}/../.."))
|
|
13
|
+
loader.setup
|
|
14
|
+
|
|
15
|
+
module Globus
|
|
16
|
+
# Client for interacting with the Globus API
|
|
17
|
+
class Client
|
|
18
|
+
include Singleton
|
|
19
|
+
|
|
20
|
+
class << self
|
|
21
|
+
# @param client_id [String] the client identifier registered with Globus
|
|
22
|
+
# @param client_secret [String] the client secret to authenticate with Globus
|
|
23
|
+
# @param uploads_directory [String] where to upload files
|
|
24
|
+
# @param transfer_endpoint_id [String] the transfer API endpoint ID supplied by Globus
|
|
25
|
+
# @param transfer_url [String] the transfer API URL
|
|
26
|
+
# @param auth_url [String] the authentication API URL
|
|
27
|
+
def configure(client_id:, client_secret:, uploads_directory:, transfer_endpoint_id:, transfer_url: default_transfer_url, auth_url: default_auth_url)
|
|
28
|
+
instance.config = OpenStruct.new(
|
|
29
|
+
token: Globus::Client::Authenticator.token(client_id, client_secret, auth_url),
|
|
30
|
+
uploads_directory:,
|
|
31
|
+
transfer_endpoint_id:,
|
|
32
|
+
transfer_url:,
|
|
33
|
+
auth_url:
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
self
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
delegate :mkdir, :config, to: :instance
|
|
40
|
+
|
|
41
|
+
def default_transfer_url
|
|
42
|
+
"https://transfer.api.globusonline.org"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def default_auth_url
|
|
46
|
+
"https://auth.globus.org"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
attr_accessor :config
|
|
51
|
+
|
|
52
|
+
def mkdir(...)
|
|
53
|
+
endpoint = Globus::Client::Endpoint.new(config, ...)
|
|
54
|
+
endpoint.mkdir
|
|
55
|
+
endpoint.set_permissions
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: globus_client
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Aaron Collier
|
|
8
|
-
|
|
8
|
+
- Laura Wrubel
|
|
9
|
+
- Mike Giarlo
|
|
10
|
+
autorequire:
|
|
9
11
|
bindir: exe
|
|
10
12
|
cert_chain: []
|
|
11
|
-
date: 2022-11-
|
|
13
|
+
date: 2022-11-21 00:00:00.000000000 Z
|
|
12
14
|
dependencies:
|
|
13
15
|
- !ruby/object:Gem::Dependency
|
|
14
16
|
name: activesupport
|
|
@@ -31,7 +33,7 @@ dependencies:
|
|
|
31
33
|
- !ruby/object:Gem::Version
|
|
32
34
|
version: '8'
|
|
33
35
|
- !ruby/object:Gem::Dependency
|
|
34
|
-
name:
|
|
36
|
+
name: faraday
|
|
35
37
|
requirement: !ruby/object:Gem::Requirement
|
|
36
38
|
requirements:
|
|
37
39
|
- - ">="
|
|
@@ -45,7 +47,7 @@ dependencies:
|
|
|
45
47
|
- !ruby/object:Gem::Version
|
|
46
48
|
version: '0'
|
|
47
49
|
- !ruby/object:Gem::Dependency
|
|
48
|
-
name:
|
|
50
|
+
name: zeitwerk
|
|
49
51
|
requirement: !ruby/object:Gem::Requirement
|
|
50
52
|
requirements:
|
|
51
53
|
- - ">="
|
|
@@ -87,19 +89,19 @@ dependencies:
|
|
|
87
89
|
- !ruby/object:Gem::Version
|
|
88
90
|
version: '3.0'
|
|
89
91
|
- !ruby/object:Gem::Dependency
|
|
90
|
-
name:
|
|
92
|
+
name: standard
|
|
91
93
|
requirement: !ruby/object:Gem::Requirement
|
|
92
94
|
requirements:
|
|
93
|
-
- - "
|
|
95
|
+
- - ">="
|
|
94
96
|
- !ruby/object:Gem::Version
|
|
95
|
-
version: '
|
|
97
|
+
version: '0'
|
|
96
98
|
type: :development
|
|
97
99
|
prerelease: false
|
|
98
100
|
version_requirements: !ruby/object:Gem::Requirement
|
|
99
101
|
requirements:
|
|
100
|
-
- - "
|
|
102
|
+
- - ">="
|
|
101
103
|
- !ruby/object:Gem::Version
|
|
102
|
-
version: '
|
|
104
|
+
version: '0'
|
|
103
105
|
- !ruby/object:Gem::Dependency
|
|
104
106
|
name: rubocop-rspec
|
|
105
107
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -145,10 +147,31 @@ dependencies:
|
|
|
145
147
|
description: This provides API interaction with the Globus API
|
|
146
148
|
email:
|
|
147
149
|
- aaron.collier@stanford.edu
|
|
150
|
+
- lwrubel@stanford.edu
|
|
151
|
+
- mjgiarlo@stanford.edu
|
|
148
152
|
executables: []
|
|
149
153
|
extensions: []
|
|
150
154
|
extra_rdoc_files: []
|
|
151
|
-
files:
|
|
155
|
+
files:
|
|
156
|
+
- ".autoupdate/postupdate"
|
|
157
|
+
- ".rspec"
|
|
158
|
+
- ".rubocop.yml"
|
|
159
|
+
- ".rubocop/custom.yml"
|
|
160
|
+
- ".rubocop_todo.yml"
|
|
161
|
+
- ".standard.yml"
|
|
162
|
+
- Gemfile
|
|
163
|
+
- Gemfile.lock
|
|
164
|
+
- LICENSE
|
|
165
|
+
- README.md
|
|
166
|
+
- Rakefile
|
|
167
|
+
- globus_client.gemspec
|
|
168
|
+
- lib/globus/client.rb
|
|
169
|
+
- lib/globus/client/authenticator.rb
|
|
170
|
+
- lib/globus/client/endpoint.rb
|
|
171
|
+
- lib/globus/client/identity.rb
|
|
172
|
+
- lib/globus/client/unexpected_response.rb
|
|
173
|
+
- lib/globus/client/version.rb
|
|
174
|
+
- sig/globus_client.rbs
|
|
152
175
|
homepage: https://github.com/sul-dlss/globus_client
|
|
153
176
|
licenses: []
|
|
154
177
|
metadata:
|
|
@@ -156,7 +179,7 @@ metadata:
|
|
|
156
179
|
source_code_uri: https://github.com/sul-dlss/globus_client
|
|
157
180
|
changelog_uri: https://github.com/sul-dlss/globus_client/releases
|
|
158
181
|
rubygems_mfa_required: 'true'
|
|
159
|
-
post_install_message:
|
|
182
|
+
post_install_message:
|
|
160
183
|
rdoc_options: []
|
|
161
184
|
require_paths:
|
|
162
185
|
- lib
|
|
@@ -171,8 +194,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
171
194
|
- !ruby/object:Gem::Version
|
|
172
195
|
version: '0'
|
|
173
196
|
requirements: []
|
|
174
|
-
rubygems_version: 3.3.
|
|
175
|
-
signing_key:
|
|
197
|
+
rubygems_version: 3.3.7
|
|
198
|
+
signing_key:
|
|
176
199
|
specification_version: 4
|
|
177
200
|
summary: Interface for interacting with the Globus API.
|
|
178
201
|
test_files: []
|