browse-everything 0.15.1 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +61 -9
  3. data/.rubocop_todo.yml +2 -15
  4. data/.travis.yml +19 -19
  5. data/CONTRIBUTING.md +6 -6
  6. data/Gemfile +12 -8
  7. data/README.md +30 -0
  8. data/Rakefile +2 -1
  9. data/app/assets/javascripts/browse_everything/behavior.js.coffee +5 -0
  10. data/app/controllers/browse_everything_controller.rb +75 -23
  11. data/app/helpers/browse_everything_helper.rb +2 -8
  12. data/app/helpers/font_awesome_version_helper.rb +9 -8
  13. data/app/services/browse_everything_session.rb +10 -0
  14. data/app/services/browse_everything_session/provider_session.rb +42 -0
  15. data/app/services/browser_factory.rb +25 -0
  16. data/app/views/browse_everything/_files.html.erb +56 -6
  17. data/browse-everything.gemspec +29 -25
  18. data/config/routes.rb +7 -2
  19. data/lib/browse-everything.rb +2 -0
  20. data/lib/browse_everything.rb +45 -12
  21. data/lib/browse_everything/auth/google/credentials.rb +28 -0
  22. data/lib/browse_everything/auth/google/request_parameters.rb +61 -0
  23. data/lib/browse_everything/browser.rb +11 -4
  24. data/lib/browse_everything/driver/authentication_factory.rb +22 -0
  25. data/lib/browse_everything/driver/base.rb +72 -19
  26. data/lib/browse_everything/driver/box.rb +46 -17
  27. data/lib/browse_everything/driver/dropbox.rb +36 -10
  28. data/lib/browse_everything/driver/file_system.rb +14 -26
  29. data/lib/browse_everything/driver/google_drive.rb +187 -54
  30. data/lib/browse_everything/driver/s3.rb +81 -75
  31. data/lib/browse_everything/engine.rb +3 -2
  32. data/lib/browse_everything/file_entry.rb +3 -1
  33. data/lib/browse_everything/retriever.rb +103 -31
  34. data/lib/browse_everything/version.rb +3 -1
  35. data/lib/generators/browse_everything/assets_generator.rb +3 -2
  36. data/lib/generators/browse_everything/config_generator.rb +11 -9
  37. data/lib/generators/browse_everything/install_generator.rb +3 -2
  38. data/lib/generators/browse_everything/templates/browse_everything_providers.yml.example +12 -11
  39. data/spec/controllers/browse_everything_controller_spec.rb +80 -0
  40. data/spec/features/select_files_spec.rb +13 -13
  41. data/spec/features/test_compiling_stylesheets_spec.rb +2 -0
  42. data/spec/fixtures/vcr_cassettes/google_drive.yml +331 -0
  43. data/spec/fixtures/vcr_cassettes/retriever.yml +93 -0
  44. data/spec/helper/browse_everything_controller_helper_spec.rb +21 -7
  45. data/spec/javascripts/jasmine_spec.rb +2 -0
  46. data/spec/javascripts/support/jasmine_helper.rb +1 -0
  47. data/spec/lib/browse_everything/auth/google/credentials_spec.rb +41 -0
  48. data/spec/{unit → lib/browse_everything}/browse_everything_helper_spec.rb +2 -0
  49. data/spec/lib/browse_everything/browser_spec.rb +109 -0
  50. data/spec/{unit → lib/browse_everything/driver}/base_spec.rb +5 -4
  51. data/spec/{unit → lib/browse_everything/driver}/box_spec.rb +20 -5
  52. data/spec/{unit → lib/browse_everything/driver}/dropbox_spec.rb +15 -18
  53. data/spec/{unit → lib/browse_everything/driver}/file_system_spec.rb +32 -26
  54. data/spec/lib/browse_everything/driver/google_drive_spec.rb +171 -0
  55. data/spec/{unit → lib/browse_everything/driver}/s3_spec.rb +38 -21
  56. data/spec/lib/browse_everything/driver_spec.rb +38 -0
  57. data/spec/{unit → lib/browse_everything}/file_entry_spec.rb +4 -1
  58. data/spec/lib/browse_everything/retriever_spec.rb +200 -0
  59. data/spec/lib/browse_everything_spec.rb +67 -0
  60. data/spec/services/browse_everything_session/provider_session_spec.rb +50 -0
  61. data/spec/services/browser_factory_spec.rb +40 -0
  62. data/spec/spec_helper.rb +39 -18
  63. data/spec/support/app/controllers/file_handler_controller.rb +4 -4
  64. data/spec/support/app/views/file_handler/main.html.erb +1 -1
  65. data/spec/support/capybara.rb +17 -0
  66. data/spec/support/rake.rb +3 -1
  67. data/spec/support/wait_for_ajax.rb +14 -0
  68. data/spec/test_app_templates/Gemfile.extra +1 -0
  69. data/spec/test_app_templates/lib/generators/test_app_generator.rb +10 -4
  70. data/spec/views/browse_everything/{_file.html.erb_spec.rb → _files.html.erb_spec.rb} +24 -18
  71. data/tasks/ci.rake +2 -0
  72. metadata +159 -107
  73. data/app/views/browse_everything/_file.html.erb +0 -52
  74. data/app/views/browse_everything/resolve.html.erb +0 -1
  75. data/spec/unit/browser_spec.rb +0 -76
  76. data/spec/unit/retriever_spec.rb +0 -109
