globus_client 0.13.0 → 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 +17 -20
- data/Rakefile +3 -3
- data/api_test.rb +20 -18
- data/globus_client.gemspec +19 -26
- data/lib/globus_client/authenticator.rb +5 -5
- data/lib/globus_client/endpoint.rb +34 -29
- data/lib/globus_client/identity.rb +5 -5
- data/lib/globus_client/unexpected_response.rb +1 -1
- data/lib/globus_client/version.rb +1 -1
- data/lib/globus_client.rb +28 -24
- metadata +5 -91
- data/.autoupdate/postupdate +0 -19
- data/.rubocop/custom.yml +0 -80
- data/.standard.yml +0 -1
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)
|
@@ -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.instance, user_id:,
|
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.instance, user_id:,
|
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,11 +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
21
|
|
22
|
-
JSON.parse(response.body)[
|
22
|
+
JSON.parse(response.body)['access_token']
|
23
23
|
end
|
24
24
|
|
25
25
|
private
|
@@ -34,9 +34,9 @@ class GlobusClient
|
|
34
34
|
{
|
35
35
|
client_id:,
|
36
36
|
client_secret:,
|
37
|
-
encoding:
|
38
|
-
grant_type:
|
39
|
-
scope:
|
37
|
+
encoding: 'form',
|
38
|
+
grant_type: 'client_credentials',
|
39
|
+
scope: 'urn:globus:auth:scope:transfer.api.globus.org:all'
|
40
40
|
}
|
41
41
|
end
|
42
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
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
|
-
|
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)
|
14
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?
|
@@ -33,25 +35,27 @@ class GlobusClient
|
|
33
35
|
client.post(
|
34
36
|
base_url: client.config.transfer_url,
|
35
37
|
path: "#{transfer_path}/mkdir",
|
36
|
-
body: {DATA_TYPE:
|
37
|
-
expected_response:
|
38
|
+
body: { DATA_TYPE: 'mkdir', path: },
|
39
|
+
expected_response: lambda { |resp|
|
40
|
+
resp.status == 502 && JSON.parse(resp.body)['code'] == 'ExternalError.MkdirFailed.Exists'
|
41
|
+
}
|
38
42
|
)
|
39
43
|
end
|
40
44
|
end
|
41
45
|
|
42
46
|
# Assign a user read/write permissions for a directory https://docs.globus.org/api/transfer/acl/#rest_access_create
|
43
47
|
def allow_writes
|
44
|
-
access_request(permissions:
|
48
|
+
access_request(permissions: 'rw')
|
45
49
|
end
|
46
50
|
|
47
51
|
# Assign a user read-only permissions for a directory https://docs.globus.org/api/transfer/acl/#rest_access_create
|
48
52
|
def disallow_writes
|
49
|
-
update_access_request(permissions:
|
53
|
+
update_access_request(permissions: 'r')
|
50
54
|
end
|
51
55
|
|
52
56
|
# Delete the access rule https://docs.globus.org/api/transfer/acl/#delete_access_rule
|
53
57
|
def delete_access_rule
|
54
|
-
raise(StandardError, "Access rule not found for #{path}")
|
58
|
+
raise(StandardError, "Access rule not found for #{path}") unless access_rule_id
|
55
59
|
|
56
60
|
client.delete(
|
57
61
|
base_url: client.config.transfer_url,
|
@@ -61,7 +65,7 @@ class GlobusClient
|
|
61
65
|
|
62
66
|
private
|
63
67
|
|
64
|
-
attr_reader :client, :path, :user_id
|
68
|
+
attr_reader :client, :path, :user_id, :notify_email
|
65
69
|
|
66
70
|
def globus_identity_id
|
67
71
|
Identity.new(client).get_identity_id(user_id)
|
@@ -98,24 +102,24 @@ class GlobusClient
|
|
98
102
|
response = client.get(
|
99
103
|
base_url: client.config.transfer_url,
|
100
104
|
path: "#{transfer_path}/ls",
|
101
|
-
params: {path: filepath}
|
105
|
+
params: { path: filepath }
|
102
106
|
)
|
103
107
|
|
104
|
-
response[
|
105
|
-
.select { |object| object[
|
108
|
+
response['DATA']
|
109
|
+
.select { |object| object['type'] == 'file' }
|
106
110
|
.each do |file|
|
107
111
|
return true if return_presence
|
108
112
|
|
109
|
-
files << FileInfo.new("#{filepath}#{file[
|
113
|
+
files << FileInfo.new("#{filepath}#{file['name']}", file['size'])
|
110
114
|
end
|
111
115
|
|
112
|
-
response[
|
113
|
-
.select { |object| object[
|
116
|
+
response['DATA']
|
117
|
+
.select { |object| object['type'] == 'dir' }
|
114
118
|
.each do |dir|
|
115
119
|
# NOTE: This allows the recursive method to short-circuit iff ls_path
|
116
120
|
# returns true, which only happens when return_presence is true
|
117
121
|
# and the first file is found in the ls operation.
|
118
|
-
return true if ls_path("#{filepath}#{dir[
|
122
|
+
return true if ls_path("#{filepath}#{dir['name']}/", files, return_presence:) == true
|
119
123
|
end
|
120
124
|
|
121
125
|
return false if return_presence
|
@@ -127,29 +131,30 @@ class GlobusClient
|
|
127
131
|
if access_rule_id
|
128
132
|
update_access_request(permissions:)
|
129
133
|
else
|
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
|
130
142
|
client.post(
|
131
143
|
base_url: client.config.transfer_url,
|
132
144
|
path: access_path,
|
133
|
-
body:
|
134
|
-
DATA_TYPE: "access",
|
135
|
-
principal_type: "identity",
|
136
|
-
principal: globus_identity_id,
|
137
|
-
path: full_path,
|
138
|
-
permissions:,
|
139
|
-
notify_email: user_id
|
140
|
-
}
|
145
|
+
body:
|
141
146
|
)
|
142
147
|
end
|
143
148
|
end
|
144
149
|
|
145
150
|
def update_access_request(permissions:)
|
146
|
-
raise(StandardError, "Access rule not found for #{path}")
|
151
|
+
raise(StandardError, "Access rule not found for #{path}") unless access_rule_id
|
147
152
|
|
148
153
|
client.put(
|
149
154
|
base_url: client.config.transfer_url,
|
150
155
|
path: "#{access_path}/#{access_rule_id}",
|
151
156
|
body: {
|
152
|
-
DATA_TYPE:
|
157
|
+
DATA_TYPE: 'access',
|
153
158
|
permissions:
|
154
159
|
}
|
155
160
|
)
|
@@ -159,14 +164,14 @@ class GlobusClient
|
|
159
164
|
response = client.get(
|
160
165
|
base_url: client.config.transfer_url,
|
161
166
|
path: access_list_path,
|
162
|
-
content_type:
|
167
|
+
content_type: 'application/json'
|
163
168
|
)
|
164
169
|
|
165
|
-
response.fetch(
|
170
|
+
response.fetch('DATA').find { |acl| acl['path'] == full_path }
|
166
171
|
end
|
167
172
|
|
168
173
|
def access_rule_id
|
169
|
-
access_rule&.fetch(
|
174
|
+
access_rule&.fetch('id')
|
170
175
|
end
|
171
176
|
|
172
177
|
def transfer_path
|
@@ -12,23 +12,23 @@ class GlobusClient
|
|
12
12
|
def get_identity(user_id)
|
13
13
|
response = client.get(
|
14
14
|
base_url: client.config.auth_url,
|
15
|
-
path:
|
16
|
-
params: {usernames: user_id}
|
15
|
+
path: '/v2/api/identities',
|
16
|
+
params: { usernames: user_id }
|
17
17
|
)
|
18
18
|
|
19
|
-
response[
|
19
|
+
response['identities'].find { |id| id['username'] == user_id }
|
20
20
|
end
|
21
21
|
|
22
22
|
# @param user_id [String] the username in the form of an email addresss
|
23
23
|
# @return [Boolean] whether the account has a valid status
|
24
24
|
def valid?(user_id)
|
25
|
-
[
|
25
|
+
%w[used private unused].include?(get_identity(user_id)['status'])
|
26
26
|
end
|
27
27
|
|
28
28
|
# @param user_id [String] the username in the form of an email addresss
|
29
29
|
# @return [String] UUID for Globus identity
|
30
30
|
def get_identity_id(user_id)
|
31
|
-
get_identity(user_id)[
|
31
|
+
get_identity(user_id)['id']
|
32
32
|
end
|
33
33
|
|
34
34
|
private
|
@@ -39,7 +39,7 @@ class GlobusClient
|
|
39
39
|
when 502
|
40
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
44
|
raise StandardError, "Unexpected response: #{response.status} #{response.body}."
|
45
45
|
end
|
data/lib/globus_client.rb
CHANGED
@@ -1,18 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require "zeitwerk"
|
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'
|
10
9
|
|
11
10
|
# Load the gem's internal dependencies: use Zeitwerk instead of needing to manually require classes
|
12
11
|
Zeitwerk::Loader.for_gem.setup
|
13
12
|
|
14
13
|
# Client for interacting with the Globus API
|
15
|
-
class GlobusClient
|
14
|
+
class GlobusClient # rubocop:disable Metrics/ClassLength
|
16
15
|
include Singleton
|
17
16
|
|
18
17
|
class << self
|
@@ -22,8 +21,10 @@ class GlobusClient
|
|
22
21
|
# @param transfer_endpoint_id [String] the transfer API endpoint ID supplied by Globus
|
23
22
|
# @param transfer_url [String] the transfer API URL
|
24
23
|
# @param auth_url [String] the authentication API URL
|
25
|
-
|
26
|
-
|
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(
|
27
28
|
# For the initial token, use a dummy value to avoid hitting any APIs
|
28
29
|
# during configuration, allowing `with_token_refresh_when_unauthorized` to handle
|
29
30
|
# auto-magic token refreshing. Why not immediately get a valid token? Our apps
|
@@ -34,7 +35,7 @@ class GlobusClient
|
|
34
35
|
# NOTE: `nil` and blank string cannot be used as dummy values here as
|
35
36
|
# they lead to a malformed request to be sent, which triggers an
|
36
37
|
# exception not rescued by `with_token_refresh_when_unauthorized`
|
37
|
-
token:
|
38
|
+
token: 'a temporary dummy token to avoid hitting the API before it is needed',
|
38
39
|
client_id:,
|
39
40
|
client_secret:,
|
40
41
|
uploads_directory:,
|
@@ -45,16 +46,17 @@ class GlobusClient
|
|
45
46
|
|
46
47
|
self
|
47
48
|
end
|
49
|
+
# rubocop:enable Metrics/ParameterLists
|
48
50
|
|
49
51
|
delegate :config, :disallow_writes, :delete_access_rule, :file_count, :list_files, :mkdir, :total_size,
|
50
|
-
|
52
|
+
:user_valid?, :get_filenames, :has_files?, :delete, :get, :post, :put, to: :instance
|
51
53
|
|
52
54
|
def default_transfer_url
|
53
|
-
|
55
|
+
'https://transfer.api.globusonline.org'
|
54
56
|
end
|
55
57
|
|
56
58
|
def default_auth_url
|
57
|
-
|
59
|
+
'https://auth.globus.org'
|
58
60
|
end
|
59
61
|
end
|
60
62
|
|
@@ -67,8 +69,8 @@ class GlobusClient
|
|
67
69
|
def get(base_url:, path:, params: {}, content_type: nil)
|
68
70
|
response = with_token_refresh_when_unauthorized do
|
69
71
|
connection(base_url).get(path, params) do |request|
|
70
|
-
request.headers[
|
71
|
-
request.headers[
|
72
|
+
request.headers['Authorization'] = "Bearer #{config.token}"
|
73
|
+
request.headers['Content-Type'] = content_type if content_type
|
72
74
|
end
|
73
75
|
end
|
74
76
|
|
@@ -84,11 +86,11 @@ class GlobusClient
|
|
84
86
|
# @param path [String] the path to the Globus API request
|
85
87
|
# @param body [String] the body of the Globus API request
|
86
88
|
# @param expected_response [#call] an expected response handler to allow short-circuiting the unexpected response
|
87
|
-
def post(base_url:, path:, body:, expected_response: ->(
|
89
|
+
def post(base_url:, path:, body:, expected_response: ->(_resp) { false })
|
88
90
|
response = with_token_refresh_when_unauthorized do
|
89
91
|
connection(base_url).post(path) do |request|
|
90
|
-
request.headers[
|
91
|
-
request.headers[
|
92
|
+
request.headers['Authorization'] = "Bearer #{config.token}"
|
93
|
+
request.headers['Content-Type'] = 'application/json'
|
92
94
|
request.body = body.to_json
|
93
95
|
end
|
94
96
|
end
|
@@ -107,8 +109,8 @@ class GlobusClient
|
|
107
109
|
def put(base_url:, path:, body:)
|
108
110
|
response = with_token_refresh_when_unauthorized do
|
109
111
|
connection(base_url).put(path) do |request|
|
110
|
-
request.headers[
|
111
|
-
request.headers[
|
112
|
+
request.headers['Authorization'] = "Bearer #{config.token}"
|
113
|
+
request.headers['Content-Type'] = 'application/json'
|
112
114
|
request.body = body.to_json
|
113
115
|
end
|
114
116
|
end
|
@@ -126,7 +128,7 @@ class GlobusClient
|
|
126
128
|
def delete(base_url:, path:)
|
127
129
|
response = with_token_refresh_when_unauthorized do
|
128
130
|
connection(base_url).delete(path) do |request|
|
129
|
-
request.headers[
|
131
|
+
request.headers['Authorization'] = "Bearer #{config.token}"
|
130
132
|
end
|
131
133
|
end
|
132
134
|
|
@@ -159,10 +161,10 @@ class GlobusClient
|
|
159
161
|
# NOTE: Can't use the `...` (argument forwarding) operator here because we
|
160
162
|
# want to route the keyword args to `Endpoint#new` and the block arg to
|
161
163
|
# `Endpoint#list_files`
|
162
|
-
def list_files(**keywords, &
|
164
|
+
def list_files(**keywords, &)
|
163
165
|
Endpoint
|
164
166
|
.new(self, **keywords)
|
165
|
-
.list_files(&
|
167
|
+
.list_files(&)
|
166
168
|
end
|
167
169
|
|
168
170
|
def file_count(...)
|
@@ -197,6 +199,8 @@ class GlobusClient
|
|
197
199
|
|
198
200
|
private
|
199
201
|
|
202
|
+
Config = Struct.new(:client_id, :auth_url, :client_secret, :transfer_endpoint_id, :transfer_url, :uploads_directory, :token, keyword_init: true)
|
203
|
+
|
200
204
|
def connection(base_url)
|
201
205
|
Faraday.new(url: base_url) do |conn|
|
202
206
|
conn.request :retry, {
|
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
|
@@ -200,14 +114,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
200
114
|
requirements:
|
201
115
|
- - ">="
|
202
116
|
- !ruby/object:Gem::Version
|
203
|
-
version:
|
117
|
+
version: 3.1.0
|
204
118
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
205
119
|
requirements:
|
206
120
|
- - ">="
|
207
121
|
- !ruby/object:Gem::Version
|
208
122
|
version: '0'
|
209
123
|
requirements: []
|
210
|
-
rubygems_version: 3.4.
|
124
|
+
rubygems_version: 3.4.13
|
211
125
|
signing_key:
|
212
126
|
specification_version: 4
|
213
127
|
summary: Interface for interacting with the Globus API.
|
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
|