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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 65d27626412e2eff8ff6514be3ece81fd5f27e471137fcfd8409e51cd6e0f105
4
- data.tar.gz: 321b1d89734daacb8ad54487836a68f1fdf1af931fad426d72ec35bc2f355ec8
3
+ metadata.gz: bae9be2599dca47133561cc9f50d02118d633e973f973619bf642a890d266d7d
4
+ data.tar.gz: 032a19fb8edf3b11740dd487415a98f14522daac3ae4966302376b902c787e01
5
5
  SHA512:
6
- metadata.gz: a4ce82aac85e8c00df5310cdb3b4f821cd1415ee0d525938562b20d97c86f330707691a8ee69dcbfeee53d0e20064f78fd902678d5aa4a0113cc7ce38f209c9a
7
- data.tar.gz: 75888830f1b0660f78d551eb3fd2a323f40fb21e5ac9a361c0f8ea22be0b32a11a014dd42719e4b25c33f5f497e2d1b6b296c8db15f5dc89abaed5d4f03ff5e1
6
+ metadata.gz: f66705c5adbda9f69f8ab99b8c02ad8b16dafcf4760b0918f63191874066691c1c46211588a197629206f6e53f72b1893748bcac04594db44afeaa1f3a40706a
7
+ data.tar.gz: f901d249a7b112efb537e88387aa6833759f2ec6f801277d4970a0830d7761abfe64e8a4163337ce252e2b9beb4e46410757832ecc300f649d8e21a5a6381669
data/.rubocop.yml CHANGED
@@ -1,18 +1,27 @@
1
- inherit_mode:
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
- inherit_gem:
13
- standard: config/base.yml
14
- standard-performance: config/base.yml
15
- standard-custom: config/base.yml
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
- inherit_from:
18
- - .rubocop/custom.yml
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 "https://rubygems.org"
3
+ source 'https://rubygems.org'
4
4
 
5
5
  # Specify your gem's dependencies in globus_client.gemspec
6
6
  gemspec
7
7
 
8
- gem "byebug"
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.12.1)
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.8)
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.3)
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.3)
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.18.0)
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.0)
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.4.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.11)
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 "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
5
- require "rubocop/rake_task"
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 "benchmark"
5
- require "bundler/setup"
6
- require "globus_client"
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("Configure:") do
11
+ benchmark.report('Configure:') do
12
12
  GlobusClient.configure(
13
- client_id: ENV["GLOBUS_CLIENT_ID"],
14
- client_secret: ENV["GLOBUS_CLIENT_SECRET"],
15
- uploads_directory: ENV["GLOBUS_UPLOADS_DIRECTORY"],
16
- transfer_endpoint_id: ENV["GLOBUS_ENDPOINT"]
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("mkdir:") do
20
+ benchmark.report('mkdir:') do
21
21
  GlobusClient.mkdir(user_id:, path:)
22
22
  end
23
23
 
24
- benchmark.report("user_valid?:") do
24
+ benchmark.report('user_valid?:') do
25
25
  @user_exists = GlobusClient.user_valid?(user_id)
26
26
  end
27
27
 
28
- benchmark.report("before_perms:") do
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.config, user_id:, path:).send(:access_rule)["permissions"]
30
+ @before_permissions = GlobusClient::Endpoint.new(GlobusClient.instance, user_id:,
31
+ path:).send(:access_rule)['permissions']
31
32
  end
32
33
 
33
- benchmark.report("has_files?:") do
34
+ benchmark.report('has_files?:') do
34
35
  @has_files = GlobusClient.has_files?(user_id:, path:)
35
36
  end
36
37
 
37
- benchmark.report("list_files:") do
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("disallow_writes:") do
46
+ benchmark.report('disallow_writes:') do
46
47
  GlobusClient.disallow_writes(user_id:, path:)
47
48
  end
48
49
 