@@ -0,0 +1,171 @@
1
+ # frozen_string_literal: true
2
+
3
+ include BrowserConfigHelper
4
+
5
+ describe BrowseEverything::Driver::GoogleDrive, vcr: { cassette_name: 'google_drive', record: :none } do
6
+ let(:browser) { BrowseEverything::Browser.new(url_options) }
7
+ let(:provider) { browser.providers['google_drive'] }
8
+ let(:provider_yml) do
9
+ {
10
+ client_id: 'CLIENTID', client_secret: 'CLIENTSECRET',
11
+ url_options: { port: '3000', protocol: 'http://', host: 'example.com' }
12
+ }
13
+ end
14
+
15
+ before do
16
+ stub_configuration
17
+ end
18
+
19
+ after do
20
+ unstub_configuration
21
+ end
22
+
23
+ describe 'simple properties' do
24
+ subject { provider }
25
+
26
+ its(:name) { is_expected.to eq('Google Drive') }
27
+ its(:key) { is_expected.to eq('google_drive') }
28
+ its(:icon) { is_expected.to eq('google-plus-sign') }
29
+ end
30
+
31
+ describe '#validate_config' do
32
+ it 'raises and error with an incomplete configuration' do
33
+ expect { BrowseEverything::Driver::GoogleDrive.new({}) }.to raise_error(BrowseEverything::InitializationError)
34
+ end
35
+
36
+ it 'raises and error with a configuration without a client secret' do
37
+ expect { BrowseEverything::Driver::GoogleDrive.new(client_id: 'test-client-id') }.to raise_error(BrowseEverything::InitializationError)
38
+ end
39
+ end
40
+
41
+ context 'without valid credentials' do
42
+ let(:driver) { described_class.new(provider_yml) }
43
+
44
+ describe '#token=' do
45
+ let(:value) { 'test' }
46
+
47
+ it 'restores the credentials' do
48
+ allow(driver).to receive(:restore_credentials)
49
+ driver.token = value
50
+ expect(driver).to have_received(:restore_credentials).with('test')
51
+ end
52
+
53
+ context 'when set to a Hash' do
54
+ let(:value) { { 'access_token' => 'test' } }
55
+
56
+ before do
57
+ driver.token = value
58
+ end
59
+
60
+ it 'sets the access token value' do
61
+ expect(driver.token).to be_a String
62
+ expect(driver.token).to eq 'test'
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ context 'with a valid connection' do
69
+ let(:driver) { described_class.new(provider_yml) }
70
+
71
+ before do
72
+ driver.connect({ code: 'code' }, {})
73
+ end
74
+
75
+ describe '#authorized?' do
76
+ it 'is authorized' do
77
+ expect(driver.authorized?).to be true
78
+ end
79
+ end
80
+
81
+ describe '#contents' do
82
+ subject(:contents) { driver.contents.to_a }
83
+
84
+ let(:drive_service_class) { class_double(Google::Apis::DriveV3::DriveService).as_stubbed_const(transfer_nested_constants: true) }
85
+ let(:drive_service) { instance_double(Google::Apis::DriveV3::DriveService) }
86
+ let(:file_list) { instance_double(Google::Apis::DriveV3::FileList) }
87
+ let(:file1) { instance_double(Google::Apis::DriveV3::File) }
88
+ let(:file2) { instance_double(Google::Apis::DriveV3::File) }
89
+ let(:files) { [file1, file2] }
90
+
91
+ before do
92
+ allow(file1).to receive(:id).and_return('asset-id2')
93
+ allow(file2).to receive(:id).and_return('directory-id1')
94
+ allow(file1).to receive(:name).and_return('asset-name2.pdf')
95
+ allow(file2).to receive(:name).and_return('directory-name1')
96
+ allow(file1).to receive(:size).and_return('891764')
97
+ allow(file2).to receive(:size).and_return('0')
98
+ allow(file1).to receive(:modified_time).and_return(Time.current)
99
+ allow(file2).to receive(:modified_time).and_return(Time.current)
100
+ allow(file1).to receive(:mime_type).and_return('application/pdf')
101
+ allow(file2).to receive(:mime_type).and_return('application/vnd.google-apps.folder')
102
+ allow(file_list).to receive(:files).and_return(files)
103
+ allow(file_list).to receive(:next_page_token).and_return(nil)
104
+ allow(drive_service).to receive(:list_files).and_yield(file_list, nil)
105
+ allow(drive_service).to receive(:batch).and_yield(drive_service)
106
+ allow(drive_service).to receive(:authorization=)
107
+ allow(drive_service).to receive(:tap).and_yield(drive_service).and_return(drive_service)
108
+ allow(drive_service_class).to receive(:new).and_return(drive_service)
109
+ end
110
+
111
+ it 'retrieves files' do
112
+ expect(contents).not_to be_empty
113
+
114
+ expect(contents.first).to be_a BrowseEverything::FileEntry
115
+ expect(contents.first.location).to eq 'google_drive:directory-id1'
116
+ expect(contents.first.mtime).to be_a Time
117
+ expect(contents.first.name).to eq 'directory-name1'
118
+ expect(contents.first.size).to eq 0
119
+ expect(contents.first.type).to eq 'directory'
120
+
121
+ expect(contents.last).to be_a BrowseEverything::FileEntry
122
+ expect(contents.last.location).to eq 'google_drive:asset-id2'
123
+ expect(contents.last.mtime).to be_a Time
124
+ expect(contents.last.name).to eq 'asset-name2.pdf'
125
+ expect(contents.last.size).to eq 891764
126
+ expect(contents.last.type).to eq 'application/pdf'
127
+ end
128
+
129
+ context 'when an error is encountered while authenticating' do
130
+ before do
131
+ allow(drive_service).to receive(:list_files).and_yield(file_list, Google::Apis::Error.new('test error'))
132
+ end
133
+
134
+ it 'raises an exception' do
135
+ expect { driver.contents.to_a }.to raise_error(Google::Apis::Error, 'test error')
136
+ end
137
+ end
138
+ end
139
+
140
+ describe '#link_for' do
141
+ subject(:link) { driver.link_for('asset-id2') }
142
+
143
+ it 'generates the link for a Google Drive asset' do
144
+ expect(link).to be_an Array
145
+ expect(link.first).to eq 'https://www.googleapis.com/drive/v3/files/asset-id2?alt=media'
146
+ expect(link.last).to be_a Hash
147
+ expect(link.last).to include auth_header: { 'Authorization' => 'Bearer access-token' }
148
+ expect(link.last).to include :expires
149
+ expect(link.last).to include file_name: 'asset-name2.pdf'
150
+ expect(link.last).to include file_size: 0
151
+ end
152
+ end
153
+
154
+ describe '#auth_link' do
155
+ subject(:uri) { driver.auth_link }
156
+
157
+ it 'exposes the authorization endpoint URI' do
158
+ expect(uri).to be_a Addressable::URI
159
+ expect(uri.to_s).to eq 'https://accounts.google.com/o/oauth2/auth?access_type=offline&approval_prompt=force&client_id=CLIENTID&include_granted_scopes=true&redirect_uri=http://example.com:3000/browse/connect&response_type=code&scope=https://www.googleapis.com/auth/drive'
160
+ end
161
+ end
162
+
163
+ describe '#drive' do
164
+ subject(:drive) { driver.drive_service }
165
+
166
+ it 'exposes the Google Drive API client' do
167
+ expect(drive).to be_a Google::Apis::DriveV3::DriveService
168
+ end
169
+ end
170
+ end
171
+ end
@@ -1,11 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  include BrowserConfigHelper
2
4
 
3
- describe BrowseEverything::Driver::FileSystem do
4
- before(:all) { stub_configuration }
5
- after(:all) { unstub_configuration }
5
+ describe BrowseEverything::Driver::S3 do
6
+ subject { provider }
7
+
6
8
  let(:browser) { BrowseEverything::Browser.new(url_options) }
7
9
  let(:provider) { browser.providers['s3'] }
8
- subject { provider }
10
+
11
+ before do
12
+ stub_configuration
13
+ end
14
+
15
+ after do
16
+ unstub_configuration
17
+ end
9
18
 
10
19
  describe 'defaults' do
11
20
  its(:icon) { is_expected.to eq('amazon') }
@@ -14,34 +23,34 @@ describe BrowseEverything::Driver::FileSystem do
14
23
 
15
24
  describe 'configuration' do
16
25
  it '#validate_config' do
17
- expect { BrowseEverything::Driver::S3.new({}) }.to raise_error(BrowseEverything::InitializationError)
26
+ expect { described_class.new({}) }.to raise_error(BrowseEverything::InitializationError)
18
27
  end
19
28
 
20
29
  it 'rejects app_key if app_secret is missing' do
21
- expect { BrowseEverything::Driver::S3.new(bucket: 'bucket', app_key: 'APP_KEY') }.to raise_error(BrowseEverything::InitializationError)
30
+ expect { described_class.new(bucket: 'bucket', app_key: 'APP_KEY') }.to raise_error(BrowseEverything::InitializationError)
22
31
  end
23
32
 
24
33
  it 'rejects app_secret if app_key is missing' do
25
- expect { BrowseEverything::Driver::S3.new(bucket: 'bucket', app_secret: 'APP_SECRET') }.to raise_error(BrowseEverything::InitializationError)
34
+ expect { described_class.new(bucket: 'bucket', app_secret: 'APP_SECRET') }.to raise_error(BrowseEverything::InitializationError)
26
35
  end
27
36
 
28
37
  it 'accepts app_key and app_secret together' do
29
- expect { BrowseEverything::Driver::S3.new(bucket: 'bucket', app_key: 'APP_KEY', app_secret: 'APP_SECRET') }.not_to raise_error
38
+ expect { described_class.new(bucket: 'bucket', app_key: 'APP_KEY', app_secret: 'APP_SECRET') }.not_to raise_error
30
39
  end
31
40
 
32
41
  it 'rejects an invalid response type' do
33
- expect { BrowseEverything::Driver::S3.new(bucket: 'bucket', response_type: :foo) }.to raise_error(BrowseEverything::InitializationError)
42
+ expect { described_class.new(bucket: 'bucket', response_type: :foo) }.to raise_error(BrowseEverything::InitializationError)
34
43
  end
35
44
 
36
45
  it 'deprecates :signed_url' do
37
- driver = BrowseEverything::Driver::S3.new(bucket: 'bucket', signed_url: false)
46
+ driver = described_class.new(bucket: 'bucket', signed_url: false)
38
47
  expect(driver.config).not_to have_key(:signed_url)
39
48
  expect(driver.config[:response_type]).to eq(:public_url)
40
49
  end
41
50
  end
42
51
 
43
52
  describe '#contents' do
44
- context 'root directory' do
53
+ context 'when in a root directory' do
45
54
  before do
