browse-everything 1.0.0 → 1.1.2

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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +82 -133
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +43 -35
  5. data/CONTRIBUTING.md +3 -3
  6. data/Gemfile +3 -6
  7. data/LICENSE.txt +205 -22
  8. data/README.md +7 -6
  9. data/app/assets/javascripts/browse_everything/behavior.js +16 -8
  10. data/app/assets/javascripts/treetable.webpack.js +687 -0
  11. data/app/controllers/browse_everything_controller.rb +60 -60
  12. data/app/helpers/browse_everything_helper.rb +4 -0
  13. data/app/views/browse_everything/_files.html.erb +4 -2
  14. data/browse-everything.gemspec +6 -7
  15. data/lib/browse_everything.rb +2 -1
  16. data/lib/browse_everything/auth/google/credentials.rb +5 -5
  17. data/lib/browse_everything/auth/google/request_parameters.rb +38 -38
  18. data/lib/browse_everything/driver/base.rb +14 -14
  19. data/lib/browse_everything/driver/box.rb +62 -55
  20. data/lib/browse_everything/driver/dropbox.rb +34 -33
  21. data/lib/browse_everything/driver/file_system.rb +30 -18
  22. data/lib/browse_everything/driver/google_drive.rb +40 -39
  23. data/lib/browse_everything/driver/s3.rb +61 -45
  24. data/lib/browse_everything/file_entry.rb +1 -1
  25. data/lib/browse_everything/retriever.rb +69 -69
  26. data/lib/browse_everything/version.rb +1 -1
  27. data/spec/features/test_compiling_stylesheets_spec.rb +1 -1
  28. data/spec/lib/browse_everything/driver_spec.rb +43 -3
  29. data/spec/spec_helper.rb +9 -3
  30. data/spec/support/capybara.rb +0 -5
  31. data/spec/test_app_templates/Gemfile.extra +9 -0
  32. data/spec/test_app_templates/lib/generators/test_app_generator.rb +54 -3
  33. metadata +49 -45
  34. data/.travis.yml +0 -30
@@ -2,7 +2,7 @@
2
2
 
3
3
  require File.expand_path('../helpers/browse_everything_helper', __dir__)
4
4
 
5
- class BrowseEverythingController < ActionController::Base
5
+ class BrowseEverythingController < ApplicationController
6
6
  layout 'browse_everything'
7
7
  helper BrowseEverythingHelper
8
8
 
@@ -61,72 +61,72 @@ class BrowseEverythingController < ActionController::Base
61
61
 
62
62
  private
63
63
 
64
- # Constructs or accesses an existing session manager Object
65
- # @return [BrowseEverythingSession::ProviderSession] the session manager
66
- def provider_session
67
- BrowseEverythingSession::ProviderSession.new(session: session, name: provider_name)
68
- end
64
+ # Constructs or accesses an existing session manager Object
65
+ # @return [BrowseEverythingSession::ProviderSession] the session manager
66
+ def provider_session
67
+ BrowseEverythingSession::ProviderSession.new(session: session, name: provider_name)
68
+ end
69
69
 
70
- # Clears all authentication tokens, codes, and other data from the Rails session
71
- def reset_provider_session!
72
- return unless @provider_session
73
- @provider_session.token = nil
74
- @provider_session.code = nil
75
- @provider_session.data = nil
76
- @provider_session = nil
77
- end
70
+ # Clears all authentication tokens, codes, and other data from the Rails session
71
+ def reset_provider_session!
72
+ return unless @provider_session
73
+ @provider_session.token = nil
74
+ @provider_session.code = nil
75
+ @provider_session.data = nil
76
+ @provider_session = nil
77
+ end
78
78
 
79
- def connector_response_url_options
80
- { protocol: request.protocol, host: request.host, port: request.port }
81
- end
79
+ def connector_response_url_options
80
+ { protocol: request.protocol, host: request.host, port: request.port }
81
+ end
82
82
 
83
- # Generates the authentication link for a given provider service
84
- # @return [String] the authentication link
85
- def auth_link
86
- @auth_link ||= if provider.present?
87
- link, data = provider.auth_link(connector_response_url_options)
88
- provider_session.data = data
89
- link = "#{link}&state=#{provider.key}" unless link.to_s.include?('state')
90
- link
91
- end
92
- end
83
+ # Generates the authentication link for a given provider service
84
+ # @return [String] the authentication link
85
+ def auth_link
86
+ @auth_link ||= if provider.present?
87
+ link, data = provider.auth_link(connector_response_url_options)
88
+ provider_session.data = data
89
+ link = "#{link}&state=#{provider.key}" unless link.to_s.include?('state')
90
+ link
91
+ end
92
+ end
93
93
 
