browse-everything 1.5.0 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 75a514ad33ec151f78c54fd74220a6d385bde4107894e2b6beb69e30eab6c27e
4
- data.tar.gz: e688ee1ba6a0a9b8f519ca33077acda6bbc062033fc122c33876da850ff36146
3
+ metadata.gz: faa729e0e6ae6d2ae76c0c9364b44017d689bbb89153b3c1e98bde8c17fa1427
4
+ data.tar.gz: 7a29fe7df1a1dd5bdd309e23e4edc22af28d3fb618bceaf01d852537d37fdb3b
5
5
  SHA512:
6
- metadata.gz: 8b3b2068517c326ca832b8adf020dff2e297576bb19189f0707e5f812cc01b09ceef932cc8903fd1f60d6d0031d02d66685d5ba72829c89c22ee84474c2527d4
7
- data.tar.gz: cf3298fd572ca0666e9a808ffb1d359cc48050ad6a512d8777d2c9fb0635b8546c0fa7c9312198ea2113440cfc24d4a6ef28302c98d343c2dde7b5edefcae260
6
+ metadata.gz: 4a97f843dafad43ddb8a58cf30f663d586dcdecaec0ba1fde64f38c2189c6a3c731d9fd0e1c9c39437ce63f8ad294ded4befac02d651b9de2063eedf3b13311b
7
+ data.tar.gz: 487830c4ba65dbd1810774a87fef7ecd4202e805a4153bbfdb4c333e9c833afdcfaab24de26e285c198e2bc29d1ea944f387bf1011cb21c76e697da88a23e2d3
data/Gemfile CHANGED
@@ -25,6 +25,12 @@ end
25
25
 
26
26
  ENV['RAILS_VERSION'] ||= "8.0.0"
27
27
 
28
+ # json 3 dropped the quirks_mode option that ActiveSupport before 8.1 passes
29
+ # when encoding, so Hash#to_json raises ArgumentError under it.
30
+ if ENV['RAILS_VERSION'] != 'edge' && Gem::Version.new(ENV['RAILS_VERSION']) < Gem::Version.new('8.1')
31
+ gem 'json', '< 3'
32
+ end
33
+
28
34
  if ENV['RAILS_VERSION']
29
35
  if ENV['RAILS_VERSION'] == 'edge'
30
36
  gem 'rails', github: 'rails/rails'
@@ -45,6 +51,10 @@ if ENV['RAILS_VERSION']
45
51
 
46
52
  # sprockets is optional for rails 7, but we currently require it, and test with it.
47
53
  gem "sprockets-rails"
54
+
55
+ # workaround bug in old rails
56
+ # https://github.com/rails/rails/pull/54264
57
+ gem "concurrent-ruby", "< 1.3.5"
48
58
  when /^6\.1\./
49
59
  # rails previous to 7.1 can't use sqlite 2
50
60
  gem "sqlite3", "~> 1.4"
@@ -53,17 +63,18 @@ if ENV['RAILS_VERSION']
53
63
  # Once mail 2.8.0 final is released this will not be required.
54
64
  # https://github.com/mikel/mail/pull/1472
55
65
  gem "mail", ">= 2.8.0.rc1"
66
+
67
+ # workaround bug in old rails
68
+ # https://github.com/rails/rails/pull/54264
69
+ gem "concurrent-ruby", "< 1.3.5"
56
70
  when /^6\.0\./
57
71
  # rails previous to 7.1 can't use sqlite 2
58
72
  gem "sqlite3", "~> 1.4"
59
73
 
60
74
  gem 'sass-rails', '>= 6'
61
- when /^5\.[12]\./
62
- # rails previous to 7.1 can't use sqlite 2
63
- gem "sqlite3", "~> 1.4"
64
75
 
65
- gem 'sass-rails', '~> 5.0'
66
- gem 'sprockets', '~> 3.7'
67
- gem 'thor', '~> 0.20'
76
+ # workaround bug in old rails
77
+ # https://github.com/rails/rails/pull/54264
78
+ gem "concurrent-ruby", "< 1.3.5"
68
79
  end
