browse-everything 0.16.1 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +3 -0
  3. data/CODE_OF_CONDUCT.md +36 -0
  4. data/CONTRIBUTING.md +21 -19
  5. data/Gemfile +1 -0
  6. data/README.md +29 -9
  7. data/SUPPORT.md +5 -0
  8. data/app/controllers/browse_everything_controller.rb +7 -3
  9. data/app/views/browse_everything/_files.html.erb +14 -6
  10. data/app/views/browse_everything/index.html.erb +7 -1
  11. data/browse-everything.gemspec +5 -5
  12. data/lib/browse_everything/driver/base.rb +1 -1
  13. data/lib/browse_everything/driver/box.rb +4 -14
  14. data/lib/browse_everything/driver/dropbox.rb +34 -11
  15. data/lib/browse_everything/driver/google_drive.rb +2 -2
  16. data/lib/browse_everything/driver/s3.rb +8 -14
  17. data/lib/browse_everything/retriever.rb +4 -5
  18. data/lib/browse_everything/version.rb +1 -1
  19. data/lib/generators/browse_everything/templates/browse_everything_providers.yml.example +2 -2
  20. data/spec/controllers/browse_everything_controller_spec.rb +8 -1
  21. data/spec/lib/browse_everything/driver/box_spec.rb +114 -34
  22. data/spec/lib/browse_everything/driver/dropbox_spec.rb +53 -5
  23. data/spec/lib/browse_everything/driver/google_drive_spec.rb +41 -4
  24. data/spec/lib/browse_everything/driver/s3_spec.rb +27 -18
  25. data/spec/lib/browse_everything/retriever_spec.rb +65 -2
  26. data/spec/spec_helper.rb +4 -9
  27. data/spec/support/capybara.rb +14 -10
  28. metadata +31 -37
  29. data/spec/fixtures/vcr_cassettes/box.yml +0 -498
  30. data/spec/fixtures/vcr_cassettes/dropbox.yml +0 -282
  31. data/spec/fixtures/vcr_cassettes/google_drive.yml +0 -331
  32. data/spec/fixtures/vcr_cassettes/retriever.yml +0 -170
@@ -120,7 +120,7 @@ module BrowseEverything
120
120
 
121
121
  # Provides a URL for authorizing against Google Drive
122
122
  # @return [String] the URL
123
- def auth_link
123
+ def auth_link(*_args)
124
124
  Addressable::URI.parse(authorizer.get_authorization_url)
125
125
  end
126
126
 
@@ -175,7 +175,7 @@ module BrowseEverything
175
175
  # @param params [Hash] HTTP response passed to the OAuth callback
176
176
  # @param _data [Object,nil] an unused parameter
177
177
  # @return [String] a new access token
178
- def connect(params, _data)
178
+ def connect(params, _data, _url_options)
179
179
  @code = params[:code]
180
180
  authorize!
181
181
  end
@@ -8,7 +8,7 @@ module BrowseEverything
8
8
  class S3 < Base
9
9
  DEFAULTS = { response_type: :signed_url, expires_in: 14400 }.freeze
10
10
  RESPONSE_TYPES = %i[signed_url public_url s3_uri].freeze
11
- CONFIG_KEYS = %i[bucket].freeze
11
+ CONFIG_KEYS = %i[bucket region].freeze
12
12
 
13
13
  class << self
14
14
  attr_accessor :authentication_klass
@@ -21,9 +21,10 @@ module BrowseEverything
21
21
  attr_reader :entries
22
22
 
23
23
  def initialize(config, *args)
24
- if config.key?(:signed_url) && config.delete(:signed_url) == false
25
- warn '[DEPRECATION] Amazon S3 driver: `:signed_url` is deprecated. Please use `:response_type` instead.'
26
- config[:response_type] = :public_url
24
+ if config.key?(:signed_url)
25
+ warn '[DEPRECATION] Amazon S3 driver: `:signed_url` is deprecated. Please use `response_type :signed_url` instead.'
26
+ response_type = config.delete(:signed_url) ? :signed_url : :public_url
27
+ config[:response_type] = response_type
27
28
  end
28
29
  merged_config = DEFAULTS.merge(config)
29
30
  self.class.authentication_klass ||= self.class.default_authentication_klass
@@ -45,7 +46,8 @@ module BrowseEverything
45
46
  # @return [Array<BrowseEverything::FileEntry>]
46
47
  def contents(path = '')
47
48
  path = File.join(path, '') unless path.empty?
48
- init_entries(path)
49
+ @entries = []
50
+
49
51
  generate_listing(path)
50
52
  @sorter.call(@entries)
51
53
  end
@@ -111,15 +113,6 @@ module BrowseEverything
111
113
  @client ||= authenticate
112
114
  end
113
115
 
114
- def init_entries(path)
115
- @entries = if path.empty?
116
- []
117
- else
118
- [BrowseEverything::FileEntry.new(Pathname(path).join('..').to_s, '', '..',
119
- 0, Time.current, true)]
120
- end
121
- end
122
-
123
116
  def entry_for(name, size, date, dir)
124
117
  BrowseEverything::FileEntry.new(name, [key, name].join(':'), File.basename(name), size, date, dir)
125
118
  end
@@ -138,6 +131,7 @@ module BrowseEverything
138
131
  end
139
132
 
140
133
  def generate_listing(path)
134
+ client
141
135
  listing = client.list_objects(bucket: config[:bucket], delimiter: '/', prefix: full_path(path))
142
136
  add_directories(listing)
143
137
  add_files(listing, path)
@@ -70,7 +70,7 @@ module BrowseEverything
70
70
  end
71
71
 
72
72
  download_options = extract_download_options(options)
73
- url = download_options.fetch(:url)
73
+ url = download_options[:url]
74
74
 
75
75
  case url.scheme
76
76
  when 'file'