94
- # Accesses the relative path for browsing from the Rails session
95
- # @return [String]
96
- def browse_path
97
- params[:path] || ''
98
- end
94
+ # Accesses the relative path for browsing from the Rails session
95
+ # @return [String]
96
+ def browse_path
97
+ params[:path] || ''
98
+ end
99
99
 
100
- # Generate the provider name from the Rails session state value
101
- # @return [String]
102
- def provider_name_from_state
103
- params[:state].to_s.split(/\|/).last
104
- end
100
+ # Generate the provider name from the Rails session state value
101
+ # @return [String]
102
+ def provider_name_from_state
103
+ params[:state].to_s.split(/\|/).last
104
+ end
105
105
 
106
- # Generates the name of the provider using Rails session values
107
- # @return [String]
108
- def provider_name
109
- params[:provider] || provider_name_from_state || browser.providers.each_key.to_a.first
110
- end
106
+ # Generates the name of the provider using Rails session values
107
+ # @return [String]
108
+ def provider_name
109
+ params[:provider] || provider_name_from_state || browser.providers.each_key.to_a.first
110
+ end
111
111
 
112
- # Retrieve the Driver for each request
113
- # @return [BrowseEverything::Driver::Base]
114
- def provider
115
- browser.providers[provider_name.to_sym] || browser.first_provider
116
- end
112
+ # Retrieve the Driver for each request
113
+ # @return [BrowseEverything::Driver::Base]
114
+ def provider
115
+ browser.providers[provider_name.to_sym] || browser.first_provider
116
+ end
117
117
 
118
- # Constructs a browser manager Object
119
- # Browser state cannot persist between requests to the Controller
120
- # Hence, a Browser must be reinstantiated for each request using the state provided in the Rails session
121
- # @return [BrowseEverything::Browser]
122
- def browser
123
- BrowserFactory.build(session: session, url_options: url_options)
124
- end
118
+ # Constructs a browser manager Object
119
+ # Browser state cannot persist between requests to the Controller
120
+ # Hence, a Browser must be reinstantiated for each request using the state provided in the Rails session
121
+ # @return [BrowseEverything::Browser]
122
+ def browser
123
+ BrowserFactory.build(session: session, url_options: url_options)
124
+ end
125
125
 
126
- helper_method :auth_link
127
- helper_method :browser
128
- helper_method :browse_path
129
- helper_method :provider
130
- helper_method :provider_name
131
- helper_method :provider_contents
126
+ helper_method :auth_link
127
+ helper_method :browser
128
+ helper_method :browse_path
129
+ helper_method :provider
130
+ helper_method :provider_name
131
+ helper_method :provider_contents
132
132
  end
@@ -20,4 +20,8 @@ module BrowseEverythingHelper
20
20
  acceptable_types << 'application/x-directory'
21
21
  acceptable_types.any? { |type| mime_match?(file.type, type) }
22
22
  end
23
+
24
+ def file_size_to_human_size(file_size)
25
+ "#{file_size} bytes"
26
+ end
23
27
  end
@@ -9,12 +9,14 @@
9
9
  </tr>
10
10
  </thead>
11
11
  <% provider_contents.each_with_index do |file, index| %>
12
+ <% next if file.nil? %>
12
13
  <% path = browse_everything_engine.contents_path(provider_name, file.id) %>
13
14
  <% parent = params[:parent] %>
14
15
  <% if file.container? || provider.config[:max_upload_file_size].blank? %>
15
16
  <% disabled = false %>
16
17
  <% else %>
17
18
  <% max_size = provider.config[:max_upload_file_size].to_i %>
19
+ <% max_human_size = file_size_to_human_size(max_size) %>
18
20
  <% disabled = file.size > max_size %>
19
21
  <% end %>
20
22
 
@@ -27,7 +29,7 @@
27
29
 
28
30
  <td role="gridcell" title="<%= file.name %>" class="<%=file.container? ? 'ev-container' : 'ev-file'%> ev-file-name">
29
31
  <% if disabled %>
30
- <span title="<%= t('browse_everything.size_disabled', max_size: number_to_human_size(max_size)) %>"
32
+ <span title="<%= t('browse_everything.size_disabled', max_size: max_human_size) %>"
31
33
  class="<%=file.container? ? 'folder' : 'file'%>" aria-hidden="true">
32
34
  <%= file.name %>