69
80
  end
@@ -323,7 +323,12 @@ $(function () {
323
323
 
324
324
  $(document).on('click', 'button.ev-submit', function (event) {
325
325
  event.preventDefault();
326
- $(this).button('loading');
326
+ // button('loading') is Bootstrap 3's button plugin. When jQuery UI's
327
+ // button widget owns $.fn.button instead, it throws on the call and the
328
+ // selection is never submitted. startWait() disables the button either way.
329
+ if ($.fn.button && $.fn.button.Constructor) {
330
+ $(this).button('loading');
331
+ }
327
332
  startWait();
328
333
  $('form.ev-submit-form').append(Array.from(selected_files.values()));
329
334
  var main_form = $(this).closest('form');
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
26
26
  spec.add_dependency 'rails', '>= 4.2', '< 8.1'
27
27
  spec.add_dependency 'ruby-box'
28
28
  spec.add_dependency 'signet', '~> 0.8'
29
- spec.add_dependency 'typhoeus'
29
+ spec.add_dependency 'faraday', "~> 2.0"
30
30
 
31
31
  # Development dependencies include dependencies necessary for running
32
32
  # the dummy test app at ./spec/dummy_test_app
@@ -70,7 +70,7 @@ module BrowseEverything
70
70
  # @param path [String] the path (default to the root)
71
71
  # @return [Array<BrowseEverything::FileEntry>] file entries for the path
72
72
  def list_files(drive, request_params, path: '')
73
- drive.list_files(request_params.to_h) do |file_list, error|
73
+ drive.list_files(**request_params.to_h) do |file_list, error|
74
74
  # Raise an exception if there was an error Google API's
75
75
  if error.present?
76
76
  # In order to properly trigger reauthentication, the token must be cleared
@@ -55,17 +55,19 @@ module BrowseEverything
55
55
  def link_for(path)
56
56
  obj = bucket.object(full_path(path))
57
57
 
58
- extras = {
59
- file_name: File.basename(path),
60
- expires: (config[:expires_in] if config[:response_type] == :signed_url)
61
- }.compact
62
-
63
- url = case config[:response_type].to_sym
58
+ response_type = config[:response_type].to_sym
59
+ url = case response_type
64
60
  when :signed_url then obj.presigned_url(:get, expires_in: config[:expires_in])
65
61
  when :public_url then obj.public_url
66
62
  when :s3_uri then "s3://#{obj.bucket_name}/#{obj.key}"
67
63
  end
68
64
 
65
+ extras = {
66
+ file_name: File.basename(path),
67
+ file_size: object_size(obj),
68
+ expires: (signed_url_expiry(url) if response_type == :signed_url)
69
+ }.compact
70
+
69
71
  [url, extras]
70
72
  end
71
73
 
@@ -79,6 +81,26 @@ module BrowseEverything
79
81
 
80
82
  private
81
83
 
84
+ # The size is a convenience for consumers, so credentials that can list
85
+ # but not read an object (enough for an :s3_uri link) leave it out
86
+ # rather than fail the link.
87
+ def object_size(obj)
88
+ obj.content_length
89
+ rescue Aws::S3::Errors::ServiceError, Seahorse::Client::NetworkingError
90
+ nil
91
+ end
92
+
93
+ # Retriever#retrieve parses `expires` as a time, so send when the link
94
+ # expires, as the Box and Google Drive drivers do. It is read from the
95
+ # signed URL itself because the SDK can sign for less than expires_in,
96
+ # for example when the signing credentials are temporary.
97
+ def signed_url_expiry(url)
98
+ params = Rack::Utils.parse_query(URI(url).query)
99
+ return unless params['X-Amz-Date'] && params['X-Amz-Expires']
100
+
101
+ Time.strptime(params['X-Amz-Date'], '%Y%m%dT%H%M%S%z') + params['X-Amz-Expires'].to_i
102
+ end
103
+
82
104
  def strip(path)
83
105
  path.sub %r{^/?(.+?)/?$}, '\1'
84
106
  end
@@ -2,25 +2,25 @@
2
2
 
3
3
  require 'addressable'
4
4
  require 'tempfile'
5
- require 'typhoeus'
5
+ require 'faraday'
6
6
 
7
7
  module BrowseEverything
8
8
  # Class for raising errors when a download is invalid
9
9
  class DownloadError < StandardError
10
- attr_reader :response
10
+ attr_accessor :response_body
11
11
 
12
12
  # Constructor
13
13
  # @param msg [String]
14
- # @param response [Typhoeus::Response] response from the server
15
- def initialize(msg, response)
16
- @response = response
14
+ # @param response [String] response from the server
15
+ def initialize(msg, response_body)
16
+ @response_body = response_body
17
17
  super(msg)
18
18
  end
19
19
 
20
20
  # Generate the message for the exception
21
21
  # @return [String]
22
22
  def message
23
- "#{super}: #{response.body}"
23
+ "#{super}: #{response_body}"
24
24
  end
25
25
  end
26
26
 
@@ -36,7 +36,7 @@ module BrowseEverything
36
36
  # @param headers [Hash] any custom headers required to transit the request
37
37
  def can_retrieve?(uri, headers = {})
38
38
  request_headers = headers.merge(Range: 'bytes=0-0')
39
- response = Typhoeus.get(uri, headers: request_headers)
39
+ response = Faraday.get(uri, headers: request_headers)
40
40
  response.success?
41
41
  end
42
42
  end
@@ -133,17 +133,23 @@ module BrowseEverything
133
133
  file_size = options.fetch(:file_size)
134
134
  headers = options.fetch(:headers)
135
135
  url = options.fetch(:url)
136
- retrieved = 0
137
-
138
- request = Typhoeus::Request.new(url.to_s, method: :get, headers: headers)
139
- request.on_headers do |response|
140
- raise DownloadError.new("#{self.class}: Failed to download #{url}: Status Code: #{response.code}", response) unless response.code == 200
136
+ error_response = +""
137
+
138
+ conn = Faraday.new(url.to_s, headers: headers)
139
+ response = conn.get do |req|
140
+ req.options.on_data = proc do |chunk, overall_received_bytes, env|
141
+ if env.status == 200
142
+ # will yield to block passed in to outer retrieve_http, amazing.
143
+ yield(chunk, overall_received_bytes, file_size)
144
+ else
145
+ error_response << chunk
146
+ end
147
+ end
141
148
  end
142
- request.on_body do |chunk|
143
- retrieved += chunk.bytesize
144
- yield(chunk, retrieved, file_size)
149
+
150
+ unless response.status == 200
151
+ raise DownloadError.new("#{self.class}: Failed to download #{url}: Status Code: #{response.status}", error_response)
145
152
  end
146
- request.run
147
153
  end
148
154
 
149
155
  # Retrieve the file size
@@ -158,7 +164,7 @@ module BrowseEverything
158
164
  when 'file'
159
165
  File.size(url.path)
160
166
  when /https?/
161
- response = Typhoeus.head(url.to_s, headers: headers)
167
+ response = Faraday.head(url.to_s, headers: headers)
162
168
  length_value = response.headers['Content-Length'] || file_size
163
169
  length_value.to_i
164
170
  else
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BrowseEverything
4
- VERSION = '1.5.0'
4
+ VERSION = '1.6.1'
5
5
  end
@@ -134,28 +134,68 @@ describe BrowseEverything::Driver::S3 do
134
134
  context 'when retrieving the link for an asset' do
135
135
  subject { contents[2] }
136
136
 
137
+ let(:signed_at) { Time.now.utc.change(usec: 0) }
138
+ let(:presigned_url) do
139
+ "https://s3.amazonaws.com/presigned_url?X-Amz-Date=#{signed_at.strftime('%Y%m%dT%H%M%SZ')}&X-Amz-Expires=3600"
140
+ end
141
+
137
142
  before do
138
143
  object = instance_double(Aws::S3::Object)
139
- allow(object).to receive(:presigned_url).and_return('https://s3.amazonaws.com/presigned_url')
144
+ allow(object).to receive(:presigned_url).and_return(presigned_url)
140
145
  allow(object).to receive(:public_url).and_return('https://s3.amazonaws.com/public_url')
141
146
  allow(object).to receive(:bucket_name).and_return('s3.bucket')
142
147
  allow(object).to receive(:key).and_return('foo/quux.png')
148
+ allow(object).to receive(:content_length).and_return(1_511_860)
143
149
  allow(provider.bucket).to receive(:object).and_return(object)
144
150
  end
145
151
 
146
152
  it ':signed_url' do
147
153
  provider.config[:response_type] = :signed_url
148
- expect(provider.link_for('foo/quux.png')).to eq ['https://s3.amazonaws.com/presigned_url', { file_name: 'quux.png', expires: 14400 }]
154
+ expect(provider.link_for('foo/quux.png')).to eq [presigned_url, { file_name: 'quux.png', file_size: 1_511_860, expires: signed_at + 3600 }]
155
+ end
156
+
157
+ it ':signed_url reports the lifetime the URL was signed for, not expires_in' do
158
+ provider.config[:response_type] = :signed_url
159
+ provider.config[:expires_in] = 14400
160
+ expect(provider.link_for('foo/quux.png').last[:expires]).to eq signed_at + 3600
161
+ end
162
+
163
+ it ':signed_url configured as a string still sends the expiry' do
164
+ provider.config[:response_type] = 'signed_url'
165
+ expect(provider.link_for('foo/quux.png').last[:expires]).to eq signed_at + 3600
166
+ end
167
+
168
+ [Aws::S3::Errors::Forbidden.new(nil, 'Forbidden'), Seahorse::Client::NetworkingError.new(Errno::ECONNRESET.new)].each do |error|
169
+ it "leaves out the size when reading it raises #{error.class}" do
170
+ provider.config[:response_type] = :s3_uri
171
+ allow(provider.bucket.object('foo/quux.png')).to receive(:content_length).and_raise(error)
172
+ expect(provider.link_for('foo/quux.png')).to eq ['s3://s3.bucket/foo/quux.png', { file_name: 'quux.png' }]
173
+ end
174
+ end
175
+
176
+ it ':s3_uri keeps a key that is not a valid URI' do
177
+ provider.config[:response_type] = :s3_uri
178
+ allow(provider.bucket.object('foo/quux.png')).to receive(:key).and_return('foo/résumé file.pdf')
179
+ expect(provider.link_for('foo/quux.png').first).to eq 's3://s3.bucket/foo/résumé file.pdf'
180
+ end
181
+
182
+ it ':signed_url gives an expiry the retriever accepts' do
183
+ provider.config[:response_type] = :signed_url
184
+ url, extras = provider.link_for('foo/quux.png')
185
+ spec = { 'url' => url, 'expires' => extras[:expires].to_s, 'file_size' => extras[:file_size] }
186
+ retriever = BrowseEverything::Retriever.new
187
+ allow(retriever).to receive(:retrieve_http)
188
+ expect { retriever.retrieve(spec) }.not_to raise_error
149
189
  end
150
190
 
151
191
  it ':public_url' do
152
192
  provider.config[:response_type] = :public_url
153
- expect(provider.link_for('foo/quux.png')).to eq ['https://s3.amazonaws.com/public_url', { file_name: 'quux.png' }]
193
+ expect(provider.link_for('foo/quux.png')).to eq ['https://s3.amazonaws.com/public_url', { file_name: 'quux.png', file_size: 1_511_860 }]
154
194
  end
155
195
 
156
196
  it ':s3_uri' do
157
197
  provider.config[:response_type] = :s3_uri
158
- expect(provider.link_for('foo/quux.png')).to eq ['s3://s3.bucket/foo/quux.png', { file_name: 'quux.png' }]
198
+ expect(provider.link_for('foo/quux.png')).to eq ['s3://s3.bucket/foo/quux.png', { file_name: 'quux.png', file_size: 1_511_860 }]
159
199
  end
160
200
  end
161
201
  end
@@ -160,12 +160,13 @@ describe BrowseEverything::Retriever do
160
160
  stub_request(
161
161
  :get, "https://retrieve.cloud.example.com/some/dir/file_error.pdf"
162
162
  ).and_return(
163
- status: 403
163
+ status: 403,
164
+ body: "Did not like this"
164
165
  )
165
166
  end
166
167
 
167
168
  it 'raises an exception' do
168
- expect { retriever.download(download_options) }.to raise_error(BrowseEverything::DownloadError, /BrowseEverything::Retriever: Failed to download/)
169
+ expect { retriever.download(download_options) }.to raise_error(BrowseEverything::DownloadError, /BrowseEverything::Retriever: Failed to download.* Status Code: 403.*Did not like this/)
169
170
  end
170
171
  end
171
172
  end
@@ -234,7 +235,7 @@ describe BrowseEverything::Retriever do
234
235
  let(:url) { 'https://retrieve.cloud.example.com/some/dir/can_retrieve.pdf' }
235
236
  before do
236
237
  stub_request(
237
- :get, "https://retrieve.cloud.example.com/some/dir/can_retrieve.pdf"
238
+ :get, %r{^https://retrieve.cloud.example.com/some/dir/can_retrieve.pdf\?}
238
239
  ).to_return(
239
240
  status: 206,
240
241
  body: '%'
@@ -250,7 +251,7 @@ describe BrowseEverything::Retriever do
250
251
  let(:url) { 'https://retrieve.cloud.example.com/some/dir/cannot_retrieve.pdf' }
251
252
  before do
252
253
  stub_request(
253
- :get, "https://retrieve.cloud.example.com/some/dir/cannot_retrieve.pdf"
254
+ :get, %r{^https://retrieve.cloud.example.com/some/dir/cannot_retrieve.pdf\?}
254
255
  ).to_return(
255
256
  status: 403
256
257
  )
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: browse-everything
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
4
+ version: 1.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carolyn Cole
@@ -10,10 +10,9 @@ authors:
10
10
  - Thomas Scherz
11
11
  - Xiaoming Wang
12
12
  - Jeremy Friesen
13
- autorequire:
14
13
  bindir: bin
15
14
  cert_chain: []
16
- date: 2024-12-12 00:00:00.000000000 Z
15
+ date: 1980-01-02 00:00:00.000000000 Z
17
16
  dependencies:
18
17
  - !ruby/object:Gem::Dependency
19
18
  name: addressable
@@ -140,19 +139,19 @@ dependencies:
140
139
  - !ruby/object:Gem::Version
141
140
  version: '0.8'
142
141
  - !ruby/object:Gem::Dependency
143
- name: typhoeus
142
+ name: faraday
144
143
  requirement: !ruby/object:Gem::Requirement
145
144
  requirements:
146
- - - ">="
145
+ - - "~>"
147
146
  - !ruby/object:Gem::Version
148
- version: '0'
147
+ version: '2.0'
149
148
  type: :runtime
150
149
  prerelease: false
151
150
  version_requirements: !ruby/object:Gem::Requirement
152
151
  requirements:
153
- - - ">="
152
+ - - "~>"
154
153
  - !ruby/object:Gem::Version
155
- version: '0'
154
+ version: '2.0'
156
155
  - !ruby/object:Gem::Dependency
157
156
  name: bixby
158
157
  requirement: !ruby/object:Gem::Requirement
@@ -616,7 +615,6 @@ homepage: https://github.com/projecthydra/browse-everything
616
615
  licenses:
617
616
  - Apache 2
618
617
  metadata: {}
619
- post_install_message:
620
618
  rdoc_options: []
621
619
  require_paths:
622
620
  - lib
@@ -631,8 +629,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
631
629
  - !ruby/object:Gem::Version
632
630
  version: '0'
633
631
  requirements: []
634
- rubygems_version: 3.5.9
635
- signing_key:
632
+ rubygems_version: 4.0.22
636
633
  specification_version: 4
637
634
  summary: AJAX/Rails engine file browser for cloud storage services
638
635
  test_files: