globus_client 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +10 -7
- data/README.md +24 -3
- data/api_test.rb +37 -0
- data/lib/globus/client/endpoint.rb +85 -42
- data/lib/globus/client/identity.rb +7 -0
- data/lib/globus/client/version.rb +1 -1
- data/lib/globus/client.rb +22 -2
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7a4c2d5c57d5c7439a64bf4a0c9da297422987a026ff9eafdb38722df11de72c
|
4
|
+
data.tar.gz: 48b723cfdcad41ccd033dcb57ea2e09ad1da8ec139f7a98cf36a6c264565898c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fc74c6a8123ff04756bffcb06e6f0765eba0aaa18c634dd1d4481767392f320aa57bf0dec54904b92d92d88c6fa239dff417fec6c372f0b5af7293f61dc00dcd
|
7
|
+
data.tar.gz: 1b0b02bcbfe3964fb4f8e526af347403654a93441ddebe8fb3ea3ea8bd5cde326a3327c026114a2309f6650d029f0c4c3484bd8a831a2b55867b1d67436fc5f4
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
globus_client (0.1
|
4
|
+
globus_client (0.2.1)
|
5
5
|
activesupport (>= 4.2, < 8)
|
6
6
|
faraday
|
7
7
|
zeitwerk
|
@@ -31,9 +31,10 @@ GEM
|
|
31
31
|
i18n (1.12.0)
|
32
32
|
concurrent-ruby (~> 1.0)
|
33
33
|
json (2.6.2)
|
34
|
+
language_server-protocol (3.17.0.1)
|
34
35
|
minitest (5.16.3)
|
35
36
|
parallel (1.22.1)
|
36
|
-
parser (3.1.
|
37
|
+
parser (3.1.3.0)
|
37
38
|
ast (~> 2.4.1)
|
38
39
|
public_suffix (5.0.0)
|
39
40
|
rainbow (3.1.1)
|
@@ -63,9 +64,9 @@ GEM
|
|
63
64
|
rubocop-ast (>= 1.23.0, < 2.0)
|
64
65
|
ruby-progressbar (~> 1.7)
|
65
66
|
unicode-display_width (>= 1.4.0, < 3.0)
|
66
|
-
rubocop-ast (1.
|
67
|
+
rubocop-ast (1.24.0)
|
67
68
|
parser (>= 3.1.1.0)
|
68
|
-
rubocop-performance (1.15.
|
69
|
+
rubocop-performance (1.15.1)
|
69
70
|
rubocop (>= 1.7.0, < 2.0)
|
70
71
|
rubocop-ast (>= 0.4.0)
|
71
72
|
rubocop-rspec (2.15.0)
|
@@ -78,9 +79,10 @@ GEM
|
|
78
79
|
simplecov_json_formatter (~> 0.1)
|
79
80
|
simplecov-html (0.12.3)
|
80
81
|
simplecov_json_formatter (0.1.4)
|
81
|
-
standard (1.
|
82
|
+
standard (1.19.0)
|
83
|
+
language_server-protocol (= 3.17.0.1)
|
82
84
|
rubocop (= 1.39.0)
|
83
|
-
rubocop-performance (= 1.15.
|
85
|
+
rubocop-performance (= 1.15.1)
|
84
86
|
tzinfo (2.0.5)
|
85
87
|
concurrent-ruby (~> 1.0)
|
86
88
|
unicode-display_width (2.3.0)
|
@@ -91,6 +93,7 @@ GEM
|
|
91
93
|
zeitwerk (2.6.6)
|
92
94
|
|
93
95
|
PLATFORMS
|
96
|
+
x86_64-darwin-19
|
94
97
|
x86_64-darwin-20
|
95
98
|
x86_64-darwin-21
|
96
99
|
x86_64-linux
|
@@ -106,4 +109,4 @@ DEPENDENCIES
|
|
106
109
|
webmock
|
107
110
|
|
108
111
|
BUNDLED WITH
|
109
|
-
2.3.
|
112
|
+
2.3.26
|
data/README.md
CHANGED
@@ -32,6 +32,8 @@ client = Globus::Client.configure(
|
|
32
32
|
transfer_endpoint_id: Settings.globus.transfer_endpoint_id
|
33
33
|
)
|
34
34
|
client.mkdir(user_id: 'mjgiarlo', work_id: 1234, work_version: 1)
|
35
|
+
|
36
|
+
result = client.user_exists?('mjgiarlo')
|
35
37
|
```
|
36
38
|
|
37
39
|
You can also invoke methods directly on the client class, which is useful in a
|
@@ -58,12 +60,31 @@ end
|
|
58
60
|
|
59
61
|
## Development
|
60
62
|
|
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.
|
63
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
64
64
|
|
65
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
66
|
|
67
|
+
### Integration Testing
|
68
|
+
|
69
|
+
To test that the gem works against the Globus APIs, run `api_test.rb` via:
|
70
|
+
|
71
|
+
```shell
|
72
|
+
# NOTE: This is bash syntax, YMMV
|
73
|
+
$ export GLOBUS_CLIENT_ID=$(vault kv get -field=content puppet/application/sdr/globus/{prod|qa|stage}/client_id)
|
74
|
+
$ export GLOBUS_CLIENT_SECRET=$(vault kv get -field=content puppet/application/sdr/globus/{prod|qa|stage}/client_secret)
|
75
|
+
$ export GLOBUS_ENDPOINT=$(vault kv get -field=content puppet/application/sdr/globus/{prod|qa|stage}/endpoint_uuid)
|
76
|
+
$ export GLOBUS_UPLOADS_DIRECTORY=from_shared_configs
|
77
|
+
# NOTE: The three args below are a user ID, a work ID, and a work version
|
78
|
+
$ ./api_test.rb mjgiarlo 987 1
|
79
|
+
|
80
|
+
Initial directory permissions: rw
|
81
|
+
Number of files in directory: 2
|
82
|
+
Total size of files in directory: 66669
|
83
|
+
Final directory permissions: r
|
84
|
+
```
|
85
|
+
|
86
|
+
Inspect the output and compare it to what you see in Globus Personal Connect to determine if behavior is correct.
|
87
|
+
|
67
88
|
## Contributing
|
68
89
|
|
69
90
|
Bug reports and pull requests are welcome on GitHub at https://github.com/sul-dlss/globus_client.
|
data/api_test.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "globus/client"
|
6
|
+
|
7
|
+
Globus::Client.configure(
|
8
|
+
client_id: ENV["GLOBUS_CLIENT_ID"],
|
9
|
+
client_secret: ENV["GLOBUS_CLIENT_SECRET"],
|
10
|
+
uploads_directory: ENV["GLOBUS_UPLOADS_DIRECTORY"],
|
11
|
+
transfer_endpoint_id: ENV["GLOBUS_ENDPOINT"]
|
12
|
+
)
|
13
|
+
|
14
|
+
user_id, work_id, work_version = *ARGV
|
15
|
+
|
16
|
+
# Test public API methods here.
|
17
|
+
Globus::Client.mkdir(user_id:, work_id:, work_version:)
|
18
|
+
|
19
|
+
user_exists = Globus::Client.user_exists?(user_id)
|
20
|
+
|
21
|
+
# Not part of the public API but this allows us to test access changes
|
22
|
+
before_permissions = Globus::Client::Endpoint.new(Globus::Client.config, user_id: user_id, work_id: work_id, work_version: work_version).send(:access_rule)["permissions"]
|
23
|
+
|
24
|
+
files_count = Globus::Client.file_count(user_id:, work_id:, work_version:)
|
25
|
+
|
26
|
+
total_size = Globus::Client.total_size(user_id:, work_id:, work_version:)
|
27
|
+
|
28
|
+
Globus::Client.disallow_writes(user_id:, work_id:, work_version:)
|
29
|
+
|
30
|
+
# Not part of the public API but this allows us to test access changes
|
31
|
+
after_permissions = Globus::Client::Endpoint.new(Globus::Client.config, user_id: user_id, work_id: work_id, work_version: work_version).send(:access_rule)["permissions"]
|
32
|
+
|
33
|
+
puts "User #{user_id} exists: #{user_exists}"
|
34
|
+
puts "Initial directory permissions: #{before_permissions}"
|
35
|
+
puts "Number of files in directory: #{files_count}"
|
36
|
+
puts "Total size of files in directory: #{total_size}"
|
37
|
+
puts "Final directory permissions: #{after_permissions}"
|
@@ -15,22 +15,32 @@ module Globus
|
|
15
15
|
@work_version = work_version
|
16
16
|
end
|
17
17
|
|
18
|
-
|
19
|
-
def length
|
18
|
+
def file_count
|
20
19
|
objects["total"]
|
21
20
|
end
|
22
21
|
|
22
|
+
def total_size
|
23
|
+
files.sum { |file| file["size"] }
|
24
|
+
end
|
25
|
+
|
23
26
|
# Create a directory https://docs.globus.org/api/transfer/file_operations/#make_directory
|
24
27
|
def mkdir
|
25
28
|
# transfer API does not support recursive directory creation
|
26
29
|
paths.each do |path|
|
27
|
-
response =
|
30
|
+
response = connection.post("#{transfer_path}/mkdir") do |req|
|
31
|
+
req.headers["Content-Type"] = "application/json"
|
32
|
+
req.body = {
|
33
|
+
DATA_TYPE: "mkdir",
|
34
|
+
path:
|
35
|
+
}.to_json
|
36
|
+
end
|
37
|
+
|
28
38
|
next if response.success?
|
29
39
|
|
30
|
-
# if directory already exists
|
40
|
+
# Ignore error if directory already exists
|
31
41
|
if response.status == 502
|
32
42
|
error = JSON.parse(response.body)
|
33
|
-
next if error["code"] == "ExternalError.
|
43
|
+
next if error["code"] == "ExternalError.MkdirFailed.Exists"
|
34
44
|
end
|
35
45
|
|
36
46
|
UnexpectedResponse.call(response)
|
@@ -38,11 +48,13 @@ module Globus
|
|
38
48
|
end
|
39
49
|
|
40
50
|
# Assign a user read/write permissions for a directory https://docs.globus.org/api/transfer/acl/#rest_access_create
|
41
|
-
def
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
51
|
+
def allow_writes
|
52
|
+
access_request(permissions: "rw")
|
53
|
+
end
|
54
|
+
|
55
|
+
# Assign a user read-only permissions for a directory https://docs.globus.org/api/transfer/acl/#rest_access_create
|
56
|
+
def disallow_writes
|
57
|
+
access_request(permissions: "r")
|
46
58
|
end
|
47
59
|
|
48
60
|
private
|
@@ -57,6 +69,10 @@ module Globus
|
|
57
69
|
)
|
58
70
|
end
|
59
71
|
|
72
|
+
def user
|
73
|
+
Identity.new(config).get_identity_id(user_id)
|
74
|
+
end
|
75
|
+
|
60
76
|
# Builds up a path from a list of path elements. E.g., input would look like:
|
61
77
|
# ["mjgiarlo", "work123", "version1"]
|
62
78
|
# And this method returns:
|
@@ -67,52 +83,79 @@ module Globus
|
|
67
83
|
end
|
68
84
|
end
|
69
85
|
|
86
|
+
# @see #paths
|
87
|
+
def full_path
|
88
|
+
paths.last
|
89
|
+
end
|
90
|
+
|
70
91
|
def path_segments
|
71
92
|
[user_id, "work#{work_id}", "version#{work_version}"]
|
72
93
|
end
|
73
94
|
|
74
|
-
def
|
75
|
-
|
95
|
+
def objects
|
96
|
+
# List files at an endpoint https://docs.globus.org/api/transfer/file_operations/#list_directory_contents
|
97
|
+
response = connection.get("#{transfer_path}/ls?path=#{full_path}")
|
98
|
+
return JSON.parse(response.body) if response.success?
|
99
|
+
|
100
|
+
UnexpectedResponse.call(response)
|
76
101
|
end
|
77
102
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
103
|
+
def files
|
104
|
+
objects["DATA"].select { |object| object["DATA_TYPE"] == "file" }
|
105
|
+
end
|
106
|
+
|
107
|
+
def access_request(permissions:)
|
108
|
+
response = if access_rule_id
|
109
|
+
connection.put("#{access_path}/#{access_rule_id}") do |req|
|
110
|
+
req.body = {
|
111
|
+
DATA_TYPE: "access",
|
112
|
+
permissions:
|
113
|
+
}.to_json
|
114
|
+
req.headers["Content-Type"] = "application/json"
|
115
|
+
end
|
116
|
+
else
|
117
|
+
connection.post(access_path) do |req|
|
118
|
+
req.body = {
|
119
|
+
DATA_TYPE: "access",
|
120
|
+
principal_type: "identity",
|
121
|
+
principal: user,
|
122
|
+
path: full_path,
|
123
|
+
permissions:,
|
124
|
+
notify_email: "#{user_id}@stanford.edu"
|
125
|
+
}.to_json
|
126
|
+
req.headers["Content-Type"] = "application/json"
|
127
|
+
end
|
86
128
|
end
|
129
|
+
|
130
|
+
return response if response.success?
|
131
|
+
|
132
|
+
UnexpectedResponse.call(response)
|
87
133
|
end
|
88
134
|
|
89
|
-
|
90
|
-
|
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
|
135
|
+
def access_rule
|
136
|
+
response = connection.get(access_list_path) do |req|
|
104
137
|
req.headers["Content-Type"] = "application/json"
|
105
138
|
end
|
106
|
-
UnexpectedResponse.call(response) unless response.success?
|
107
139
|
|
108
|
-
|
140
|
+
JSON
|
141
|
+
.parse(response.body)["DATA"]
|
142
|
+
.find { |acl| acl["path"] == full_path }
|
109
143
|
end
|
110
144
|
|
111
|
-
def
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
145
|
+
def access_rule_id
|
146
|
+
access_rule&.fetch("id")
|
147
|
+
end
|
148
|
+
|
149
|
+
def transfer_path
|
150
|
+
"/v0.10/operation/endpoint/#{config.transfer_endpoint_id}"
|
151
|
+
end
|
152
|
+
|
153
|
+
def access_path
|
154
|
+
"/v0.10/endpoint/#{config.transfer_endpoint_id}/access"
|
155
|
+
end
|
156
|
+
|
157
|
+
def access_list_path
|
158
|
+
"/v0.10/endpoint/#{config.transfer_endpoint_id}/access_list"
|
116
159
|
end
|
117
160
|
end
|
118
161
|
end
|
data/lib/globus/client.rb
CHANGED
@@ -36,7 +36,7 @@ module Globus
|
|
36
36
|
self
|
37
37
|
end
|
38
38
|
|
39
|
-
delegate :mkdir, :
|
39
|
+
delegate :config, :disallow_writes, :file_count, :mkdir, :total_size, :user_exists?, to: :instance
|
40
40
|
|
41
41
|
def default_transfer_url
|
42
42
|
"https://transfer.api.globusonline.org"
|
@@ -52,7 +52,27 @@ module Globus
|
|
52
52
|
def mkdir(...)
|
53
53
|
endpoint = Globus::Client::Endpoint.new(config, ...)
|
54
54
|
endpoint.mkdir
|
55
|
-
endpoint.
|
55
|
+
endpoint.allow_writes
|
56
|
+
end
|
57
|
+
|
58
|
+
def disallow_writes(...)
|
59
|
+
endpoint = Globus::Client::Endpoint.new(config, ...)
|
60
|
+
endpoint.disallow_writes
|
61
|
+
end
|
62
|
+
|
63
|
+
def file_count(...)
|
64
|
+
endpoint = Globus::Client::Endpoint.new(config, ...)
|
65
|
+
endpoint.file_count
|
66
|
+
end
|
67
|
+
|
68
|
+
def total_size(...)
|
69
|
+
endpoint = Globus::Client::Endpoint.new(config, ...)
|
70
|
+
endpoint.total_size
|
71
|
+
end
|
72
|
+
|
73
|
+
def user_exists?(...)
|
74
|
+
identity = Globus::Client::Identity.new(config)
|
75
|
+
identity.exists?(...)
|
56
76
|
end
|
57
77
|
end
|
58
78
|
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.3.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: 2022-
|
13
|
+
date: 2022-12-07 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
@@ -164,6 +164,7 @@ files:
|
|
164
164
|
- LICENSE
|
165
165
|
- README.md
|
166
166
|
- Rakefile
|
167
|
+
- api_test.rb
|
167
168
|
- globus_client.gemspec
|
168
169
|
- lib/globus/client.rb
|
169
170
|
- lib/globus/client/authenticator.rb
|