33
35
  </span>
@@ -52,7 +54,7 @@
52
54
 
53
55
  <% if file.size %>
54
56
  <td role="gridcell" class="ev-file-size">
55
- <%= number_to_human_size(file.size).sub(/Bytes/,'bytes') %>
57
+ <%= file_size_to_human_size(file.size) %>
56
58
  </td>
57
59
  <% else %>
58
60
  <td role="gridcell" class="ev-file-size">Unknown</td>
@@ -23,18 +23,16 @@ Gem::Specification.new do |spec|
23
23
  spec.add_dependency 'aws-sdk-s3'
24
24
  spec.add_dependency 'dropbox_api', '>= 0.1.10'
25
25
  spec.add_dependency 'google-api-client', '~> 0.23'
26
- spec.add_dependency 'google_drive', '~> 2.1'
27
- spec.add_dependency 'googleauth', '0.6.6'
28
- spec.add_dependency 'rails', '>= 4.2'
26
+ spec.add_dependency 'google_drive', '>= 2.1', "< 4"
27
+ spec.add_dependency 'googleauth', '>= 0.6.6', '< 1.0'
28
+ spec.add_dependency 'rails', '>= 4.2', '< 7.0'
29
29
  spec.add_dependency 'ruby-box'
30
30
  spec.add_dependency 'signet', '~> 0.8'
31
- spec.add_dependency 'thor', '~> 0.19'
32
31
  spec.add_dependency 'typhoeus'
33
32
 
34
- spec.add_development_dependency 'bixby', '>= 1.0'
35
- spec.add_development_dependency 'bundler', '~> 1.3'
33
+ spec.add_development_dependency 'bixby', '~> 3.0'
34
+ spec.add_development_dependency 'bundler', '>= 1.3'
36
35
  spec.add_development_dependency 'capybara'
37
- spec.add_development_dependency 'chromedriver-helper', '~> 2.1'
38
36
  spec.add_development_dependency 'coveralls'
39
37
  spec.add_development_dependency 'engine_cart', '~> 2.0'
40
38
  spec.add_development_dependency 'factory_bot_rails'
@@ -50,5 +48,6 @@ Gem::Specification.new do |spec|
50
48
  spec.add_development_dependency 'selenium-webdriver'
51
49
  spec.add_development_dependency 'simplecov'
52
50
  spec.add_development_dependency 'sqlite3'
51
+ spec.add_development_dependency 'webdrivers'
53
52
  spec.add_development_dependency 'webmock'
54
53
  end
@@ -59,7 +59,8 @@ module BrowseEverything
59
59
  @config = ActiveSupport::HashWithIndifferentAccess.new config_values
60
60
  @config.deep_symbolize_keys
61
61
  rescue Errno::ENOENT
62
- raise ConfigurationError, 'Missing browse_everything_providers.yml configuration file'
62
+ Rails.logger.warn 'Missing browse_everything_providers.yml configuration file'
63
+ @config = ActiveSupport::HashWithIndifferentAccess.new({})
63
64
  end
64
65
  else
65
66
  raise InitializationError, "Unrecognized configuration: #{value.inspect}"
@@ -17,11 +17,11 @@ module BrowseEverything
17
17
 
18
18
  private
19
19
 
20
- # Structure a hash from existing access token values (usually cached within a Cookie)
21
- # @return [Hash]
22
- def build_token_hash
23
- { 'access_token' => access_token }
24
- end
20
+ # Structure a hash from existing access token values (usually cached within a Cookie)
21
+ # @return [Hash]
22
+ def build_token_hash
23
+ { 'access_token' => access_token }
24
+ end
25
25
  end
26
26
  end
27
27
  end
@@ -14,47 +14,47 @@ module BrowseEverything
14
14
 
15
15
  private
16
16
 
17
- # The default query parameters for the Google Drive API
18
- # @return [Hash]
19
- def default_params
20
- {
21
- q: default_query,
22
- order_by: 'modifiedTime desc,folder,name',
23
- fields: 'nextPageToken,files(name,id,mimeType,size,modifiedTime,parents,web_content_link)',
24
- supports_team_drives: true,
25
- include_team_drive_items: true,
26
- corpora: 'user,allTeamDrives',
27
- page_size: 1000
28
- }
29
- end
17
+ # The default query parameters for the Google Drive API
18
+ # @return [Hash]
19
+ def default_params
20
+ {
21
+ q: default_query,
22
+ order_by: 'modifiedTime desc,folder,name',
23
+ fields: 'nextPageToken,files(name,id,mimeType,size,modifiedTime,parents,web_content_link)',
24
+ supports_team_drives: true,
25
+ include_team_drive_items: true,
26
+ corpora: 'user,allTeamDrives',
27
+ page_size: 1000
28
+ }
29
+ end
30
30
 