49
- benchmark.report("after_perms:") do
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.config, user_id:, path:).send(:access_rule)["permissions"]
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}"
@@ -1,24 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path("lib", __dir__)
3
+ lib = File.expand_path('lib', __dir__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
- require "globus_client/version"
5
+ require 'globus_client/version'
6
6
 
7
7
  Gem::Specification.new do |spec|
8
- spec.name = "globus_client"
8
+ spec.name = 'globus_client'
9
9
  spec.version = GlobusClient::VERSION
10
- spec.authors = ["Aaron Collier", "Laura Wrubel", "Mike Giarlo"]
11
- spec.email = ["aaron.collier@stanford.edu", "lwrubel@stanford.edu", "mjgiarlo@stanford.edu"]
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 = "Interface for interacting with the Globus API."
14
- spec.description = "This provides API interaction with the Globus API"
15
- spec.homepage = "https://github.com/sul-dlss/globus_client"
16
- spec.required_ruby_version = ">= 2.6.0"
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["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"
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 = "exe"
30
+ spec.bindir = 'exe'
31
31
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
32
- spec.require_paths = ["lib"]
32
+ spec.require_paths = ['lib']
33
33
 
34
- spec.add_dependency "activesupport", ">= 4.2", "< 8"
35
- spec.add_dependency "faraday"
36
- spec.add_dependency "faraday-retry"
37
- spec.add_dependency "zeitwerk"
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("/v2/oauth2/token", form_data)
18
+ response = connection.post('/v2/oauth2/token', form_data)
19
19
 
20
- JSON.parse(response.body)["access_token"]
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: "form",
36
- grant_type: "client_credentials",
37
- scope: "urn:globus:auth:scope:transfer.api.globus.org:all"
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 config [#token, #uploads_directory, #transfer_endpoint_id, #transfer_url, #auth_url] configuration for the gem
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
- def initialize(config, path:, user_id:)
14
- @config = config
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
- response = connection.post("#{transfer_path}/mkdir") do |req|
34
- req.headers["Content-Type"] = "application/json"
35
- req.body = {
36
- DATA_TYPE: "mkdir",
37
- path:
38
- }.to_json
39
- end
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: "rw")
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: "r")
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}") if !access_rule_id
58
+ raise(StandardError, "Access rule not found for #{path}") unless access_rule_id
66
59
 
67
- response = connection.delete("#{access_path}/#{access_rule_id}")
68
- return true if response.success?
69
-
70
- UnexpectedResponse.call(response)
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 :config, :path, :user_id
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(config).get_identity_id(user_id)
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 = connection.get("#{transfer_path}/ls?path=#{CGI.escape(filepath)}")
127
- return UnexpectedResponse.call(response) unless response.success?
128
-
129
- data = JSON.parse(response.body)["DATA"]
130
- data
131
- .select { |object| object["type"] == "file" }
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["name"]}", file["size"])
113
+ files << FileInfo.new("#{filepath}#{file['name']}", file['size'])
136
114
  end
137
- data
138
- .select { |object| object["type"] == "dir" }
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["name"]}/", files, return_presence:) == true
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
- response = if access_rule_id
153
- connection.put("#{access_path}/#{access_rule_id}") do |req|
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
- connection.post(access_path) do |req|
162
- req.body = {
163
- DATA_TYPE: "access",
164
- principal_type: "identity",
165
- principal: globus_identity_id,
166
- path: full_path,
167
- permissions:,
168
- notify_email: user_id
169
- }.to_json
170
- req.headers["Content-Type"] = "application/json"
171
- end
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}") if !access_rule_id
151
+ raise(StandardError, "Access rule not found for #{path}") unless access_rule_id
181
152
 
182
- response = connection.put("#{access_path}/#{access_rule_id}") do |req|
183
- req.body = {
184
- DATA_TYPE: "access",
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
- }.to_json
187
- req.headers["Content-Type"] = "application/json"
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 = connection.get(access_list_path) do |req|
197
- req.headers["Content-Type"] = "application/json"
198
- end
199
-
200
- # debugging of Globus responses
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
- JSON
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("id")
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(config)
7
- @config = config
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
- @email = user_id
14
- response = lookup_identity
15
- UnexpectedResponse.call(response) unless response.success?
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
- data = JSON.parse(response.body)
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
- ["used", "private", "unused"].include?(get_identity(user_id)["status"])
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)["id"]
31
+ get_identity(user_id)['id']
31
32
  end