@@ -91,8 +91,7 @@ module BrowseEverything
91
91
  url = options.fetch('url')
92
92
 
93
93
  # This avoids the potential for a KeyError
94
- headers = options.fetch('auth_header', {}) || {}
95
- headers.each_pair { |k, v| headers[k] = v.tr('+', ' ') }
94
+ headers = options.fetch('headers', {}) || {}
96
95
 
97
96
  file_size_value = options.fetch('file_size', 0)
98
97
  file_size = file_size_value.to_i
@@ -131,9 +130,9 @@ module BrowseEverything
131
130
  url = options.fetch(:url)
132
131
  retrieved = 0
133
132
 
134
- request = Typhoeus::Request.new(url.to_s, headers: headers)
133
+ request = Typhoeus::Request.new(url.to_s, method: :get, headers: headers)
135
134
  request.on_headers do |response|
136
- raise DownloadError.new("#{self.class}: Failed to download #{url}", response) unless response.code == 200
135
+ raise DownloadError.new("#{self.class}: Failed to download #{url}: Status Code: #{response.code}", response) unless response.code == 200
137
136
  end
138
137
  request.on_body do |chunk|
139
138
  retrieved += chunk.bytesize
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BrowseEverything
4
- VERSION = '0.16.1'
4
+ VERSION = '1.0.0.rc1'
5
5
  end
@@ -3,8 +3,8 @@
3
3
  # The file_system provider can be a path to any directory on the server where your application is running.
4
4
  #
5
5
  # dropbox:
6
- # app_key: YOUR_DROPBOX_APP_KEY
7
- # app_secret: YOUR_DROPBOX_APP_SECRET
6
+ # client_id: YOUR_DROPBOX_APP_KEY
7
+ # client_secret: YOUR_DROPBOX_APP_SECRET
8
8
  # box:
9
9
  # client_id: YOUR_BOX_CLIENT_ID
10
10
  # client_secret: YOUR_BOX_CLIENT_SECRET
@@ -15,6 +15,13 @@ RSpec.describe BrowseEverythingController, type: :controller do
15
15
 
16
16
  describe '#auth' do
17
17
  let(:provider_session) { instance_double(BrowseEverythingSession::ProviderSession) }
18
+ let(:connector_response_url_options) do
19
+ {
20
+ protocol: 'http://',
21
+ host: 'test.host',
22
+ port: 80
23
+ }
24
+ end
18
25
 
19
26
  before do
20
27
  allow(controller).to receive(:params).and_return('code' => 'test-code')
@@ -24,7 +31,7 @@ RSpec.describe BrowseEverythingController, type: :controller do
24
31
  end
25
32
 
26
33
  it 'retrieves the authorization code from the parameters' do
27
- expect(provider).to have_received(:connect).with({ 'code' => 'test-code' }, nil)
34
+ expect(provider).to have_received(:connect).with({ 'code' => 'test-code' }, nil, connector_response_url_options)
28
35
  end
29
36
  end
30
37
 
@@ -2,7 +2,9 @@
2
2
 
3
3
  include BrowserConfigHelper
4
4
 
5
- describe BrowseEverything::Driver::Box, vcr: { cassette_name: 'box', record: :none } do
5
+ describe BrowseEverything::Driver::Box do
6
+ subject { provider }
7
+
6
8
  let(:browser) { BrowseEverything::Browser.new(url_options) }
7
9
  let(:provider) { browser.providers['box'] }
8
10
  let(:auth_params) do
@@ -11,31 +13,27 @@ describe BrowseEverything::Driver::Box, vcr: { cassette_name: 'box', record: :no
11
13
  'state' => 'box'
12
14
  }
13
15
  end
14
-
15
- # To re-record the VCR responses, you will need to substitute the 'token' and 'refresh_token' values with the
16
- # actual tokens returned from Box.
17
- #
18
- # The easiest way to do this is output @token to the Rails log from the box_client method:
19
- #
20
- # def box_client
21
- # Rails.logger.info(@token)
22
- # [...]
23
- #
24
- # Make sure you have the internal_test_app built and have copied your client id and secret to the yaml config file.
25
- # With the test app running, you can authenticate to your Box account to browse files, and should see the
26
- # tokens listed in the Rails log. Once you have copied the new tokens, the spec test will run and use your tokens
27
- # to authenticate to Box. To record the new responses replace `record: :none` with `record: :all`.
28
16
  let(:token) do
29
17
  {
30
18
  'token' => 'TOKEN',
31
19
  'refresh_token' => 'REFRESH_TOKEN'
32
20
  }
33
21
  end
34
-
35
- subject { provider }
22
+ let(:oauth_response_body) { '{"access_token":"TOKEN","expires_in":3762,"restricted_to":[],"refresh_token":"REFRESH_TOKEN","token_type":"bearer"}' }
36
23
 
37
24
  before do
38
25
  stub_configuration
26
+
27
+ stub_request(
28
+ :post,
29
+ 'https://www.box.com/api/oauth2/token'
30
+ ).to_return(
31
+ body: oauth_response_body,
32
+ status: 200,
33
+ headers: {
34
+ 'Content-Type' => 'application/json'
35
+ }
36
+ )
39
37
  end
40
38
 
41
39
  after do