46
55
  Aws.config[:s3] = {
47
56
  stub_responses: {
@@ -51,19 +60,22 @@ describe BrowseEverything::Driver::FileSystem do
51
60
  end
52
61
 
53
62
  let(:contents) { provider.contents('') }
54
- context '[0]' do
63
+
64
+ context 'with a single asset' do
55
65
  subject { contents[0] }
66
+
56
67
  its(:name) { is_expected.to eq('bar') }
57
68
  specify { is_expected.to be_container }
58
69
  end
59
- context '[1]' do
70
+ context 'with two assets' do
60
71
  subject { contents[1] }
72
+
61
73
  its(:name) { is_expected.to eq('foo') }
62
74
  specify { is_expected.to be_container }
63
75
  end
64
76
  end
65
77
 
66
- context 'subdirectory' do
78
+ context 'when in a subdirectory' do
67
79
  before do
68
80
  Aws.config[:s3] = {
69
81
  stub_responses: {
@@ -81,21 +93,25 @@ describe BrowseEverything::Driver::FileSystem do
81
93
  end
82
94
 
83
95
  let(:contents) { provider.contents('foo/') }
84
- context '[0]' do
96
+
97
+ context 'with a single directory' do
85
98
  subject { contents[0] }
99
+
86
100
  its(:name) { is_expected.to eq('..') }
87
101
  specify { is_expected.to be_container }
88
102
  end
89
- context '[1]' do
103
+ context 'with a JPEG asset' do
90
104
  subject { contents[1] }
105
+
91
106
  its(:name) { is_expected.to eq('baz.jpg') }
92
107
  its(:location) { is_expected.to eq('s3:foo/baz.jpg') }
93
108
  its(:type) { is_expected.to eq('image/jpeg') }
94
109
  its(:size) { is_expected.to eq(52645) }
95
110
  specify { is_expected.not_to be_container }
96
111
  end
97
- context '[2]' do
112
+ context 'with a PNG asset' do
98
113
  subject { contents[2] }
114
+
99
115
  its(:name) { is_expected.to eq('quux.png') }
100
116
  its(:location) { is_expected.to eq('s3:foo/quux.png') }
101
117
  its(:type) { is_expected.to eq('image/png') }
@@ -103,8 +119,9 @@ describe BrowseEverything::Driver::FileSystem do
103
119
  specify { is_expected.not_to be_container }
104
120
  end
105
121
 
106
- context '#link_for' do
122
+ context 'when retrieving the link for an asset' do
107
123
  subject { contents[2] }
124
+
108
125
  before do
109
126
  object = instance_double(Aws::S3::Object)
110
127
  allow(object).to receive(:presigned_url).and_return('https://s3.amazonaws.com/presigned_url')
@@ -116,17 +133,17 @@ describe BrowseEverything::Driver::FileSystem do
116
133
 
117
134
  it ':signed_url' do
118
135
  provider.config[:response_type] = :signed_url
119
- expect(provider.link_for('foo/quux.png')).to eq('https://s3.amazonaws.com/presigned_url')
136
+ expect(provider.link_for('foo/quux.png')).to eq ["https://s3.amazonaws.com/presigned_url", {:file_name=>"quux.png", :expires=>14400}]
120
137
  end
121
138
 
122
139
  it ':public_url' do
123
140
  provider.config[:response_type] = :public_url
124
- expect(provider.link_for('foo/quux.png')).to eq('https://s3.amazonaws.com/public_url')
141
+ expect(provider.link_for('foo/quux.png')).to eq ["https://s3.amazonaws.com/public_url", {:file_name=>"quux.png"}]
125
142
  end
126
143
 
127
144
  it ':s3_uri' do
128
145
  provider.config[:response_type] = :s3_uri
129
- expect(provider.link_for('foo/quux.png')).to eq('s3://s3.bucket/foo/quux.png')
146
+ expect(provider.link_for('foo/quux.png')).to eq ['s3://s3.bucket/foo/quux.png', {:file_name=>"quux.png"}]
130
147
  end
131
148
  end
132
149
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe BrowseEverything::Driver do
4
+
5
+ let(:my_driver) do
6
+ MyDriver.new
7
+ end
8
+
9
+ before do
10
+ class MyDriver
11
+ include BrowseEverything::Driver
12
+
13
+ def get_sorter
14
+ sorter
15
+ end
16
+ end
17
+ end
18
+
19
+ describe '#sorter' do
20
+ it 'defaults to nil' do
21
+ expect(BrowseEverything::Driver.sorter).to be nil
22
+ end
23
+ end
24
+
25
+ describe '#sorter=' do
26
+ let(:new_sorter) do
27
+ lambda { |files| }
28
+ end
29
+
30
+ before do
31
+ BrowseEverything::Driver.sorter = new_sorter
32
+ end
33
+
34
+ it 'mutates the sorter from the initializer' do
35
+ expect(my_driver.get_sorter).to eq new_sorter
36
+ end
37
+ end
38
+ end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  describe BrowseEverything::FileEntry do
2
- let(:mtime) { Time.now }
4
+ let(:mtime) { Time.current }
3
5
 
4
6
  describe 'regular file' do
5
7
  subject do
@@ -42,6 +44,7 @@ describe BrowseEverything::FileEntry do
42
44
  '..', '', mtime, true
43
45
  )
44
46
  end
47
+
45
48
  it { is_expected.to be_container }
46
49
  it { is_expected.to be_relative_parent_path }
47
50
 
@@ -0,0 +1,200 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe BrowseEverything::Retriever, vcr: { cassette_name: 'retriever', record: :none } do
4
+ let(:retriever) { described_class.new }
5
+ let(:datafile) { File.expand_path('../../fixtures/file_system/file_1.pdf', __dir__) }
6
+ let(:datafile_with_spaces) { File.expand_path('../../fixtures/file_system/file 1.pdf', __dir__) }
7
+ let(:data) { File.open(datafile, 'rb', &:read) }
8
+ let(:data_with_spaces) { File.open(datafile_with_spaces, 'rb', &:read) }
9
+ let(:size) { File.size(datafile) }
10
+
11
+ describe '#get_file_size' do
12
+ subject(:file_size) { retriever.file_size(options) }
13
+ let(:url) { URI.parse("file://#{datafile}") }
14
+ let(:headers) { [] }
15
+ let(:file_size) { 0 }
16
+ let(:options) do
17
+ {
18
+ url: url,
19
+ headers: headers,
20
+ file_size: file_size
21
+ }.with_indifferent_access
22
+ end
23
+
24
+ it 'calculates or retrieves the size of a file' do
25
+ retriever.retrieve(options) do |chunk, retrieved, total|
26
+ expect(total).to eq 2256
27
+ end
28
+ end
29
+
30
+ context "when retrieving a resource from a cloud storage provider" do
31
+ let(:url) { URI.parse("https://drive.google.com/uc?id=id&export=download") }
32
+
33
+ it 'calculates or retrieves the size of a file' do
34
+ retriever.retrieve(options) do |chunk, retrieved, total|
35
+ expect(total).to eq 1234
36
+ end
37
+ end
38
+ end
39
+
40
+ context "when retrieving a resource with an unsupported protocol" do
41
+ let(:url) { URI.parse("ftp://invalid") }
42
+
43
+ it "raises an error" do
44
+ expect { retriever.retrieve(options) {|c, r, t|} }.to raise_error(URI::BadURIError, "Unknown URI scheme: ftp")
45
+ end
46
+ end
47
+ end
48
+
49
+ context 'with a non-URI' do
50
+ let(:spec) do
51
+ {
52
+ '0' => {
53
+ 'url' => '/some/dir/file.pdf',
54
+ 'file_name' => 'file.pdf',
55
+ 'file_size' => size.to_s
56
+ }
57
+ }
58
+ end
59
+
60
+ describe '#retrieve' do
61
+ it 'raises an error' do
62
+ expect { retriever.retrieve(spec['0']) }.to raise_error(URI::BadURIError)
63
+ end
64
+ end
65
+ end
66
+
67
+ context 'when retrieving using the HTTP' do
68
+ let(:expiry_time) { (Time.current + 3600).xmlschema }
69
+ let(:spec) do
70
+ {
71
+ '0' => {
72
+ 'url' => 'https://retrieve.cloud.example.com/some/dir/file.pdf',
73
+ 'auth_header' => { 'Authorization' => 'Bearer ya29.kQCEAHj1bwFXr2AuGQJmSGRWQXpacmmYZs4kzCiXns3d6H1ZpIDWmdM8' },
74
+ 'expires' => expiry_time,
75
+ 'file_name' => 'file.pdf',
76
+ 'file_size' => size.to_s
77
+ }
78
+ }
79
+ end
80
+
81
+ context 'when retrieving data using chunked-encoded streams' do
82
+ it 'content' do
83
+ content = +''
84
+ retriever.retrieve(spec['0']) { |chunk, _retrieved, _total| content << chunk }
85
+ expect(content).to eq(data)
86
+ end
87
+
88
+ it 'callbacks' do
89
+ expect { |block| retriever.retrieve(spec['0'], &block) }.to yield_with_args(data, data.length, data.length)
90
+ end
91
+ end
92
+
93
+ context 'when downloading content' do
94
+ it 'content' do
95
+ file = retriever.download(spec['0'])
96
+ expect(File.open(file, 'rb', &:read)).to eq(data)
97
+ end
98
+
99
+ it 'callbacks' do
100
+ expect { |block| retriever.download(spec['0'], &block) }.to yield_with_args(String, data.length, data.length)
101
+ end
102
+ end
103
+
104
+ context 'when downloading content and a server error occurs' do
105
+ let(:spec) do
106
+ {
107
+ '0' => {
108
+ 'url' => 'https://retrieve.cloud.example.com/some/dir/file_error.pdf',
109
+ 'auth_header' => { 'Authorization' => 'Bearer ya29.kQCEAHj1bwFXr2AuGQJmSGRWQXpacmmYZs4kzCiXns3d6H1ZpIDWmdM8' },
110
+ 'expires' => expiry_time,
111
+ 'file_name' => 'file.pdf',
112
+ 'file_size' => size.to_s
113
+ }
114
+ }
115
+ end
116
+ let(:download_options) { spec['0'] }
117
+
118
+ it 'raises an exception' do
119
+ expect { retriever.download(download_options) }.to raise_error(BrowseEverything::DownloadError, /BrowseEverything::Retriever: Failed to download/)
120
+ end
121
+ end
122
+ end
123
+
124
+ context 'when retrieving file content' do
125
+ let(:spec) do
126
+ {
127
+ '0' => {
128
+ 'url' => "file://#{datafile}",
129
+ 'file_name' => 'file.pdf',
130
+ 'file_size' => size.to_s
131
+ },
132
+ '1' => {
133
+ 'url' => "file://#{datafile_with_spaces}",
134
+ 'file_name' => 'file.pdf',
135
+ 'file_size' => size.to_s
136
+ }
137
+ }
138
+ end
139
+
140
+ context 'when retrieving data using chunked-encoded streams' do
141
+ it 'content' do
142
+ content = +''
143
+ retriever.retrieve(spec['0']) { |chunk, _retrieved, _total| content << chunk }
144
+ expect(content).to eq(data)
145
+ end
146
+
147
+ it 'content with spaces' do
148
+ content = +''
149
+ retriever.retrieve(spec['1']) { |chunk, _retrieved, _total| content << chunk }
150
+ expect(content).to eq(data_with_spaces)
151
+ end
152
+
153
+ it 'callbacks' do
154
+ expect { |block| retriever.retrieve(spec['0'], &block) }.to yield_with_args(data, data.length, data.length)
155
+ end
156
+ end
157
+
158
+ context 'when downloading content' do
159
+ it 'content' do
160
+ file = retriever.download(spec['0'])
161
+ expect(File.open(file, 'rb', &:read)).to eq(data)
162
+ end
163
+
164
+ it 'callbacks' do
165
+ expect { |block| retriever.download(spec['0'], &block) }.to yield_with_args(String, data.length, data.length)
166
+ end
167
+ end
168
+ end
169
+
170
+ context '.can_retrieve?' do
171
+ let(:expiry_time) { (Time.current + 3600).xmlschema }
172
+ let(:spec) do
173
+ {
174
+ '0' => {
175
+ 'url' => url,
176
+ 'auth_header' => { 'Authorization' => 'Bearer ya29.kQCEAHj1bwFXr2AuGQJmSGRWQXpacmmYZs4kzCiXns3d6H1ZpIDWmdM8' },
177
+ 'expires' => expiry_time,
178
+ 'file_name' => 'file.pdf',
179
+ 'file_size' => '64134'
180
+ }
181
+ }
182
+ end
183
+
184
+ context 'can retrieve' do
185
+ let(:url) { 'https://retrieve.cloud.example.com/some/dir/can_retrieve.pdf' }
186
+
187
+ it 'says it can' do
188
+ expect(described_class.can_retrieve?(url)).to be_truthy
189
+ end
190
+ end
191
+
192
+ context 'cannot retrieve' do
193
+ let(:url) { 'https://retrieve.cloud.example.com/some/dir/cannot_retrieve.pdf' }
194
+
195
+ it 'says it cannot' do
196
+ expect(described_class.can_retrieve?(url)).to be_falsey
197
+ end
198
+ end
199
+ end
200
+ end