globus_client 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/globus_client.svg)](https://badge.fury.io/rb/globus_client)
|
2
|
+
[![CircleCI](https://circleci.com/gh/sul-dlss/globus_client.svg?style=svg)](https://circleci.com/gh/sul-dlss/globus_client)
|
3
|
+
[![Code Climate](https://codeclimate.com/github/sul-dlss/globus_client/badges/gpa.svg)](https://codeclimate.com/github/sul-dlss/globus_client)
|
4
|
+
[![Code Climate Test Coverage](https://api.codeclimate.com/v1/badges/8e6bf1a5d3fc86a6fbd0/test_coverage)](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: []
|