@@ -48,7 +46,7 @@ describe BrowseEverything::Driver::Box, vcr: { cassette_name: 'box', record: :no
48
46
 
49
47
  describe '#validate_config' do
50
48
  it 'raises and error with an incomplete configuration' do
51
- expect { BrowseEverything::Driver::Box.new({}) }.to raise_error(BrowseEverything::InitializationError)
49
+ expect { described_class.new({}) }.to raise_error(BrowseEverything::InitializationError)
52
50
  end
53
51
  end
54
52
 
@@ -92,14 +90,42 @@ describe BrowseEverything::Driver::Box, vcr: { cassette_name: 'box', record: :no
92
90
  end
93
91
 
94
92
  it 'registers new tokens' do
95
- provider.connect(auth_params, 'data')
93
+ provider.connect(auth_params, 'data', nil)
96
94
  expect(provider).to have_received(:register_access_token).with(kind_of(OAuth2::AccessToken))
97
95
  end
98
96
  end
99
97
 
100
98
  describe '#contents' do
101
- before { provider.token = token }
99
+ let(:folders_response_body) do
100
+ '{"type":"folder","id":"0","sequence_id":null,"etag":null,"name":"All Files","created_at":null,"modified_at":null,"description":"","size":28747877,"path_collection":{"total_count":0,"entries":[]},"created_by":{"type":"user","id":"","name":"","login":""},"modified_by":{"type":"user","id":"225383863","name":"ADAM GARNER WEAD","login":"agw13@psu.edu"},"trashed_at":null,"purged_at":null,"content_created_at":null,"content_modified_at":null,"owned_by":{"type":"user","id":"225383863","name":"ADAM GARNER WEAD","login":"agw13@psu.edu"},"shared_link":null,"folder_upload_email":null,"parent":null,"item_status":"active","item_collection":{"total_count":13,"entries":[{"type":"folder","id":"20375782799","sequence_id":"0","etag":"0","name":"A very looooooooooooong box folder, why so loooooooong Lets make it even longer to show how far it gets sent to the side"},{"type":"folder","id":"2571160559","sequence_id":"0","etag":"0","name":"Apps Team - Shared"},{"type":"folder","id":"20194542723","sequence_id":"0","etag":"0","name":"DSRD - W Pattee 3"},{"type":"folder","id":"20284062015","sequence_id":"0","etag":"0","name":"My Box Notes"},{"type":"folder","id":"11305958926","sequence_id":"0","etag":"0","name":"PCDM-Sufia"},{"type":"folder","id":"4227519189","sequence_id":"0","etag":"0","name":"refactor"},{"type":"folder","id":"2459961273","sequence_id":"0","etag":"0","name":"SaS - Development Team"},{"type":"folder","id":"3399219062","sequence_id":"0","etag":"0","name":"Scholarsphere - Migration"},{"type":"folder","id":"1168461187","sequence_id":"0","etag":"0","name":"test"},{"type":"folder","id":"3055812547","sequence_id":"0","etag":"0","name":"UX Artifacts"},{"type":"file","id":"25581309763","file_version":{"type":"file_version","id":"23869158869","sha1":"4604bbe44fdcdd4afef3c666cf582e3773960954"},"sequence_id":"1","etag":"1","sha1":"4604bbe44fdcdd4afef3c666cf582e3773960954","name":"failed.tar.gz"},{"type":"file","id":"25588823531","file_version":{"type":"file_version","id":"23877641673","sha1":"abd18ce0a685a27b464fb05f27af5a84f9ec9be7"},"sequence_id":"1","etag":"1","sha1":"abd18ce0a685a27b464fb05f27af5a84f9ec9be7","name":"scholarsphere_5712md360.xml"},{"type":"file","id":"113711622968","file_version":{"type":"file_version","id":"122136107320","sha1":"da39a3ee5e6b4b0d3255bfef95601890afd80709"},"sequence_id":"0","etag":"0","sha1":"da39a3ee5e6b4b0d3255bfef95601890afd80709","name":"test.txt"}],"offset":0,"limit":100,"order":[{"by":"type","direction":"ASC"},{"by":"name","direction":"ASC"}]}}'
101
+ end
102
+ let(:folders_items_response_body) do
103
+ '{"total_count":13,"entries":[{"type":"folder","id":"20375782799","etag":"0","name":"A very looooooooooooong box folder, why so loooooooong Lets make it even longer to show how far it gets sent to the side","size":0,"created_at":"2017-03-01T04:15:15-08:00"},{"type":"folder","id":"2571160559","etag":"0","name":"Apps Team - Shared","size":1249,"created_at":"2014-10-15T13:00:29-07:00"},{"type":"folder","id":"20194542723","etag":"0","name":"DSRD - W Pattee 3","size":2949416,"created_at":"2017-02-27T08:17:21-08:00"},{"type":"folder","id":"20284062015","etag":"0","name":"My Box Notes","size":0,"created_at":"2017-02-28T08:52:26-08:00"},{"type":"folder","id":"11305958926","etag":"0","name":"PCDM-Sufia","size":650658,"created_at":"2016-09-14T09:14:25-07:00"},{"type":"folder","id":"4227519189","etag":"0","name":"refactor","size":8766,"created_at":"2015-08-14T07:53:56-07:00"},{"type":"folder","id":"2459961273","etag":"0","name":"SaS - Development Team","size":152720753,"created_at":"2014-09-17T13:39:31-07:00"},{"type":"folder","id":"3399219062","etag":"0","name":"Scholarsphere - Migration","size":270984,"created_at":"2015-04-07T13:17:51-07:00"},{"type":"folder","id":"1168461187","etag":"0","name":"test","size":20625445557,"created_at":"2013-09-19T12:57:59-07:00"},{"type":"folder","id":"3055812547","etag":"0","name":"UX Artifacts","size":3801994,"created_at":"2015-02-04T08:21:16-08:00"},{"type":"file","id":"25581309763","etag":"1","name":"failed.tar.gz","size":28650839,"created_at":"2015-01-29T05:18:43-08:00"},{"type":"file","id":"25588823531","etag":"1","name":"scholarsphere_5712md360.xml","size":97038,"created_at":"2015-01-29T08:38:44-08:00"},{"type":"file","id":"113711622968","etag":"0","name":"test.txt","size":0,"created_at":"2016-12-20T07:50:30-08:00"}],"offset":0,"limit":1000,"order":[{"by":"type","direction":"ASC"},{"by":"name","direction":"ASC"}]}'
104
+ end
105
+ before do
106
+ provider.token = token
107
+
108
+ stub_request(
109
+ :get, "https://api.box.com/2.0/folders/0"
110
+ ).to_return(
111
+ body: folders_response_body,
112
+ status: 200,
113
+ headers: {
114
+ 'Content-Type' => 'application/json'
115
+ }
116
+ )
117
+ stub_request(
118
+ :get, "https://api.box.com/2.0/folders/0/items?fields=name,size,created_at&limit=99999&offset=0"
119
+ ).to_return(
120
+ body: folders_items_response_body,
121
+ status: 200,
122
+ headers: {
123
+ 'Content-Type' => 'application/json'
124
+ }
125
+ )
126
+ end
102
127
 
128
+ # HERE
103
129
  context 'with files and folders in the root directory' do
104
130
  let(:root_directory) { provider.contents('') }
105
131
  let(:long_file) { root_directory[0] }
@@ -136,18 +162,36 @@ describe BrowseEverything::Driver::Box, vcr: { cassette_name: 'box', record: :no
136
162
  end
137
163
 
138
164
  context 'with files and folders in the SaS - Development Team directory' do
139
- let(:sas_directory) { provider.contents('2459961273') }
140
- let(:parent_directory) { sas_directory[0] }
141
- let(:apps_dir) { sas_directory[1] }
142
- let(:equipment) { sas_directory[12] }
143
-
144
- describe 'the first item' do
145
- subject { parent_directory }
165
+ let(:folder_id) { '2459961273' }
166
+ let(:sas_directory) { provider.contents(folder_id) }
167
+ let(:apps_dir) { sas_directory[0] }
168
+ let(:equipment) { sas_directory[11] }
169
+ let(:folders_response_body) do
170
+ '{"type":"folder","id":"2459961273","sequence_id":"0","etag":"0","name":"SaS - Development Team","created_at":"2014-09-17T13:39:31-07:00","modified_at":"2017-03-17T08:41:40-07:00","description":"","size":152720753,"path_collection":{"total_count":1,"entries":[{"type":"folder","id":"0","sequence_id":null,"etag":null,"name":"All Files"}]},"created_by":{"type":"user","id":"191882215","name":"Daniel Coughlin","login":"dmc186@psu.edu"},"modified_by":{"type":"user","id":"190902775","name":"CAROLYN A COLE","login":"cam156@psu.edu"},"trashed_at":null,"purged_at":null,"content_created_at":"2014-09-17T13:39:31-07:00","content_modified_at":"2017-03-17T08:41:40-07:00","owned_by":{"type":"user","id":"191882215","name":"Daniel Coughlin","login":"dmc186@psu.edu"},"shared_link":{"url":"https:\/\/psu.box.com\/s\/8hb3e06nthwld39ehlg5","download_url":null,"vanity_url":null,"effective_access":"collaborators","is_password_enabled":false,"unshared_at":null,"download_count":0,"preview_count":0,"access":"collaborators","permissions":{"can_download":true,"can_preview":true}},"folder_upload_email":null,"parent":null,"item_status":"active","item_collection":{"total_count":17,"entries":[{"type":"folder","id":"2459974427","sequence_id":"0","etag":"0","name":"Apps&Int"},{"type":"folder","id":"11217040834","sequence_id":"0","etag":"0","name":"Credentials"},{"type":"folder","id":"5662231069","sequence_id":"0","etag":"0","name":"DevOps Presentation"},{"type":"folder","id":"8648394509","sequence_id":"0","etag":"0","name":"old"},{"type":"folder","id":"12445061821","sequence_id":"0","etag":"0","name":"Ops Plan"},{"type":"file","id":"136917954839","file_version":{"type":"file_version","id":"158227338595","sha1":"2e2124c0d6589dd05d03a854b9c5c3a68bbc4501"},"sequence_id":"17","etag":"17","sha1":"2e2124c0d6589dd05d03a854b9c5c3a68bbc4501","name":"2-17 Upcoming ScholarSphere changes.boxnote"},{"type":"file","id":"75228871930","file_version":{"type":"file_version","id":"79668751214","sha1":"43cd4ac728cef7767dc132c26b181133d867d78f"},"sequence_id":"1","etag":"1","sha1":"43cd4ac728cef7767dc132c26b181133d867d78f","name":"99bottles (1).epub"},{"type":"file","id":"72329914177","file_version":{"type":"file_version","id":"76429987577","sha1":"a3310fdad0e6c9654b19d88b35318b8cdc246379"},"sequence_id":"2","etag":"2","sha1":"a3310fdad0e6c9654b19d88b35318b8cdc246379","name":"Development Team Projects and Milestones (not downloaded).xlsx"},{"type":"file","id":"74507436394","file_version":{"type":"file_version","id":"78854145518","sha1":"589ab64efb1ba49537074fd2c56e1570a24a47b5"},"sequence_id":"7","etag":"7","sha1":"589ab64efb1ba49537074fd2c56e1570a24a47b5","name":"Development Team Projects and Milestones - Editable.xlsx"},{"type":"file","id":"72324078305","file_version":{"type":"file_version","id":"76423451525","sha1":"42c0a5a34717cd17e315653643b61d23e94b7cd7"},"sequence_id":"3","etag":"3","sha1":"42c0a5a34717cd17e315653643b61d23e94b7cd7","name":"Development Team Projects and Milestones.xlsx"},{"type":"file","id":"106651625938","file_version":{"type":"file_version","id":"114805197783","sha1":"0c1c90a0186bd3934ce3877e092a228dc1568e93"},"sequence_id":"2","etag":"2","sha1":"0c1c90a0186bd3934ce3877e092a228dc1568e93","name":"Digital Scholarship and Repository Development (DSRD) Org Chart 2016.pdf"},{"type":"file","id":"76960974625","file_version":{"type":"file_version","id":"106919307872","sha1":"74608bfb532feff774502c6132eafe9acb1e5217"},"sequence_id":"162","etag":"162","sha1":"74608bfb532feff774502c6132eafe9acb1e5217","name":"Equipment.boxnote"},{"type":"file","id":"68362464289","file_version":{"type":"file_version","id":"101137863811","sha1":"40a0a3f98926874f1ee62fd522621a4408e0058c"},"sequence_id":"8","etag":"8","sha1":"40a0a3f98926874f1ee62fd522621a4408e0058c","name":"migrationDesign.pptx"},{"type":"file","id":"99028795390","file_version":{"type":"file_version","id":"107183805224","sha1":"7044bd7f0892f915b77b914430c2915161f942d9"},"sequence_id":"37","etag":"37","sha1":"7044bd7f0892f915b77b914430c2915161f942d9","name":"Onboarding.boxnote"},{"type":"file","id":"134865535934","file_version":{"type":"file_version","id":"143971901576","sha1":"32cc33b2cd5cc46f0a3fb924b4779cc387a81018"},"sequence_id":"1","etag":"1","sha1":"32cc33b2cd5cc46f0a3fb924b4779cc387a81018","name":"passenger-test.boxnote"},{"type":"file","id":"92102699797","file_version":{"type":"file_version","id":"98754405909","sha1":"053b762f6d5752c261ef7ff6a3d776853cdb2ca3"},"sequence_id":"1","etag":"1","sha1":"053b762f6d5752c261ef7ff6a3d776853cdb2ca3","name":"sas_development_libraries_mou_2016_08_01.docx"},{"type":"file","id":"22182934895","file_version":{"type":"file_version","id":"50126669941","sha1":"8ca396b5b26ff90d625ea3a49f0cc69ad844476b"},"sequence_id":"41","etag":"41","sha1":"8ca396b5b26ff90d625ea3a49f0cc69ad844476b","name":"TimeBox.xlsx"}],"offset":0,"limit":100,"order":[{"by":"type","direction":"ASC"},{"by":"name","direction":"ASC"}]}}'
171
+ end
172
+ let(:folders_items_response_body) do
173
+ '{"total_count":17,"entries":[{"type":"folder","id":"2459974427","etag":"0","name":"Apps&Int","size":14341346,"created_at":"2014-09-17T13:42:30-07:00"},{"type":"folder","id":"11217040834","etag":"0","name":"Credentials","size":12603,"created_at":"2016-09-07T08:37:23-07:00"},{"type":"folder","id":"5662231069","etag":"0","name":"DevOps Presentation","size":128158633,"created_at":"2015-12-07T05:51:08-08:00"},{"type":"folder","id":"8648394509","etag":"0","name":"old","size":117232,"created_at":"2016-06-30T08:03:46-07:00"},{"type":"folder","id":"12445061821","etag":"0","name":"Ops Plan","size":6181924,"created_at":"2016-11-17T06:52:58-08:00"},{"type":"file","id":"136917954839","etag":"17","name":"2-17 Upcoming ScholarSphere changes.boxnote","size":1768,"created_at":"2017-02-17T09:27:57-08:00"},{"type":"file","id":"75228871930","etag":"1","name":"99bottles (1).epub","size":1013573,"created_at":"2016-07-21T03:53:01-07:00"},{"type":"file","id":"72329914177","etag":"2","name":"Development Team Projects and Milestones (not downloaded).xlsx","size":20812,"created_at":"2016-06-30T09:10:36-07:00"},{"type":"file","id":"74507436394","etag":"7","name":"Development Team Projects and Milestones - Editable.xlsx","size":21001,"created_at":"2016-07-15T11:08:54-07:00"},{"type":"file","id":"72324078305","etag":"3","name":"Development Team Projects and Milestones.xlsx","size":22410,"created_at":"2016-06-30T08:31:56-07:00"},{"type":"file","id":"106651625938","etag":"2","name":"Digital Scholarship and Repository Development (DSRD) Org Chart 2016.pdf","size":17677,"created_at":"2016-11-30T08:27:58-08:00"},{"type":"file","id":"76960974625","etag":"162","name":"Equipment.boxnote","size":10140,"created_at":"2016-08-03T12:22:39-07:00"},{"type":"file","id":"68362464289","etag":"8","name":"migrationDesign.pptx","size":47751,"created_at":"2016-06-03T03:56:54-07:00"},{"type":"file","id":"99028795390","etag":"37","name":"Onboarding.boxnote","size":4465,"created_at":"2016-10-24T11:41:47-07:00"},{"type":"file","id":"134865535934","etag":"1","name":"passenger-test.boxnote","size":4988,"created_at":"2017-02-13T10:47:38-08:00"},{"type":"file","id":"92102699797","etag":"1","name":"sas_development_libraries_mou_2016_08_01.docx","size":134018,"created_at":"2016-08-16T07:32:53-07:00"},{"type":"file","id":"22182934895","etag":"41","name":"TimeBox.xlsx","size":21864,"created_at":"2014-10-23T08:22:16-07:00"}],"offset":0,"limit":1000,"order":[{"by":"type","direction":"ASC"},{"by":"name","direction":"ASC"}]}'
174
+ end
146
175
 
147
- its(:name) { is_expected.to eq('..') }
148
- its(:location) { is_expected.to be_empty }
149
- its(:id) { is_expected.to be_kind_of(Pathname) }
150
- it { is_expected.to be_container }
176
+ before do
177
+ stub_request(
178
+ :get, "https://api.box.com/2.0/folders/#{folder_id}"
179
+ ).to_return(
180
+ body: folders_response_body,
181
+ status: 200,
182
+ headers: {
183
+ 'Content-Type' => 'application/json'
184
+ }
185
+ )
186
+ stub_request(
187
+ :get, "https://api.box.com/2.0/folders/#{folder_id}/items?fields=name,size,created_at&limit=99999&offset=0"
188
+ ).to_return(
189
+ body: folders_items_response_body,
190
+ status: 200,
191
+ headers: {
192
+ 'Content-Type' => 'application/json'
193
+ }
194
+ )
151
195
  end
152
196
 
153
197
  describe 'the second item' do
@@ -172,17 +216,53 @@ describe BrowseEverything::Driver::Box, vcr: { cassette_name: 'box', record: :no
172
216
  end
173
217
 
174
218
  describe '#link_for' do
175
- before { provider.token = token }
219
+ before do
220
+ provider.token = token
221
+
222
+ # Initial request for metadata
223
+ stub_request(
224
+ :get, "https://api.box.com/2.0/files/#{file_id}"
225
+ ).to_return(
226
+ body: files_response_body,
227
+ status: 200,
228
+ headers: {
229
+ 'Content-Type' => 'application/json'
230
+ }
231
+ )
232
+
233
+ # Request for the content
234
+ stub_request(
235
+ :get, "https://api.box.com/2.0/files/#{file_id}/content"
236
+ ).to_return(
237
+ status: 302,
238
+ headers: {
239
+ 'location' => files_response_location
240
+ }
241
+ )
242
+ end
243
+ let(:link) { provider.link_for(file_id) }
176
244
 
177
245
  context 'with a file from the root directory' do
178
- let(:link) { provider.link_for('25581309763') }
246
+ let(:files_response_location) do
247
+ 'https://dl.boxcloud.com/d/1/B7Qd7B_iwPHTU4-71W2qYoTbvAaHCPzNsy5WTFHj5XpbmydlF8ud_0n7Ji7zswdU3Kg0patL8EUXCR3cpPw1PZjp-_a1t6MoH-tX3eQeCAR080Hr-yQaEmLQ8dULnnlOhYHeYwPuRp-0gCcXCF5w3O3bE6ZHgML3SCQchPoJQlsfvcYwXZyPRFEVRNUo7qou5X9dbkMCJGmB0CsvqKTXfEs8bqbmRV4hZ5qpJbD0Jer1m4vsqu8h5VkBdMIcgFMn_D9TheZOrmQdg8ExZZVPJ7X8QjFjI707WwIl7CNYkmocCdAJnbYQljFMiQsp0wF0etUqoiskNnaJBS3NagvtvSKaX3TVfiXa87CHDwPiQ3PNJE32d49eKcBK9nLLigoX3SJOkJqMqUSXO_UAxl0bO0EszCNpaWsiQwWiG2jjB5YBfDwQbmfuSTCBPkhDjIH3S3n5cadk4H_8rvgHshYBXNJd2NshgPsYt1XIkE8qj4WznzwayZoi_2k5x4liFvs2F91anj0bPAW2Mzeyz0Pi49mythoH7Rrq_i0sbYDt3VJNyCB0wqs8xUCa38Z6NocTKrKtFHrNSyM2-g5nWsAIwGeG4L4kZ_qIq8nNL49LsntvpFmhogONBozQQCvoEj0WXqhLCxxwT4U-pZ5rCYDt6dpoNlmYv962wXUzg_s21CO96J1o9-_xKl9JxmbWPf0DDJ35gCBFWMhL6BTaLckbBrybx41luHjjDuv72CxfsqsabdyW2yDN3uoBAcXIXD7QUOACPXKHvC98kL9aXL9o9X-zt89vUP61ijizsjtoI9RQc4CrjerfnxC7jS-ER3w2nwkgOEr1wkdEvq4QabcVlyixgrRxKTmzHXa0JGfmpLdEQ821Sgi7QHOpa0l5CSZTQycnWHoMKn4f_xJo5WZLmkmNgkE2DRsok0xGVUK_lNKjrt_N9mPVAdeeXLjSyoSol2_ugU79ELQSHJKKyTFAEJOscUSseg5MtWrIOLKQY6NlAaE0Ckn7c_LX4MYaSj83F-sZouvwSRNVbTklMNkC6j2fy4kr8_T3eb_9-Df94B1867kSJzt7TjXSAxz_p9PbRuj7eY1NW15zjxtOPQpk/download'
248
+ end
249
+ let(:files_response_body) do
250
+ '{"type":"file","id":"25581309763","file_version":{"type":"file_version","id":"23869158869","sha1":"4604bbe44fdcdd4afef3c666cf582e3773960954"},"sequence_id":"1","etag":"1","sha1":"4604bbe44fdcdd4afef3c666cf582e3773960954","name":"failed.tar.gz","description":"","size":28650839,"path_collection":{"total_count":1,"entries":[{"type":"folder","id":"0","sequence_id":null,"etag":null,"name":"All Files"}]},"created_at":"2015-01-29T05:18:43-08:00","modified_at":"2015-01-29T05:18:43-08:00","trashed_at":null,"purged_at":null,"content_created_at":"2015-01-17T05:59:48-08:00","content_modified_at":"2015-01-17T06:00:03-08:00","created_by":{"type":"user","id":"225383863","name":"ADAM GARNER WEAD","login":"agw13@psu.edu"},"modified_by":{"type":"user","id":"225383863","name":"ADAM GARNER WEAD","login":"agw13@psu.edu"},"owned_by":{"type":"user","id":"225383863","name":"ADAM GARNER WEAD","login":"agw13@psu.edu"},"shared_link":{"url":"https:\/\/psu.box.com\/s\/w89hg6kf73q8fn1ww51cj92ohb6piyet","download_url":"https:\/\/psu.box.com\/shared\/static\/w89hg6kf73q8fn1ww51cj92ohb6piyet.gz","vanity_url":null,"effective_access":"open","is_password_enabled":false,"unshared_at":null,"download_count":1,"preview_count":0,"access":"open","permissions":{"can_download":true,"can_preview":true}},"parent":{"type":"folder","id":"0","sequence_id":null,"etag":null,"name":"All Files"},"item_status":"active"}'
251
+ end
252
+ let(:file_id) { '25581309763' }
179
253
 
180
254
  specify { expect(link[0]).to start_with('https://dl.boxcloud.com/d/1') }
181
255
  specify { expect(link[1]).to have_key(:expires) }
182
256
  end
183
257
 
184
258
  context 'with a file from the SaS - Development Team directory' do
185
- let(:link) { provider.link_for('76960974625') }
259
+ let(:files_response_location) do
260
+ 'https://dl.boxcloud.com/d/1/lCA2dDrIV1QAvpQLU7_mkJ0bt2Soa1dLRT6FOzmF37EfJgjRmCO-rZ0VFyKCtoHgPzRoCIHgx-IWCoV8cvtfTX4Yw--RmBLBV9f4JsK6i3LMRKQzgDMxsMu97RSmuMagV-GayR8uO6NGFBtoX81yujebVY-JRB7cxhPU0fbCxAzAnv1711_IUs6YXwhWc-rNFHrNjPsUnJLqw0soQPZqF3Q5irxJLu7traVIeoeuGSfhw-7G-qfqu4CIFonC4ktwwh8jMgN9KW712kRg61moLG6Aa6FTbdCCSt_jCwvQFVGqCz_VivfzG2_BHBqh4IhIB76DmF1ISM-jD9VToUwYfscg6BgC2fqGj6OycsuYr3v3EE0gHlPw_X2rJnq9J2M9plnnhqBvXEwoUXDqyGPvSqWBeQvrrTYfvqj7fG48tUr7RKOytc_K0B4aw9hgGUk3EaifSVWzYvXzoXBp27HFHPsssvIK8fXoSU2J3HCEsGhIDFzu3cvXyPZJV_Co1_LvivWM0AGjXhyoC8PNO5qmtrZFgCob3KlZ8BEI1yJfX_0K8wpocUJP95eBNutBhWa2DEvCK6R5OG-Z0XwrJu8XFRwPcIl3_7lSsIhsQlYA7MoJer7hhwVGAl3kPP-bFsw1et66UM6KHG2FdN2xKP10DYFqR_JrShPP8DQ-ik9-lKej_8LTfiHdf0YbkdMNKzBYlXZsiQNwd3VXdRo1Z4Uhza_XtTDcfH0uxRQyYRecHr-5lEn5iUfV1199GrTsLkcOb9ONNSTFPKW15XCLpgJm6ylSpG2JSZ6zWsGL5hUzdvMytwBCMpIj84UZuwo2RTH1ZDUJYjmHRX11rDnwe2zKMz4woYIA8RjhnYEj6d13BeBz9Fy4GgR90jmM5xHIv9fiAG_b99fpDuYvT-tccN4pdrxPf0P-kCjYAHBt7kY7ToaMWcnOf1nGdLP0oaCGHhAzN0iurjRTLRi2S4N1hsWxjoMuQqayEq15QhRujy2lTZX_XyBgrU2ZodkNsQUlaWcJ6aFT8bh48DtyQvt_xfm4jqmStageFKbydyoB8BpP6oYFAMeFlkS4A6LnXJCzpO8Q../download'
261
+ end
262
+ let(:files_response_body) do
263
+ '{"type":"file","id":"76960974625","file_version":{"type":"file_version","id":"106919307872","sha1":"74608bfb532feff774502c6132eafe9acb1e5217"},"sequence_id":"162","etag":"162","sha1":"74608bfb532feff774502c6132eafe9acb1e5217","name":"Equipment.boxnote","description":"Listing equipment we currently have","size":10140,"path_collection":{"total_count":2,"entries":[{"type":"folder","id":"0","sequence_id":null,"etag":null,"name":"All Files"},{"type":"folder","id":"2459961273","sequence_id":"0","etag":"0","name":"SaS - Development Team"}]},"created_at":"2016-08-03T12:22:39-07:00","modified_at":"2016-10-26T12:40:09-07:00","trashed_at":null,"purged_at":null,"content_created_at":"2016-08-03T12:22:39-07:00","content_modified_at":"2016-10-26T12:40:09-07:00","created_by":{"type":"user","id":"212771336","name":"NICOLE M. GAMPE","login":"nmg110@psu.edu"},"modified_by":{"type":"user","id":"208208274","name":"JUSTIN R PATTERSON","login":"jrp22@psu.edu"},"owned_by":{"type":"user","id":"191882215","name":"Daniel Coughlin","login":"dmc186@psu.edu"},"shared_link":{"url":"https:\/\/psu.box.com\/s\/uolieeszpwglsv0ipwij9ws36rrepvba","download_url":"https:\/\/psu.box.com\/shared\/static\/uolieeszpwglsv0ipwij9ws36rrepvba.boxnote","vanity_url":null,"effective_access":"open","is_password_enabled":false,"unshared_at":null,"download_count":24,"preview_count":0,"access":"open","permissions":{"can_download":true,"can_preview":true}},"parent":{"type":"folder","id":"2459961273","sequence_id":"0","etag":"0","name":"SaS - Development Team"},"item_status":"active"}'
264
+ end
265
+ let(:file_id) { '76960974625' }
186
266
 
187
267
  specify { expect(link[0]).to start_with('https://dl.boxcloud.com/d/1') }
188
268
  specify { expect(link[1]).to have_key(:expires) }
@@ -2,7 +2,7 @@
2
2
 
3
3
  include BrowserConfigHelper
4
4
 
5
- describe BrowseEverything::Driver::Dropbox, vcr: { cassette_name: 'dropbox', record: :none } do
5
+ describe BrowseEverything::Driver::Dropbox do
6
6
  let(:browser) { BrowseEverything::Browser.new(url_options) }
7
7
  let(:provider) { browser.providers['dropbox'] }
8
8
  let(:provider_yml) do
@@ -11,9 +11,22 @@ describe BrowseEverything::Driver::Dropbox, vcr: { cassette_name: 'dropbox', rec
11
11
  client_secret: 'client-secret'
12
12
  }
13
13
  end
14
+ let(:oauth_response_body) do
15
+ '{"access_token": "test-access-token", "token_type": "bearer", "uid": "test-user-id", "account_id": "dbid:id"}'
16
+ end
14
17
 
15
18
  before do
16
19
  stub_configuration
20
+
21
+ stub_request(
22
+ :post, 'https://api.dropboxapi.com/oauth2/token'
23
+ ).to_return(
24
+ body: oauth_response_body,
25
+ status: 200,
26
+ headers: {
27
+ 'Content-Type' => 'text/javascript'
28
+ }
29
+ )
17
30
  end
18
31
 
19
32
  after do
@@ -22,11 +35,11 @@ describe BrowseEverything::Driver::Dropbox, vcr: { cassette_name: 'dropbox', rec
22
35
 
23
36
  describe '#validate_config' do
24
37
  it 'raises and error with an incomplete configuration' do
25
- expect { BrowseEverything::Driver::Dropbox.new({}) }.to raise_error(BrowseEverything::InitializationError)
38
+ expect { described_class.new({}) }.to raise_error(BrowseEverything::InitializationError)
26
39
  end
27
40
 
28
41
  it 'raises and error with a configuration without a client secret' do
29
- expect { BrowseEverything::Driver::Dropbox.new(client_id: 'test-client-id') }.to raise_error(BrowseEverything::InitializationError)
42
+ expect { described_class.new(client_id: 'test-client-id') }.to raise_error(BrowseEverything::InitializationError)
30
43
  end
31
44
  end
32
45
 
@@ -40,11 +53,18 @@ describe BrowseEverything::Driver::Dropbox, vcr: { cassette_name: 'dropbox', rec
40
53
 
41
54
  context 'with a valid configuration' do
42
55
  let(:driver) { described_class.new(provider_yml) }
56
+ let(:connector_response_url_options) do
57
+ {
58
+ protocol: 'http://',
59
+ host: 'test.host',
60
+ port: 80
61
+ }
62
+ end
43
63
 
44
- before { driver.connect({ code: 'code' }, {}) }
64
+ before { driver.connect({ code: 'code' }, {}, connector_response_url_options) }
45
65
 
46
66
  describe '#auth_link' do
47
- subject { driver.auth_link }
67
+ subject { driver.auth_link(connector_response_url_options) }
48
68
 
49
69
  it { is_expected.to start_with('https://www.dropbox.com/oauth2/authorize') }
50
70
  end
@@ -58,6 +78,23 @@ describe BrowseEverything::Driver::Dropbox, vcr: { cassette_name: 'dropbox', rec
58
78
  describe '#contents' do
59
79
  context 'when in the root folder' do
60
80
  let(:contents) { driver.contents }
81
+ let(:list_folder_response) do
82
+ '{"entries": [{".tag": "folder", "name": "Photos", "path_lower": "/photos", "path_display": "/Photos", "id": "id:XAAAAAAAAAALA"}, {".tag": "file", "name": "Getting Started.pdf", "path_lower": "/getting started.pdf", "path_display": "/Getting Started.pdf", "id": "id:XAAAAAAAAAAKg", "client_modified": "2012-11-10T18:33:28Z", "server_modified": "2012-11-10T18:33:27Z", "rev": "60b9427f2", "size": 249159, "content_hash": "c3dfdd86981548e48bc8efb6c4162c76ba961ec92e60f6ba26189068a41fcaf2"}], "cursor": "AAFu-_dOPQTQnqOIb9JklCPYSxWtNRrBBOU4nNkY78wTCc-ktCP4MtIoN1nmOESizkoue2dpu3FbMwDM6BQbgkLObH_Ge-H0BYaPwjfLk5cUHZHd1swkMYGLWELfX_PIHH9hCmU0C8sUL2EJ-7y6BcRFpdOvPmxiu6azVyCx_Il7kA", "has_more": false}'
83
+ end
84
+
85
+ before do
86
+ stub_request(
87
+ :post, 'https://api.dropboxapi.com/2/files/list_folder'
88
+ ).with(
89
+ body: '{"recursive":false,"include_media_info":false,"include_deleted":false,"path":""}'
90
+ ).to_return(
91
+ body: list_folder_response,
92
+ status: 200,
93
+ headers: {
94
+ 'Content-Type' => 'application/json'
95
+ }
96
+ )
97
+ end
61
98
 
62
99
  it 'retrieves all folders the root folders' do
63
100
  expect(contents).not_to be_empty
@@ -75,6 +112,17 @@ describe BrowseEverything::Driver::Dropbox, vcr: { cassette_name: 'dropbox', rec
75
112
 
76
113
  describe '#link_for' do
77
114
  subject(:link_args) { driver.link_for('/Getting Started.pdf') }
115
+ before do
116
+ stub_request(
117
+ :post, 'https://content.dropboxapi.com/2/files/download'
118
+ ).to_return(
119
+ body: '{"name": "Getting Started.pdf", "path_lower": "/getting started.pdf", "path_display": "/Getting Started.pdf", "id": "id:XAAAAAAAAAAKg", "client_modified": "2012-11-10T18:33:28Z", "server_modified": "2012-11-10T18:33:27Z", "rev": "60b9427f2", "size": 249159, "content_hash": "c3dfdd86981548e48bc8efb6c4162c76ba961ec92e60f6ba26189068a41fcaf2"}',
120
+ status: 200,
121
+ headers: {
122
+ 'Content-Type' => 'application/json'
123
+ }
124
+ )
125
+ end
78
126
 
79
127
  it 'provides link arguments for accessing the file' do
80
128
  expect(link_args.first).to be_a String