31
- def default_query
32
- field_queries = []
33
- contraints.each_pair do |field, constraints|
34
- field_constraint = constraints.join(" and #{field} ")
35
- field_queries << "#{field} #{field_constraint}"
36
- end
37
- field_queries.join(' ')
31
+ def default_query
32
+ field_queries = []
33
+ contraints.each_pair do |field, constraints|
34
+ field_constraint = constraints.join(" and #{field} ")
35
+ field_queries << "#{field} #{field_constraint}"
38
36
  end
37
+ field_queries.join(' ')
38
+ end
39
39
 
40
- def contraints
41
- {
42
- 'mimeType' => [
43
- '!= \'application/vnd.google-apps.audio\'',
44
- '!= \'application/vnd.google-apps.document\'',
45
- '!= \'application/vnd.google-apps.drawing\'',
46
- '!= \'application/vnd.google-apps.form\'',
47
- '!= \'application/vnd.google-apps.fusiontable\'',
48
- '!= \'application/vnd.google-apps.map\'',
49
- '!= \'application/vnd.google-apps.photo\'',
50
- '!= \'application/vnd.google-apps.presentation\'',
51
- '!= \'application/vnd.google-apps.script\'',
52
- '!= \'application/vnd.google-apps.site\'',
53
- '!= \'application/vnd.google-apps.spreadsheet\'',
54
- '!= \'application/vnd.google-apps.video\''
55
- ]
56
- }
57
- end
40
+ def contraints
41
+ {
42
+ 'mimeType' => [
43
+ '!= \'application/vnd.google-apps.audio\'',
44
+ '!= \'application/vnd.google-apps.document\'',
45
+ '!= \'application/vnd.google-apps.drawing\'',
46
+ '!= \'application/vnd.google-apps.form\'',
47
+ '!= \'application/vnd.google-apps.fusiontable\'',
48
+ '!= \'application/vnd.google-apps.map\'',
49
+ '!= \'application/vnd.google-apps.photo\'',
50
+ '!= \'application/vnd.google-apps.presentation\'',
51
+ '!= \'application/vnd.google-apps.script\'',
52
+ '!= \'application/vnd.google-apps.site\'',
53
+ '!= \'application/vnd.google-apps.spreadsheet\'',
54
+ '!= \'application/vnd.google-apps.video\''
55
+ ]
56
+ }
57
+ end
58
58
  end
59
59
  end
60
60
  end
@@ -100,21 +100,21 @@ module BrowseEverything
100
100
 
101
101
  private
102
102
 
103
- # Generate the options for the Rails URL generation for API callbacks
104
- # remove the script_name parameter from the url_options since that is causing issues
105
- # with the route not containing the engine path in rails 4.2.0
106
- # @return [Hash]
107
- def callback_options
108
- options = config.to_hash
109
- options.deep_symbolize_keys!
110
- options[:url_options].reject { |k, _v| k == :script_name }
111
- end
103
+ # Generate the options for the Rails URL generation for API callbacks
104
+ # remove the script_name parameter from the url_options since that is causing issues
105
+ # with the route not containing the engine path in rails 4.2.0
106
+ # @return [Hash]
107
+ def callback_options
108
+ options = config.to_hash
109
+ options.deep_symbolize_keys!
110
+ options[:url_options].reject { |k, _v| k == :script_name }
111
+ end
112
112
 
113
- # Generate the URL for the API callback
114
- # @return [String]
115
- def callback
116
- connector_response_url(callback_options)
117
- end
113
+ # Generate the URL for the API callback
114
+ # @return [String]
115
+ def callback
116
+ connector_response_url(callback_options)
117
+ end
118
118
  end
119
119
  end
120
120
  end
@@ -33,15 +33,18 @@ module BrowseEverything
33
33
  raise InitializationError, 'Box driver requires both :client_id and :client_secret argument'
34
34
  end
35
35
 
36
+ # Retrieves the file entry objects for a given path to Box resource
36
37
  # @param [String] id of the file or folder in Box
37
- # @return [Array<RubyBox::File>]
38
+ # @return [Array<BrowseEverything::FileEntry>]
38
39
  def contents(id = '')
39
40
  folder = id.empty? ? box_client.root_folder : box_client.folder_by_id(id)
40
- @entries = []
41
+ values = []
41
42
 
42
43
  folder.items(ITEM_LIMIT, 0, %w[name size created_at]).collect do |f|
43
- @entries << directory_entry(f)
44
+ values << directory_entry(f)
44
45
  end
46
+ @entries = values.compact
47
+
45
48
  @sorter.call(@entries)
46
49
  end
47
50
 
@@ -74,68 +77,72 @@ module BrowseEverything
74
77
 
75
78
  private
76
79
 
77
- def token_expired?
78
- return true if expiration_time.nil?
79
- Time.now.to_i > expiration_time
80
- end
80
+ def token_expired?
81
+ return true if expiration_time.nil?
82
+ Time.now.to_i > expiration_time
83
+ end
81
84
 
82
- def box_client
83
- if token_expired?
84
- session = box_session
85
- register_access_token(session.refresh_token(box_refresh_token))
86
- end
87
- RubyBox::Client.new(box_session)
85
+ def box_client
86
+ if token_expired?
87
+ session = box_session
88
+ register_access_token(session.refresh_token(box_refresh_token))
88
89
  end
90
+ RubyBox::Client.new(box_session)
91
+ end
89
92
 
90
- def session
91
- AuthenticationFactory.new(
92
- self.class.authentication_klass,
93
- client_id: config[:client_id],
94
- client_secret: config[:client_secret],
95
- access_token: box_token,
96
- refresh_token: box_refresh_token
97
- )
98
- end
93
+ def session
94
+ AuthenticationFactory.new(
95
+ self.class.authentication_klass,
96
+ client_id: config[:client_id],
97
+ client_secret: config[:client_secret],
98
+ access_token: box_token,
99
+ refresh_token: box_refresh_token
100
+ )
101
+ end
99
102
 
100
- def authenticate
101
- session.authenticate
102
- end
103
+ def authenticate
104
+ session.authenticate
105
+ end
103
106
 
104
- def box_session
105
- authenticate
106
- end
107
+ def box_session
108
+ authenticate
109
+ end
107
110
 
108
- # If there is an active session, {@token} will be set by {BrowseEverythingController} using data stored in the
109
- # session. However, if there is no prior session, or the token has expired, we reset it here using # a new
110
- # access_token received from {#box_session}.
111
- #
112
- # @param [OAuth2::AccessToken] access_token
113
- def register_access_token(access_token)
114
- @token = {
115
- 'token' => access_token.token,
116
- 'refresh_token' => access_token.refresh_token,
117
- 'expires_at' => access_token.expires_at
118
- }
119
- end
111
+ # If there is an active session, {@token} will be set by {BrowseEverythingController} using data stored in the
112
+ # session. However, if there is no prior session, or the token has expired, we reset it here using # a new
113
+ # access_token received from {#box_session}.
114
+ #
115
+ # @param [OAuth2::AccessToken] access_token
116
+ def register_access_token(access_token)
117
+ @token = {
118
+ 'token' => access_token.token,
119
+ 'refresh_token' => access_token.refresh_token,
120
+ 'expires_at' => access_token.expires_at
121
+ }
122
+ end
120
123
 
121
- def box_token
122
- return unless @token
123
- @token.fetch('token', nil)
124
- end
124
+ def box_token
125
+ return unless @token
126
+ @token.fetch('token', nil)
127
+ end
125
128
 
126
- def box_refresh_token
127
- return unless @token
128
- @token.fetch('refresh_token', nil)
129
- end
129
+ def box_refresh_token
130
+ return unless @token
131
+ @token.fetch('refresh_token', nil)
132
+ end
130
133
 
131
- def expiration_time
132
- return unless @token
133
- @token.fetch('expires_at', nil).to_i
134
- end
134
+ def expiration_time
135
+ return unless @token
136
+ @token.fetch('expires_at', nil).to_i
137
+ end
135
138
 
136
- def directory_entry(file)
137
- BrowseEverything::FileEntry.new(file.id, "#{key}:#{file.id}", file.name, file.size, file.created_at, file.type == 'folder')
138
- end
139
+ # Constructs a BrowseEverything::FileEntry object for a Box file
140
+ # resource
141
+ # @param file [String] ID to the file resource
142
+ # @return [BrowseEverything::File]
143
+ def directory_entry(file)
144
+ BrowseEverything::FileEntry.new(file.id, "#{key}:#{file.id}", file.name, file.size, file.created_at, file.type == 'folder')
145
+ end
139
146
  end
140
147
  end
141
148
  end