32
33
 
33
34
  private
34
35
 
35
- attr_reader :config
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, message: "")
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}. #{message}"
40
+ raise EndpointError, "Other error with endpoint: #{response.status} #{response.body}."
41
41
  when 503
42
- raise ServiceUnavailable, "The service is down for maintenance."
42
+ raise ServiceUnavailable, 'The service is down for maintenance.'
43
43
  else
44
- raise StandardError, "Unexpected response: #{response.status} #{response.body}. #{message}"
44
+ raise StandardError, "Unexpected response: #{response.status} #{response.body}."
45
45
  end
46
46
  end
47
47
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class GlobusClient
4
- VERSION = "0.12.1"
4
+ VERSION = '0.14.0'
5
5
  end
data/lib/globus_client.rb CHANGED
@@ -1,17 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/module/delegation"
4
- require "faraday"
5
- require "faraday/retry"
6
- require "ostruct"
7
- require "singleton"
8
- 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'
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
- def configure(client_id:, client_secret:, uploads_directory:, transfer_endpoint_id:, transfer_url: default_transfer_url, auth_url: default_auth_url)
25
- instance.config = OpenStruct.new(
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 the `TokenWrapper` to handle auto-magic
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 `TokenWrapper`
36
- token: "a temporary dummy token to avoid hitting the API before it is needed",
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
- :user_valid?, :get_filenames, :has_files?, to: :instance
52
+ :user_valid?, :get_filenames, :has_files?, :delete, :get, :post, :put, to: :instance
50
53
 
51
54
  def default_transfer_url
52
- "https://transfer.api.globusonline.org"
55
+ 'https://transfer.api.globusonline.org'
53
56
  end
54
57
 
55
58
  def default_auth_url
56
- "https://auth.globus.org"
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
- TokenWrapper.refresh(config) do
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
- TokenWrapper.refresh(config) do
72
- endpoint = Endpoint.new(config, ...)
73
- endpoint.disallow_writes
74
- end
150
+ Endpoint
151
+ .new(self, ...)
152
+ .disallow_writes
75
153
  end
76
154
 
77
155
  def delete_access_rule(...)
78
- TokenWrapper.refresh(config) do
79
- endpoint = Endpoint.new(config, ...)
80
- endpoint.delete_access_rule
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, &block)
88
- TokenWrapper.refresh(config) do
89
- endpoint = Endpoint.new(config, **keywords)
90
- endpoint.list_files(&block)
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
- TokenWrapper.refresh(config) do
96
- endpoint = Endpoint.new(config, ...)
97
- endpoint.list_files { |files| return files.count }
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
- TokenWrapper.refresh(config) do
103
- endpoint = Endpoint.new(config, ...)
104
- endpoint.list_files { |files| return files.sum(&:size) }
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
- TokenWrapper.refresh(config) do
110
- endpoint = Endpoint.new(config, ...)
111
- endpoint.list_files { |files| return files.map(&:name) }
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
- TokenWrapper.refresh(config) do
117
- endpoint = Endpoint.new(config, ...)
118
- endpoint.has_files?
119
- end
189
+ Endpoint
190
+ .new(self, ...)
191
+ .has_files?
120
192
  end
121
193
 
122
194
  def user_valid?(...)
123
- TokenWrapper.refresh(config) do
124
- identity = Identity.new(config)
125
- identity.valid?(...)
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.12.1
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-09-22 00:00:00.000000000 Z
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
- - ".rubocop/custom.yml"
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: 2.6.0
117
+ version: 3.1.0
205
118
  required_rubygems_version: !ruby/object:Gem::Requirement
206
119
  requirements:
207
120
  - - ">="
@@ -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