globus_client 0.12.1 → 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +21 -12
- data/.rubocop_todo.yml +33 -0
- data/Gemfile +10 -2
- data/Gemfile.lock +19 -22
- data/Rakefile +3 -3
- data/api_test.rb +20 -18
- data/globus_client.gemspec +19 -26
- data/lib/globus_client/authenticator.rb +7 -5
- data/lib/globus_client/endpoint.rb +71 -108
- data/lib/globus_client/identity.rb +11 -27
- data/lib/globus_client/unexpected_response.rb +4 -4
- data/lib/globus_client/version.rb +1 -1
- data/lib/globus_client.rb +161 -50
- metadata +4 -91
- data/.autoupdate/postupdate +0 -19
- data/.rubocop/custom.yml +0 -80
- data/.standard.yml +0 -1
- data/lib/globus_client/token_wrapper.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bae9be2599dca47133561cc9f50d02118d633e973f973619bf642a890d266d7d
|
4
|
+
data.tar.gz: 032a19fb8edf3b11740dd487415a98f14522daac3ae4966302376b902c787e01
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f66705c5adbda9f69f8ab99b8c02ad8b16dafcf4760b0918f63191874066691c1c46211588a197629206f6e53f72b1893748bcac04594db44afeaa1f3a40706a
|
7
|
+
data.tar.gz: f901d249a7b112efb537e88387aa6833759f2ec6f801277d4970a0830d7761abfe64e8a4163337ce252e2b9beb4e46410757832ecc300f649d8e21a5a6381669
|
data/.rubocop.yml
CHANGED
@@ -1,18 +1,27 @@
|
|
1
|
-
|
2
|
-
merge:
|
3
|
-
- Exclude
|
1
|
+
inherit_from: .rubocop_todo.yml
|
4
2
|
|
5
3
|
require:
|
6
|
-
- standard
|
7
|
-
- standard-custom
|
8
|
-
- standard-performance
|
9
4
|
- rubocop-performance
|
10
5
|
- rubocop-rspec
|
11
6
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
7
|
+
AllCops:
|
8
|
+
TargetRubyVersion: 3.1
|
9
|
+
DisplayCopNames: true
|
10
|
+
SuggestExtensions: false
|
11
|
+
NewCops: disable
|
12
|
+
Exclude:
|
13
|
+
- bin/**
|
14
|
+
- vendor/bundle/**/*
|
16
15
|
|
17
|
-
|
18
|
-
|
16
|
+
# Per team developer playbook
|
17
|
+
RSpec/MultipleMemoizedHelpers:
|
18
|
+
Enabled: false
|
19
|
+
|
20
|
+
# Layout
|
21
|
+
Layout/LineLength:
|
22
|
+
Max: 150
|
23
|
+
|
24
|
+
# Naming
|
25
|
+
Naming/PredicateName:
|
26
|
+
NamePrefix:
|
27
|
+
- has
|
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config --auto-gen-only-exclude`
|
3
|
+
# on 2023-09-29 23:59:42 UTC using RuboCop version 1.56.3.
|
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.
|
8
|
+
|
9
|
+
# Offense count: 1
|
10
|
+
Lint/StructNewOverride:
|
11
|
+
Exclude:
|
12
|
+
- 'lib/globus_client/endpoint.rb'
|
13
|
+
|
14
|
+
# Offense count: 4
|
15
|
+
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
|
16
|
+
Metrics/AbcSize:
|
17
|
+
Exclude:
|
18
|
+
- 'lib/globus_client.rb'
|
19
|
+
- 'lib/globus_client/endpoint.rb'
|
20
|
+
- 'lib/globus_client/unexpected_response.rb'
|
21
|
+
|
22
|
+
# Offense count: 1
|
23
|
+
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
24
|
+
Metrics/CyclomaticComplexity:
|
25
|
+
Exclude:
|
26
|
+
- 'lib/globus_client/endpoint.rb'
|
27
|
+
|
28
|
+
# Offense count: 3
|
29
|
+
# Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns.
|
30
|
+
Metrics/MethodLength:
|
31
|
+
Exclude:
|
32
|
+
- 'lib/globus_client/endpoint.rb'
|
33
|
+
- 'lib/globus_client/unexpected_response.rb'
|
data/Gemfile
CHANGED
@@ -1,8 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
source
|
3
|
+
source 'https://rubygems.org'
|
4
4
|
|
5
5
|
# Specify your gem's dependencies in globus_client.gemspec
|
6
6
|
gemspec
|
7
7
|
|
8
|
-
|
8
|
+
group :deployment do
|
9
|
+
gem 'byebug'
|
10
|
+
gem 'rake', '~> 13.0'
|
11
|
+
gem 'rspec', '~> 3.0'
|
12
|
+
gem 'rubocop-performance'
|
13
|
+
gem 'rubocop-rspec'
|
14
|
+
gem 'simplecov'
|
15
|
+
gem 'webmock'
|
16
|
+
end
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
globus_client (0.
|
4
|
+
globus_client (0.14.0)
|
5
5
|
activesupport (>= 4.2, < 8)
|
6
6
|
faraday
|
7
7
|
faraday-retry
|
@@ -10,21 +10,30 @@ PATH
|
|
10
10
|
GEM
|
11
11
|
remote: https://rubygems.org/
|
12
12
|
specs:
|
13
|
-
activesupport (7.0
|
13
|
+
activesupport (7.1.0)
|
14
|
+
base64
|
15
|
+
bigdecimal
|
14
16
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
17
|
+
connection_pool (>= 2.2.5)
|
18
|
+
drb
|
15
19
|
i18n (>= 1.6, < 2)
|
16
20
|
minitest (>= 5.1)
|
21
|
+
mutex_m
|
17
22
|
tzinfo (~> 2.0)
|
18
23
|
addressable (2.8.5)
|
19
24
|
public_suffix (>= 2.0.2, < 6.0)
|
20
25
|
ast (2.4.2)
|
21
26
|
base64 (0.1.1)
|
27
|
+
bigdecimal (3.1.4)
|
22
28
|
byebug (11.1.3)
|
23
29
|
concurrent-ruby (1.2.2)
|
30
|
+
connection_pool (2.4.1)
|
24
31
|
crack (0.4.5)
|
25
32
|
rexml
|
26
33
|
diff-lcs (1.5.0)
|
27
34
|
docile (1.4.0)
|
35
|
+
drb (2.1.1)
|
36
|
+
ruby2_keywords
|
28
37
|
faraday (2.7.11)
|
29
38
|
base64
|
30
39
|
faraday-net_http (>= 2.0, < 3.1)
|
@@ -37,10 +46,10 @@ GEM
|
|
37
46
|
concurrent-ruby (~> 1.0)
|
38
47
|
json (2.6.3)
|
39
48
|
language_server-protocol (3.17.0.3)
|
40
|
-
lint_roller (1.1.0)
|
41
49
|
minitest (5.20.0)
|
50
|
+
mutex_m (0.1.2)
|
42
51
|
parallel (1.23.0)
|
43
|
-
parser (3.2.2.
|
52
|
+
parser (3.2.2.4)
|
44
53
|
ast (~> 2.4.1)
|
45
54
|
racc
|
46
55
|
public_suffix (5.0.3)
|
@@ -62,7 +71,7 @@ GEM
|
|
62
71
|
diff-lcs (>= 1.2.0, < 2.0)
|
63
72
|
rspec-support (~> 3.12.0)
|
64
73
|
rspec-support (3.12.1)
|
65
|
-
rubocop (1.56.
|
74
|
+
rubocop (1.56.4)
|
66
75
|
base64 (~> 0.1.1)
|
67
76
|
json (~> 2.3)
|
68
77
|
language_server-protocol (>= 3.17.0)
|
@@ -76,14 +85,14 @@ GEM
|
|
76
85
|
unicode-display_width (>= 2.4.0, < 3.0)
|
77
86
|
rubocop-ast (1.29.0)
|
78
87
|
parser (>= 3.2.1.0)
|
79
|
-
rubocop-capybara (2.
|
88
|
+
rubocop-capybara (2.19.0)
|
80
89
|
rubocop (~> 1.41)
|
81
90
|
rubocop-factory_bot (2.24.0)
|
82
91
|
rubocop (~> 1.33)
|
83
92
|
rubocop-performance (1.19.1)
|
84
93
|
rubocop (>= 1.7.0, < 2.0)
|
85
94
|
rubocop-ast (>= 0.4.0)
|
86
|
-
rubocop-rspec (2.24.
|
95
|
+
rubocop-rspec (2.24.1)
|
87
96
|
rubocop (~> 1.33)
|
88
97
|
rubocop-capybara (~> 2.17)
|
89
98
|
rubocop-factory_bot (~> 2.22)
|
@@ -95,26 +104,14 @@ GEM
|
|
95
104
|
simplecov_json_formatter (~> 0.1)
|
96
105
|
simplecov-html (0.12.3)
|
97
106
|
simplecov_json_formatter (0.1.4)
|
98
|
-
standard (1.31.1)
|
99
|
-
language_server-protocol (~> 3.17.0.2)
|
100
|
-
lint_roller (~> 1.0)
|
101
|
-
rubocop (~> 1.56.2)
|
102
|
-
standard-custom (~> 1.0.0)
|
103
|
-
standard-performance (~> 1.2)
|
104
|
-
standard-custom (1.0.2)
|
105
|
-
lint_roller (~> 1.0)
|
106
|
-
rubocop (~> 1.50)
|
107
|
-
standard-performance (1.2.0)
|
108
|
-
lint_roller (~> 1.1)
|
109
|
-
rubocop-performance (~> 1.19.0)
|
110
107
|
tzinfo (2.0.6)
|
111
108
|
concurrent-ruby (~> 1.0)
|
112
|
-
unicode-display_width (2.
|
109
|
+
unicode-display_width (2.5.0)
|
113
110
|
webmock (3.19.1)
|
114
111
|
addressable (>= 2.8.0)
|
115
112
|
crack (>= 0.3.2)
|
116
113
|
hashdiff (>= 0.4.0, < 2.0.0)
|
117
|
-
zeitwerk (2.6.
|
114
|
+
zeitwerk (2.6.12)
|
118
115
|
|
119
116
|
PLATFORMS
|
120
117
|
x86_64-darwin-19
|
@@ -128,9 +125,9 @@ DEPENDENCIES
|
|
128
125
|
globus_client!
|
129
126
|
rake (~> 13.0)
|
130
127
|
rspec (~> 3.0)
|
128
|
+
rubocop-performance
|
131
129
|
rubocop-rspec
|
132
130
|
simplecov
|
133
|
-
standard
|
134
131
|
webmock
|
135
132
|
|
136
133
|
BUNDLED WITH
|
data/Rakefile
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
require 'rubocop/rake_task'
|
6
6
|
|
7
7
|
RSpec::Core::RakeTask.new(:spec)
|
8
8
|
RuboCop::RakeTask.new
|
data/api_test.rb
CHANGED
@@ -1,40 +1,41 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
4
|
+
require 'benchmark'
|
5
|
+
require 'bundler/setup'
|
6
|
+
require 'globus_client'
|
7
7
|
|
8
|
-
Benchmark.bm(20) do |benchmark|
|
8
|
+
Benchmark.bm(20) do |benchmark| # rubocop:disable Metrics/BlockLength
|
9
9
|
user_id, path = *ARGV
|
10
10
|
|
11
|
-
benchmark.report(
|
11
|
+
benchmark.report('Configure:') do
|
12
12
|
GlobusClient.configure(
|
13
|
-
client_id: ENV
|
14
|
-
client_secret: ENV
|
15
|
-
uploads_directory: ENV
|
16
|
-
transfer_endpoint_id: ENV
|
13
|
+
client_id: ENV.fetch('GLOBUS_CLIENT_ID', nil),
|
14
|
+
client_secret: ENV.fetch('GLOBUS_CLIENT_SECRET', nil),
|
15
|
+
uploads_directory: ENV.fetch('GLOBUS_UPLOADS_DIRECTORY', nil),
|
16
|
+
transfer_endpoint_id: ENV.fetch('GLOBUS_ENDPOINT', nil)
|
17
17
|
)
|
18
18
|
end
|
19
19
|
|
20
|
-
benchmark.report(
|
20
|
+
benchmark.report('mkdir:') do
|
21
21
|
GlobusClient.mkdir(user_id:, path:)
|
22
22
|
end
|
23
23
|
|
24
|
-
benchmark.report(
|
24
|
+
benchmark.report('user_valid?:') do
|
25
25
|
@user_exists = GlobusClient.user_valid?(user_id)
|
26
26
|
end
|
27
27
|
|
28
|
-
benchmark.report(
|
28
|
+
benchmark.report('before_perms:') do
|
29
29
|
# Not part of the public API but this allows us to test access changes
|
30
|
-
@before_permissions = GlobusClient::Endpoint.new(GlobusClient.
|
30
|
+
@before_permissions = GlobusClient::Endpoint.new(GlobusClient.instance, user_id:,
|
31
|
+
path:).send(:access_rule)['permissions']
|
31
32
|
end
|
32
33
|
|
33
|
-
benchmark.report(
|
34
|
+
benchmark.report('has_files?:') do
|
34
35
|
@has_files = GlobusClient.has_files?(user_id:, path:)
|
35
36
|
end
|
36
37
|
|
37
|
-
benchmark.report(
|
38
|
+
benchmark.report('list_files:') do
|
38
39
|
GlobusClient.list_files(user_id:, path:) do |files|
|
39
40
|
@files_count = files.count
|
40
41
|
@total_size = files.sum(&:size)
|
@@ -42,13 +43,14 @@ Benchmark.bm(20) do |benchmark|
|
|
42
43
|
end
|
43
44
|
end
|
44
45
|
|
45
|
-
benchmark.report(
|
46
|
+
benchmark.report('disallow_writes:') do
|
46
47
|
GlobusClient.disallow_writes(user_id:, path:)
|
47
48
|
end
|
48
49
|
|
49
|
-
benchmark.report(
|
50
|
+
benchmark.report('after_perms:') do
|
50
51
|
# Not part of the public API but this allows us to test access changes
|
51
|
-
@after_permissions = GlobusClient::Endpoint.new(GlobusClient.
|
52
|
+
@after_permissions = GlobusClient::Endpoint.new(GlobusClient.instance, user_id:,
|
53
|
+
path:).send(:access_rule)['permissions']
|
52
54
|
end
|
53
55
|
|
54
56
|
puts "User #{user_id} exists: #{@user_exists}"
|
data/globus_client.gemspec
CHANGED
@@ -1,24 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
lib = File.expand_path(
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
-
require
|
5
|
+
require 'globus_client/version'
|
6
6
|
|
7
7
|
Gem::Specification.new do |spec|
|
8
|
-
spec.name =
|
8
|
+
spec.name = 'globus_client'
|
9
9
|
spec.version = GlobusClient::VERSION
|
10
|
-
spec.authors = [
|
11
|
-
spec.email = [
|
10
|
+
spec.authors = ['Aaron Collier', 'Laura Wrubel', 'Mike Giarlo']
|
11
|
+
spec.email = ['aaron.collier@stanford.edu', 'lwrubel@stanford.edu', 'mjgiarlo@stanford.edu']
|
12
12
|
|
13
|
-
spec.summary =
|
14
|
-
spec.description =
|
15
|
-
spec.homepage =
|
16
|
-
spec.required_ruby_version =
|
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 = '>= 3.1.0'
|
17
17
|
|
18
|
-
spec.metadata[
|
19
|
-
spec.metadata[
|
20
|
-
spec.metadata[
|
21
|
-
spec.metadata[
|
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
22
|
|
23
23
|
# Specify which files should be added to the gem when it is released.
|
24
24
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
@@ -27,19 +27,12 @@ Gem::Specification.new do |spec|
|
|
27
27
|
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
28
28
|
end
|
29
29
|
end
|
30
|
-
spec.bindir =
|
30
|
+
spec.bindir = 'exe'
|
31
31
|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
32
|
-
spec.require_paths = [
|
32
|
+
spec.require_paths = ['lib']
|
33
33
|
|
34
|
-
spec.add_dependency
|
35
|
-
spec.add_dependency
|
36
|
-
spec.add_dependency
|
37
|
-
spec.add_dependency
|
38
|
-
|
39
|
-
spec.add_development_dependency "rake", "~> 13.0"
|
40
|
-
spec.add_development_dependency "rspec", "~> 3.0"
|
41
|
-
spec.add_development_dependency "standard"
|
42
|
-
spec.add_development_dependency "rubocop-rspec"
|
43
|
-
spec.add_development_dependency "simplecov"
|
44
|
-
spec.add_development_dependency "webmock"
|
34
|
+
spec.add_dependency 'activesupport', '>= 4.2', '< 8'
|
35
|
+
spec.add_dependency 'faraday'
|
36
|
+
spec.add_dependency 'faraday-retry'
|
37
|
+
spec.add_dependency 'zeitwerk'
|
45
38
|
end
|
@@ -15,9 +15,11 @@ class GlobusClient
|
|
15
15
|
|
16
16
|
# Request an access_token
|
17
17
|
def token
|
18
|
-
response = connection.post(
|
18
|
+
response = connection.post('/v2/oauth2/token', form_data)
|
19
19
|
|
20
|
-
|
20
|
+
UnexpectedResponse.call(response) unless response.success?
|
21
|
+
|
22
|
+
JSON.parse(response.body)['access_token']
|
21
23
|
end
|
22
24
|
|
23
25
|
private
|
@@ -32,9 +34,9 @@ class GlobusClient
|
|
32
34
|
{
|
33
35
|
client_id:,
|
34
36
|
client_secret:,
|
35
|
-
encoding:
|
36
|
-
grant_type:
|
37
|
-
scope:
|
37
|
+
encoding: 'form',
|
38
|
+
grant_type: 'client_credentials',
|
39
|
+
scope: 'urn:globus:auth:scope:transfer.api.globus.org:all'
|
38
40
|
}
|
39
41
|
end
|
40
42
|
end
|
@@ -2,18 +2,20 @@
|
|
2
2
|
|
3
3
|
class GlobusClient
|
4
4
|
# The namespace for endpoint API operations
|
5
|
-
class Endpoint
|
6
|
-
PATH_SEPARATOR =
|
5
|
+
class Endpoint # rubocop:disable Metrics/ClassLength
|
6
|
+
PATH_SEPARATOR = '/'
|
7
7
|
|
8
8
|
FileInfo = Struct.new(:name, :size)
|
9
9
|
|
10
|
-
# @param
|
10
|
+
# @param client [GlobusClient] a configured instance of the GlobusClient
|
11
11
|
# @param path [String] the path to operate on
|
12
12
|
# @param user_id [String] a Globus user ID (e.g., a @stanford.edu email address)
|
13
|
-
|
14
|
-
|
13
|
+
# @param notify_email [Boolean] indicates if we should ask Globus to send emails on access change (default: true)
|
14
|
+
def initialize(client, path:, user_id:, notify_email: true)
|
15
|
+
@client = client
|
15
16
|
@user_id = user_id
|
16
17
|
@path = path
|
18
|
+
@notify_email = notify_email
|
17
19
|
end
|
18
20
|
|
19
21
|
def has_files?
|
@@ -30,69 +32,43 @@ class GlobusClient
|
|
30
32
|
def mkdir
|
31
33
|
# transfer API does not support recursive directory creation
|
32
34
|
paths.each do |path|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
next if response.success?
|
42
|
-
|
43
|
-
# Ignore error if directory already exists
|
44
|
-
if response.status == 502
|
45
|
-
error = JSON.parse(response.body)
|
46
|
-
next if error["code"] == "ExternalError.MkdirFailed.Exists"
|
47
|
-
end
|
48
|
-
|
49
|
-
UnexpectedResponse.call(response)
|
35
|
+
client.post(
|
36
|
+
base_url: client.config.transfer_url,
|
37
|
+
path: "#{transfer_path}/mkdir",
|
38
|
+
body: { DATA_TYPE: 'mkdir', path: },
|
39
|
+
expected_response: lambda { |resp|
|
40
|
+
resp.status == 502 && JSON.parse(resp.body)['code'] == 'ExternalError.MkdirFailed.Exists'
|
41
|
+
}
|
42
|
+
)
|
50
43
|
end
|
51
44
|
end
|
52
45
|
|
53
46
|
# Assign a user read/write permissions for a directory https://docs.globus.org/api/transfer/acl/#rest_access_create
|
54
47
|
def allow_writes
|
55
|
-
access_request(permissions:
|
48
|
+
access_request(permissions: 'rw')
|
56
49
|
end
|
57
50
|
|
58
51
|
# Assign a user read-only permissions for a directory https://docs.globus.org/api/transfer/acl/#rest_access_create
|
59
52
|
def disallow_writes
|
60
|
-
update_access_request(permissions:
|
53
|
+
update_access_request(permissions: 'r')
|
61
54
|
end
|
62
55
|
|
63
56
|
# Delete the access rule https://docs.globus.org/api/transfer/acl/#delete_access_rule
|
64
57
|
def delete_access_rule
|
65
|
-
raise(StandardError, "Access rule not found for #{path}")
|
58
|
+
raise(StandardError, "Access rule not found for #{path}") unless access_rule_id
|
66
59
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
60
|
+
client.delete(
|
61
|
+
base_url: client.config.transfer_url,
|
62
|
+
path: "#{access_path}/#{access_rule_id}"
|
63
|
+
)
|
71
64
|
end
|
72
65
|
|
73
66
|
private
|
74
67
|
|
75
|
-
attr_reader :
|
76
|
-
|
77
|
-
def connection
|
78
|
-
# faraday/retry is used here to catch Faraday::ConnectionFailed exceptions
|
79
|
-
# see: https://github.com/sul-dlss/happy-heron/issues/3008
|
80
|
-
@connection ||= Faraday.new(
|
81
|
-
url: config.transfer_url,
|
82
|
-
headers: {Authorization: "Bearer #{config.token}"}
|
83
|
-
) do |faraday|
|
84
|
-
faraday.request :retry, {
|
85
|
-
max: 10,
|
86
|
-
interval: 0.05,
|
87
|
-
interval_randomness: 0.5,
|
88
|
-
backoff_factor: 2,
|
89
|
-
exceptions: Faraday::Retry::Middleware::DEFAULT_EXCEPTIONS + [Faraday::ConnectionFailed]
|
90
|
-
}
|
91
|
-
end
|
92
|
-
end
|
68
|
+
attr_reader :client, :path, :user_id, :notify_email
|
93
69
|
|
94
70
|
def globus_identity_id
|
95
|
-
Identity.new(
|
71
|
+
Identity.new(client).get_identity_id(user_id)
|
96
72
|
end
|
97
73
|
|
98
74
|
# Builds up a path from a list of path elements. E.g., input would look like:
|
@@ -102,7 +78,7 @@ class GlobusClient
|
|
102
78
|
def paths
|
103
79
|
@paths ||= path_segments.map.with_index do |_segment, index|
|
104
80
|
File
|
105
|
-
.join(config.uploads_directory, path_segments.slice(..index))
|
81
|
+
.join(client.config.uploads_directory, path_segments.slice(..index))
|
106
82
|
.concat(PATH_SEPARATOR)
|
107
83
|
end
|
108
84
|
end
|
@@ -123,24 +99,27 @@ class GlobusClient
|
|
123
99
|
# @param files [Array<FileInfo>] an array of FileInfo structs, each of which has a name and a size
|
124
100
|
# @param return_presence [Boolean] if true, return a boolean to indicate if any files at all are present, short-circuiting the recursive operation
|
125
101
|
def ls_path(filepath, files, return_presence: false)
|
126
|
-
response =
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
102
|
+
response = client.get(
|
103
|
+
base_url: client.config.transfer_url,
|
104
|
+
path: "#{transfer_path}/ls",
|
105
|
+
params: { path: filepath }
|
106
|
+
)
|
107
|
+
|
108
|
+
response['DATA']
|
109
|
+
.select { |object| object['type'] == 'file' }
|
132
110
|
.each do |file|
|
133
111
|
return true if return_presence
|
134
112
|
|
135
|
-
files << FileInfo.new("#{filepath}#{file[
|
113
|
+
files << FileInfo.new("#{filepath}#{file['name']}", file['size'])
|
136
114
|
end
|
137
|
-
|
138
|
-
|
115
|
+
|
116
|
+
response['DATA']
|
117
|
+
.select { |object| object['type'] == 'dir' }
|
139
118
|
.each do |dir|
|
140
119
|
# NOTE: This allows the recursive method to short-circuit iff ls_path
|
141
120
|
# returns true, which only happens when return_presence is true
|
142
121
|
# and the first file is found in the ls operation.
|
143
|
-
return true if ls_path("#{filepath}#{dir[
|
122
|
+
return true if ls_path("#{filepath}#{dir['name']}/", files, return_presence:) == true
|
144
123
|
end
|
145
124
|
|
146
125
|
return false if return_presence
|
@@ -149,78 +128,62 @@ class GlobusClient
|
|
149
128
|
end
|
150
129
|
|
151
130
|
def access_request(permissions:)
|
152
|
-
|
153
|
-
|
154
|
-
req.body = {
|
155
|
-
DATA_TYPE: "access",
|
156
|
-
permissions:
|
157
|
-
}.to_json
|
158
|
-
req.headers["Content-Type"] = "application/json"
|
159
|
-
end
|
131
|
+
if access_rule_id
|
132
|
+
update_access_request(permissions:)
|
160
133
|
else
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
134
|
+
body = {
|
135
|
+
DATA_TYPE: 'access',
|
136
|
+
principal_type: 'identity',
|
137
|
+
principal: globus_identity_id,
|
138
|
+
path: full_path,
|
139
|
+
permissions:
|
140
|
+
}
|
141
|
+
body[:notify_email] = user_id if notify_email
|
142
|
+
client.post(
|
143
|
+
base_url: client.config.transfer_url,
|
144
|
+
path: access_path,
|
145
|
+
body:
|
146
|
+
)
|
172
147
|
end
|
173
|
-
|
174
|
-
return true if response.success?
|
175
|
-
|
176
|
-
UnexpectedResponse.call(response)
|
177
148
|
end
|
178
149
|
|
179
150
|
def update_access_request(permissions:)
|
180
|
-
raise(StandardError, "Access rule not found for #{path}")
|
151
|
+
raise(StandardError, "Access rule not found for #{path}") unless access_rule_id
|
181
152
|
|
182
|
-
|
183
|
-
|
184
|
-
|
153
|
+
client.put(
|
154
|
+
base_url: client.config.transfer_url,
|
155
|
+
path: "#{access_path}/#{access_rule_id}",
|
156
|
+
body: {
|
157
|
+
DATA_TYPE: 'access',
|
185
158
|
permissions:
|
186
|
-
}
|
187
|
-
|
188
|
-
end
|
189
|
-
|
190
|
-
return true if response.success?
|
191
|
-
|
192
|
-
UnexpectedResponse.call(response)
|
159
|
+
}
|
160
|
+
)
|
193
161
|
end
|
194
162
|
|
195
163
|
def access_rule
|
196
|
-
response =
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
response_body = JSON.parse(response.body)
|
202
|
-
|
203
|
-
UnexpectedResponse.call(response, message: "Response is missing DATA in: #{response_body}") unless response.success? && response_body.key?("DATA")
|
164
|
+
response = client.get(
|
165
|
+
base_url: client.config.transfer_url,
|
166
|
+
path: access_list_path,
|
167
|
+
content_type: 'application/json'
|
168
|
+
)
|
204
169
|
|
205
|
-
|
206
|
-
.parse(response.body)["DATA"]
|
207
|
-
.find { |acl| acl["path"] == full_path }
|
170
|
+
response.fetch('DATA').find { |acl| acl['path'] == full_path }
|
208
171
|
end
|
209
172
|
|
210
173
|
def access_rule_id
|
211
|
-
access_rule&.fetch(
|
174
|
+
access_rule&.fetch('id')
|
212
175
|
end
|
213
176
|
|
214
177
|
def transfer_path
|
215
|
-
"/v0.10/operation/endpoint/#{config.transfer_endpoint_id}"
|
178
|
+
"/v0.10/operation/endpoint/#{client.config.transfer_endpoint_id}"
|
216
179
|
end
|
217
180
|
|
218
181
|
def access_path
|
219
|
-
"/v0.10/endpoint/#{config.transfer_endpoint_id}/access"
|
182
|
+
"/v0.10/endpoint/#{client.config.transfer_endpoint_id}/access"
|
220
183
|
end
|
221
184
|
|
222
185
|
def access_list_path
|
223
|
-
"/v0.10/endpoint/#{config.transfer_endpoint_id}/access_list"
|
186
|
+
"/v0.10/endpoint/#{client.config.transfer_endpoint_id}/access_list"
|
224
187
|
end
|
225
188
|
end
|
226
189
|
end
|
@@ -3,52 +3,36 @@
|
|
3
3
|
class GlobusClient
|
4
4
|
# Lookup of a Globus identity ID
|
5
5
|
class Identity
|
6
|
-
def initialize(
|
7
|
-
@
|
6
|
+
def initialize(client)
|
7
|
+
@client = client
|
8
8
|
end
|
9
9
|
|
10
10
|
# @param user_id [String] the username in the form of an email addresss
|
11
11
|
# @return [Hash] id and status of Globus identity
|
12
12
|
def get_identity(user_id)
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
response = client.get(
|
14
|
+
base_url: client.config.auth_url,
|
15
|
+
path: '/v2/api/identities',
|
16
|
+
params: { usernames: user_id }
|
17
|
+
)
|
16
18
|
|
17
|
-
|
18
|
-
extract_id(data)
|
19
|
+
response['identities'].find { |id| id['username'] == user_id }
|
19
20
|
end
|
20
21
|
|
21
22
|
# @param user_id [String] the username in the form of an email addresss
|
22
23
|
# @return [Boolean] whether the account has a valid status
|
23
24
|
def valid?(user_id)
|
24
|
-
[
|
25
|
+
%w[used private unused].include?(get_identity(user_id)['status'])
|
25
26
|
end
|
26
27
|
|
27
28
|
# @param user_id [String] the username in the form of an email addresss
|
28
29
|
# @return [String] UUID for Globus identity
|
29
30
|
def get_identity_id(user_id)
|
30
|
-
get_identity(user_id)[
|
31
|
+
get_identity(user_id)['id']
|
31
32
|
end
|
32
33
|
|
33
34
|
private
|
34
35
|
|
35
|
-
attr_reader :
|
36
|
-
|
37
|
-
def connection
|
38
|
-
Faraday.new(url: config.auth_url)
|
39
|
-
end
|
40
|
-
|
41
|
-
def lookup_identity
|
42
|
-
id_endpoint = "/v2/api/identities"
|
43
|
-
connection.get(id_endpoint) do |req|
|
44
|
-
req.params["usernames"] = @email
|
45
|
-
req.headers["Authorization"] = "Bearer #{config.token}"
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def extract_id(data)
|
50
|
-
identities = data["identities"]
|
51
|
-
identities.find { |id| id["username"] == @email }
|
52
|
-
end
|
36
|
+
attr_reader :client
|
53
37
|
end
|
54
38
|
end
|
@@ -26,7 +26,7 @@ class GlobusClient
|
|
26
26
|
# https://docs.globus.org/api/transfer/file_operations/#errors
|
27
27
|
# https://docs.globus.org/api/transfer/acl/#common_errors
|
28
28
|
# https://docs.globus.org/api/auth/reference/
|
29
|
-
def self.call(response
|
29
|
+
def self.call(response)
|
30
30
|
case response.status
|
31
31
|
when 400
|
32
32
|
raise BadRequestError, "Invalid path or another error with the request: #{response.body}"
|
@@ -37,11 +37,11 @@ class GlobusClient
|
|
37
37
|
when 404
|
38
38
|
raise ResourceNotFound, "Endpoint ID not found or resource does not exist: #{response.body}"
|
39
39
|
when 502
|
40
|
-
raise EndpointError, "Other error with endpoint: #{response.status} #{response.body}.
|
40
|
+
raise EndpointError, "Other error with endpoint: #{response.status} #{response.body}."
|
41
41
|
when 503
|
42
|
-
raise ServiceUnavailable,
|
42
|
+
raise ServiceUnavailable, 'The service is down for maintenance.'
|
43
43
|
else
|
44
|
-
raise StandardError, "Unexpected response: #{response.status} #{response.body}.
|
44
|
+
raise StandardError, "Unexpected response: #{response.status} #{response.body}."
|
45
45
|
end
|
46
46
|
end
|
47
47
|
end
|
data/lib/globus_client.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
3
|
+
require 'active_support/core_ext/module/delegation'
|
4
|
+
require 'active_support/core_ext/object/blank'
|
5
|
+
require 'faraday'
|
6
|
+
require 'faraday/retry'
|
7
|
+
require 'singleton'
|
8
|
+
require 'zeitwerk'
|
9
9
|
|
10
10
|
# Load the gem's internal dependencies: use Zeitwerk instead of needing to manually require classes
|
11
11
|
Zeitwerk::Loader.for_gem.setup
|
12
12
|
|
13
13
|
# Client for interacting with the Globus API
|
14
|
-
class GlobusClient
|
14
|
+
class GlobusClient # rubocop:disable Metrics/ClassLength
|
15
15
|
include Singleton
|
16
16
|
|
17
17
|
class << self
|
@@ -21,19 +21,21 @@ class GlobusClient
|
|
21
21
|
# @param transfer_endpoint_id [String] the transfer API endpoint ID supplied by Globus
|
22
22
|
# @param transfer_url [String] the transfer API URL
|
23
23
|
# @param auth_url [String] the authentication API URL
|
24
|
-
|
25
|
-
|
24
|
+
# rubocop:disable Metrics/ParameterLists
|
25
|
+
def configure(client_id:, client_secret:, uploads_directory:, transfer_endpoint_id:,
|
26
|
+
transfer_url: default_transfer_url, auth_url: default_auth_url)
|
27
|
+
instance.config = Config.new(
|
26
28
|
# For the initial token, use a dummy value to avoid hitting any APIs
|
27
|
-
# during configuration, allowing
|
28
|
-
# token refreshing. Why not immediately get a valid token? Our apps
|
29
|
+
# during configuration, allowing `with_token_refresh_when_unauthorized` to handle
|
30
|
+
# auto-magic token refreshing. Why not immediately get a valid token? Our apps
|
29
31
|
# commonly invoke client `.configure` methods in the initializer in all
|
30
32
|
# application environments, even those that are never expected to
|
31
33
|
# connect to production APIs, such as local development machines.
|
32
34
|
#
|
33
35
|
# NOTE: `nil` and blank string cannot be used as dummy values here as
|
34
36
|
# they lead to a malformed request to be sent, which triggers an
|
35
|
-
# exception not rescued by `
|
36
|
-
token:
|
37
|
+
# exception not rescued by `with_token_refresh_when_unauthorized`
|
38
|
+
token: 'a temporary dummy token to avoid hitting the API before it is needed',
|
37
39
|
client_id:,
|
38
40
|
client_secret:,
|
39
41
|
uploads_directory:,
|
@@ -44,85 +46,194 @@ class GlobusClient
|
|
44
46
|
|
45
47
|
self
|
46
48
|
end
|
49
|
+
# rubocop:enable Metrics/ParameterLists
|
47
50
|
|
48
51
|
delegate :config, :disallow_writes, :delete_access_rule, :file_count, :list_files, :mkdir, :total_size,
|
49
|
-
|
52
|
+
:user_valid?, :get_filenames, :has_files?, :delete, :get, :post, :put, to: :instance
|
50
53
|
|
51
54
|
def default_transfer_url
|
52
|
-
|
55
|
+
'https://transfer.api.globusonline.org'
|
53
56
|
end
|
54
57
|
|
55
58
|
def default_auth_url
|
56
|
-
|
59
|
+
'https://auth.globus.org'
|
57
60
|
end
|
58
61
|
end
|
59
62
|
|
60
63
|
attr_accessor :config
|
61
64
|
|
65
|
+
# Send an authenticated GET request
|
66
|
+
# @param base_url [String] the base URL of the Globus API
|
67
|
+
# @param path [String] the path to the Globus API request
|
68
|
+
# @param params [Hash] params to get to the API
|
69
|
+
def get(base_url:, path:, params: {}, content_type: nil)
|
70
|
+
response = with_token_refresh_when_unauthorized do
|
71
|
+
connection(base_url).get(path, params) do |request|
|
72
|
+
request.headers['Authorization'] = "Bearer #{config.token}"
|
73
|
+
request.headers['Content-Type'] = content_type if content_type
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
UnexpectedResponse.call(response) unless response.success?
|
78
|
+
|
79
|
+
return nil if response.body.blank?
|
80
|
+
|
81
|
+
JSON.parse(response.body)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Send an authenticated POST request
|
85
|
+
# @param base_url [String] the base URL of the Globus API
|
86
|
+
# @param path [String] the path to the Globus API request
|
87
|
+
# @param body [String] the body of the Globus API request
|
88
|
+
# @param expected_response [#call] an expected response handler to allow short-circuiting the unexpected response
|
89
|
+
def post(base_url:, path:, body:, expected_response: ->(_resp) { false })
|
90
|
+
response = with_token_refresh_when_unauthorized do
|
91
|
+
connection(base_url).post(path) do |request|
|
92
|
+
request.headers['Authorization'] = "Bearer #{config.token}"
|
93
|
+
request.headers['Content-Type'] = 'application/json'
|
94
|
+
request.body = body.to_json
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
UnexpectedResponse.call(response) unless response.success? || expected_response.call(response)
|
99
|
+
|
100
|
+
return nil if response.body.blank?
|
101
|
+
|
102
|
+
JSON.parse(response.body)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Send an authenticated PUT request
|
106
|
+
# @param base_url [String] the base URL of the Globus API
|
107
|
+
# @param path [String] the path to the Globus API request
|
108
|
+
# @param body [String] the body of the Globus API request
|
109
|
+
def put(base_url:, path:, body:)
|
110
|
+
response = with_token_refresh_when_unauthorized do
|
111
|
+
connection(base_url).put(path) do |request|
|
112
|
+
request.headers['Authorization'] = "Bearer #{config.token}"
|
113
|
+
request.headers['Content-Type'] = 'application/json'
|
114
|
+
request.body = body.to_json
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
UnexpectedResponse.call(response) unless response.success?
|
119
|
+
|
120
|
+
return nil if response.body.blank?
|
121
|
+
|
122
|
+
JSON.parse(response.body)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Send an authenticated DELETE request
|
126
|
+
# @param base_url [String] the base URL of the Globus API
|
127
|
+
# @param path [String] the path to the Globus API request
|
128
|
+
def delete(base_url:, path:)
|
129
|
+
response = with_token_refresh_when_unauthorized do
|
130
|
+
connection(base_url).delete(path) do |request|
|
131
|
+
request.headers['Authorization'] = "Bearer #{config.token}"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
UnexpectedResponse.call(response) unless response.success?
|
136
|
+
|
137
|
+
return nil if response.body.blank?
|
138
|
+
|
139
|
+
JSON.parse(response.body)
|
140
|
+
end
|
141
|
+
|
62
142
|
def mkdir(...)
|
63
|
-
|
64
|
-
endpoint = Endpoint.new(config, ...)
|
143
|
+
Endpoint.new(self, ...).tap do |endpoint|
|
65
144
|
endpoint.mkdir
|
66
145
|
endpoint.allow_writes
|
67
146
|
end
|
68
147
|
end
|
69
148
|
|
70
149
|
def disallow_writes(...)
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
end
|
150
|
+
Endpoint
|
151
|
+
.new(self, ...)
|
152
|
+
.disallow_writes
|
75
153
|
end
|
76
154
|
|
77
155
|
def delete_access_rule(...)
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
end
|
156
|
+
Endpoint
|
157
|
+
.new(self, ...)
|
158
|
+
.delete_access_rule
|
82
159
|
end
|
83
160
|
|
84
161
|
# NOTE: Can't use the `...` (argument forwarding) operator here because we
|
85
162
|
# want to route the keyword args to `Endpoint#new` and the block arg to
|
86
163
|
# `Endpoint#list_files`
|
87
|
-
def list_files(**keywords, &
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
end
|
164
|
+
def list_files(**keywords, &)
|
165
|
+
Endpoint
|
166
|
+
.new(self, **keywords)
|
167
|
+
.list_files(&)
|
92
168
|
end
|
93
169
|
|
94
170
|
def file_count(...)
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
end
|
171
|
+
Endpoint
|
172
|
+
.new(self, ...)
|
173
|
+
.list_files { |files| return files.count }
|
99
174
|
end
|
100
175
|
|
101
176
|
def total_size(...)
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
end
|
177
|
+
Endpoint
|
178
|
+
.new(self, ...)
|
179
|
+
.list_files { |files| return files.sum(&:size) }
|
106
180
|
end
|
107
181
|
|
108
182
|
def get_filenames(...)
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
end
|
183
|
+
Endpoint
|
184
|
+
.new(self, ...)
|
185
|
+
.list_files { |files| return files.map(&:name) }
|
113
186
|
end
|
114
187
|
|
115
188
|
def has_files?(...)
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
end
|
189
|
+
Endpoint
|
190
|
+
.new(self, ...)
|
191
|
+
.has_files?
|
120
192
|
end
|
121
193
|
|
122
194
|
def user_valid?(...)
|
123
|
-
|
124
|
-
|
125
|
-
|
195
|
+
Identity
|
196
|
+
.new(self)
|
197
|
+
.valid?(...)
|
198
|
+
end
|
199
|
+
|
200
|
+
private
|
201
|
+
|
202
|
+
Config = Struct.new(:client_id, :auth_url, :client_secret, :transfer_endpoint_id, :transfer_url, :uploads_directory, :token, keyword_init: true)
|
203
|
+
|
204
|
+
def connection(base_url)
|
205
|
+
Faraday.new(url: base_url) do |conn|
|
206
|
+
conn.request :retry, {
|
207
|
+
max: 10,
|
208
|
+
interval: 0.05,
|
209
|
+
interval_randomness: 0.5,
|
210
|
+
backoff_factor: 2,
|
211
|
+
exceptions: Faraday::Retry::Middleware::DEFAULT_EXCEPTIONS + [Faraday::ConnectionFailed]
|
212
|
+
}
|
126
213
|
end
|
127
214
|
end
|
215
|
+
|
216
|
+
# Wraps API operations to request new access token if expired.
|
217
|
+
# @yieldreturn response [Faraday::Response] the response to inspect
|
218
|
+
#
|
219
|
+
# @note You likely want to make sure you're wrapping a _single_ HTTP request in this
|
220
|
+
# method, because 1) all calls in the block will be retried from the top if there's
|
221
|
+
# an authN failure detected, and 2) only the response returned by the block will be
|
222
|
+
# inspected for authN failure.
|
223
|
+
# Related: consider that the client instance and its token will live across many
|
224
|
+
# invocations of the GlobusClient methods once the client is configured by a consuming application,
|
225
|
+
# since this class is a Singleton. Thus, a token may expire between any two calls (i.e. it
|
226
|
+
# isn't necessary for a set of operations to collectively take longer than the token lifetime for
|
227
|
+
# expiry to fall in the middle of that related set of HTTP calls).
|
228
|
+
def with_token_refresh_when_unauthorized
|
229
|
+
response = yield
|
230
|
+
|
231
|
+
# if unauthorized, token has likely expired. try to get a new token and then retry the same request(s).
|
232
|
+
if response.status == 401
|
233
|
+
config.token = Authenticator.token(config.client_id, config.client_secret, config.auth_url)
|
234
|
+
response = yield
|
235
|
+
end
|
236
|
+
|
237
|
+
response
|
238
|
+
end
|
128
239
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: globus_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.14.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aaron Collier
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date: 2023-
|
13
|
+
date: 2023-10-11 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
@@ -74,90 +74,6 @@ dependencies:
|
|
74
74
|
- - ">="
|
75
75
|
- !ruby/object:Gem::Version
|
76
76
|
version: '0'
|
77
|
-
- !ruby/object:Gem::Dependency
|
78
|
-
name: rake
|
79
|
-
requirement: !ruby/object:Gem::Requirement
|
80
|
-
requirements:
|
81
|
-
- - "~>"
|
82
|
-
- !ruby/object:Gem::Version
|
83
|
-
version: '13.0'
|
84
|
-
type: :development
|
85
|
-
prerelease: false
|
86
|
-
version_requirements: !ruby/object:Gem::Requirement
|
87
|
-
requirements:
|
88
|
-
- - "~>"
|
89
|
-
- !ruby/object:Gem::Version
|
90
|
-
version: '13.0'
|
91
|
-
- !ruby/object:Gem::Dependency
|
92
|
-
name: rspec
|
93
|
-
requirement: !ruby/object:Gem::Requirement
|
94
|
-
requirements:
|
95
|
-
- - "~>"
|
96
|
-
- !ruby/object:Gem::Version
|
97
|
-
version: '3.0'
|
98
|
-
type: :development
|
99
|
-
prerelease: false
|
100
|
-
version_requirements: !ruby/object:Gem::Requirement
|
101
|
-
requirements:
|
102
|
-
- - "~>"
|
103
|
-
- !ruby/object:Gem::Version
|
104
|
-
version: '3.0'
|
105
|
-
- !ruby/object:Gem::Dependency
|
106
|
-
name: standard
|
107
|
-
requirement: !ruby/object:Gem::Requirement
|
108
|
-
requirements:
|
109
|
-
- - ">="
|
110
|
-
- !ruby/object:Gem::Version
|
111
|
-
version: '0'
|
112
|
-
type: :development
|
113
|
-
prerelease: false
|
114
|
-
version_requirements: !ruby/object:Gem::Requirement
|
115
|
-
requirements:
|
116
|
-
- - ">="
|
117
|
-
- !ruby/object:Gem::Version
|
118
|
-
version: '0'
|
119
|
-
- !ruby/object:Gem::Dependency
|
120
|
-
name: rubocop-rspec
|
121
|
-
requirement: !ruby/object:Gem::Requirement
|
122
|
-
requirements:
|
123
|
-
- - ">="
|
124
|
-
- !ruby/object:Gem::Version
|
125
|
-
version: '0'
|
126
|
-
type: :development
|
127
|
-
prerelease: false
|
128
|
-
version_requirements: !ruby/object:Gem::Requirement
|
129
|
-
requirements:
|
130
|
-
- - ">="
|
131
|
-
- !ruby/object:Gem::Version
|
132
|
-
version: '0'
|
133
|
-
- !ruby/object:Gem::Dependency
|
134
|
-
name: simplecov
|
135
|
-
requirement: !ruby/object:Gem::Requirement
|
136
|
-
requirements:
|
137
|
-
- - ">="
|
138
|
-
- !ruby/object:Gem::Version
|
139
|
-
version: '0'
|
140
|
-
type: :development
|
141
|
-
prerelease: false
|
142
|
-
version_requirements: !ruby/object:Gem::Requirement
|
143
|
-
requirements:
|
144
|
-
- - ">="
|
145
|
-
- !ruby/object:Gem::Version
|
146
|
-
version: '0'
|
147
|
-
- !ruby/object:Gem::Dependency
|
148
|
-
name: webmock
|
149
|
-
requirement: !ruby/object:Gem::Requirement
|
150
|
-
requirements:
|
151
|
-
- - ">="
|
152
|
-
- !ruby/object:Gem::Version
|
153
|
-
version: '0'
|
154
|
-
type: :development
|
155
|
-
prerelease: false
|
156
|
-
version_requirements: !ruby/object:Gem::Requirement
|
157
|
-
requirements:
|
158
|
-
- - ">="
|
159
|
-
- !ruby/object:Gem::Version
|
160
|
-
version: '0'
|
161
77
|
description: This provides API interaction with the Globus API
|
162
78
|
email:
|
163
79
|
- aaron.collier@stanford.edu
|
@@ -167,11 +83,9 @@ executables: []
|
|
167
83
|
extensions: []
|
168
84
|
extra_rdoc_files: []
|
169
85
|
files:
|
170
|
-
- ".autoupdate/postupdate"
|
171
86
|
- ".rspec"
|
172
87
|
- ".rubocop.yml"
|
173
|
-
- ".
|
174
|
-
- ".standard.yml"
|
88
|
+
- ".rubocop_todo.yml"
|
175
89
|
- Gemfile
|
176
90
|
- Gemfile.lock
|
177
91
|
- LICENSE
|
@@ -183,7 +97,6 @@ files:
|
|
183
97
|
- lib/globus_client/authenticator.rb
|
184
98
|
- lib/globus_client/endpoint.rb
|
185
99
|
- lib/globus_client/identity.rb
|
186
|
-
- lib/globus_client/token_wrapper.rb
|
187
100
|
- lib/globus_client/unexpected_response.rb
|
188
101
|
- lib/globus_client/version.rb
|
189
102
|
homepage: https://github.com/sul-dlss/globus_client
|
@@ -201,7 +114,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
201
114
|
requirements:
|
202
115
|
- - ">="
|
203
116
|
- !ruby/object:Gem::Version
|
204
|
-
version:
|
117
|
+
version: 3.1.0
|
205
118
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
206
119
|
requirements:
|
207
120
|
- - ">="
|
data/.autoupdate/postupdate
DELETED
@@ -1,19 +0,0 @@
|
|
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 GlobusClient (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/.rubocop/custom.yml
DELETED
@@ -1,80 +0,0 @@
|
|
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
|
-
Capybara/MatchStyle: # new in 2.17
|
52
|
-
Enabled: true
|
53
|
-
FactoryBot/AssociationStyle: # new in 2.23
|
54
|
-
Enabled: true
|
55
|
-
FactoryBot/FactoryAssociationWithStrategy: # new in 2.23
|
56
|
-
Enabled: true
|
57
|
-
FactoryBot/FactoryNameStyle: # new in 2.16
|
58
|
-
Enabled: true
|
59
|
-
FactoryBot/RedundantFactoryOption: # new in 2.23
|
60
|
-
Enabled: true
|
61
|
-
RSpec/BeEmpty: # new in 2.20
|
62
|
-
Enabled: true
|
63
|
-
RSpec/ContainExactly: # new in 2.19
|
64
|
-
Enabled: true
|
65
|
-
RSpec/DuplicatedMetadata: # new in 2.16
|
66
|
-
Enabled: true
|
67
|
-
RSpec/IndexedLet: # new in 2.20
|
68
|
-
Enabled: false # Sometimes indices *are* meaningful
|
69
|
-
RSpec/MatchArray: # new in 2.19
|
70
|
-
Enabled: true
|
71
|
-
RSpec/PendingWithoutReason: # new in 2.16
|
72
|
-
Enabled: true
|
73
|
-
RSpec/RedundantAround: # new in 2.19
|
74
|
-
Enabled: true
|
75
|
-
RSpec/SkipBlockInsideExample: # new in 2.19
|
76
|
-
Enabled: true
|
77
|
-
RSpec/Rails/MinitestAssertions: # new in 2.17
|
78
|
-
Enabled: true
|
79
|
-
RSpec/Rails/TravelAround: # new in 2.19
|
80
|
-
Enabled: true
|
data/.standard.yml
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
parallel: true
|
@@ -1,13 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class GlobusClient
|
4
|
-
# Wraps API operations to request new access token if expired
|
5
|
-
class TokenWrapper
|
6
|
-
def self.refresh(config)
|
7
|
-
yield
|
8
|
-
rescue UnexpectedResponse::UnauthorizedError
|
9
|
-
config.token = Authenticator.token(config.client_id, config.client_secret, config.auth_url)
|
10
|
-
yield
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|