browse-everything 0.15.1 → 0.16.0
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 +5 -5
- data/.rubocop.yml +61 -9
- data/.rubocop_todo.yml +2 -15
- data/.travis.yml +19 -19
- data/CONTRIBUTING.md +6 -6
- data/Gemfile +12 -8
- data/README.md +30 -0
- data/Rakefile +2 -1
- data/app/assets/javascripts/browse_everything/behavior.js.coffee +5 -0
- data/app/controllers/browse_everything_controller.rb +75 -23
- data/app/helpers/browse_everything_helper.rb +2 -8
- data/app/helpers/font_awesome_version_helper.rb +9 -8
- data/app/services/browse_everything_session.rb +10 -0
- data/app/services/browse_everything_session/provider_session.rb +42 -0
- data/app/services/browser_factory.rb +25 -0
- data/app/views/browse_everything/_files.html.erb +56 -6
- data/browse-everything.gemspec +29 -25
- data/config/routes.rb +7 -2
- data/lib/browse-everything.rb +2 -0
- data/lib/browse_everything.rb +45 -12
- data/lib/browse_everything/auth/google/credentials.rb +28 -0
- data/lib/browse_everything/auth/google/request_parameters.rb +61 -0
- data/lib/browse_everything/browser.rb +11 -4
- data/lib/browse_everything/driver/authentication_factory.rb +22 -0
- data/lib/browse_everything/driver/base.rb +72 -19
- data/lib/browse_everything/driver/box.rb +46 -17
- data/lib/browse_everything/driver/dropbox.rb +36 -10
- data/lib/browse_everything/driver/file_system.rb +14 -26
- data/lib/browse_everything/driver/google_drive.rb +187 -54
- data/lib/browse_everything/driver/s3.rb +81 -75
- data/lib/browse_everything/engine.rb +3 -2
- data/lib/browse_everything/file_entry.rb +3 -1
- data/lib/browse_everything/retriever.rb +103 -31
- data/lib/browse_everything/version.rb +3 -1
- data/lib/generators/browse_everything/assets_generator.rb +3 -2
- data/lib/generators/browse_everything/config_generator.rb +11 -9
- data/lib/generators/browse_everything/install_generator.rb +3 -2
- data/lib/generators/browse_everything/templates/browse_everything_providers.yml.example +12 -11
- data/spec/controllers/browse_everything_controller_spec.rb +80 -0
- data/spec/features/select_files_spec.rb +13 -13
- data/spec/features/test_compiling_stylesheets_spec.rb +2 -0
- data/spec/fixtures/vcr_cassettes/google_drive.yml +331 -0
- data/spec/fixtures/vcr_cassettes/retriever.yml +93 -0
- data/spec/helper/browse_everything_controller_helper_spec.rb +21 -7
- data/spec/javascripts/jasmine_spec.rb +2 -0
- data/spec/javascripts/support/jasmine_helper.rb +1 -0
- data/spec/lib/browse_everything/auth/google/credentials_spec.rb +41 -0
- data/spec/{unit → lib/browse_everything}/browse_everything_helper_spec.rb +2 -0
- data/spec/lib/browse_everything/browser_spec.rb +109 -0
- data/spec/{unit → lib/browse_everything/driver}/base_spec.rb +5 -4
- data/spec/{unit → lib/browse_everything/driver}/box_spec.rb +20 -5
- data/spec/{unit → lib/browse_everything/driver}/dropbox_spec.rb +15 -18
- data/spec/{unit → lib/browse_everything/driver}/file_system_spec.rb +32 -26
- data/spec/lib/browse_everything/driver/google_drive_spec.rb +171 -0
- data/spec/{unit → lib/browse_everything/driver}/s3_spec.rb +38 -21
- data/spec/lib/browse_everything/driver_spec.rb +38 -0
- data/spec/{unit → lib/browse_everything}/file_entry_spec.rb +4 -1
- data/spec/lib/browse_everything/retriever_spec.rb +200 -0
- data/spec/lib/browse_everything_spec.rb +67 -0
- data/spec/services/browse_everything_session/provider_session_spec.rb +50 -0
- data/spec/services/browser_factory_spec.rb +40 -0
- data/spec/spec_helper.rb +39 -18
- data/spec/support/app/controllers/file_handler_controller.rb +4 -4
- data/spec/support/app/views/file_handler/main.html.erb +1 -1
- data/spec/support/capybara.rb +17 -0
- data/spec/support/rake.rb +3 -1
- data/spec/support/wait_for_ajax.rb +14 -0
- data/spec/test_app_templates/Gemfile.extra +1 -0
- data/spec/test_app_templates/lib/generators/test_app_generator.rb +10 -4
- data/spec/views/browse_everything/{_file.html.erb_spec.rb → _files.html.erb_spec.rb} +24 -18
- data/tasks/ci.rake +2 -0
- metadata +159 -107
- data/app/views/browse_everything/_file.html.erb +0 -52
- data/app/views/browse_everything/resolve.html.erb +0 -1
- data/spec/unit/browser_spec.rb +0 -76
- data/spec/unit/retriever_spec.rb +0 -109
@@ -1,44 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'google/apis/drive_v3'
|
4
|
+
require 'googleauth'
|
5
|
+
require 'googleauth/stores/file_token_store'
|
6
|
+
require_relative 'authentication_factory'
|
7
|
+
|
1
8
|
module BrowseEverything
|
2
9
|
module Driver
|
3
10
|
class GoogleDrive < Base
|
4
|
-
|
5
|
-
|
11
|
+
class << self
|
12
|
+
attr_accessor :authentication_klass
|
13
|
+
|
14
|
+
def default_authentication_klass
|
15
|
+
Google::Auth::UserAuthorizer
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :credentials
|
20
|
+
|
21
|
+
# Constructor
|
22
|
+
# @param config_values [Hash] configuration for the driver
|
23
|
+
def initialize(config_values)
|
24
|
+
self.class.authentication_klass ||= self.class.default_authentication_klass
|
25
|
+
super(config_values)
|
26
|
+
end
|
27
|
+
|
28
|
+
# The token here must be set using a Hash
|
29
|
+
# @param value [String, Hash] the new access token
|
30
|
+
def token=(value)
|
31
|
+
# This is invoked within BrowseEverythingController using a Hash
|
32
|
+
value = value.fetch('access_token') if value.is_a? Hash
|
33
|
+
|
34
|
+
# Restore the credentials if the access token string itself has been cached
|
35
|
+
restore_credentials(value) if @credentials.nil?
|
36
|
+
|
37
|
+
super(value)
|
38
|
+
end
|
6
39
|
|
7
40
|
def icon
|
8
41
|
'google-plus-sign'
|
9
42
|
end
|
10
43
|
|
44
|
+
# Validates the configuration for the Google Drive provider
|
11
45
|
def validate_config
|
12
|
-
unless config[:client_id]
|
13
|
-
|
14
|
-
end
|
15
|
-
unless config[:client_secret]
|
16
|
-
raise BrowseEverything::InitializationError, 'GoogleDrive driver requires a :client_secret argument'
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def contents(path = '')
|
21
|
-
return to_enum(:contents, path) unless block_given?
|
22
|
-
default_params = {
|
23
|
-
order_by: 'folder,modifiedTime desc,name',
|
24
|
-
fields: 'nextPageToken,files(name,id,mimeType,size,modifiedTime,parents,web_content_link)'
|
25
|
-
# page_size: 100
|
26
|
-
}
|
27
|
-
page_token = nil
|
28
|
-
begin
|
29
|
-
default_params[:q] = "'#{path}' in parents" unless path.blank?
|
30
|
-
default_params[:page_token] = page_token unless page_token.blank?
|
31
|
-
response = drive.list_files(default_params)
|
32
|
-
page_token = response.next_page_token
|
33
|
-
response.files.select do |file|
|
34
|
-
path.blank? ? (file.parents.blank? || file.parents.any? { |p| p == 'root' }) : true
|
35
|
-
end.each do |file|
|
36
|
-
d = details(file, path)
|
37
|
-
yield d if d
|
38
|
-
end
|
39
|
-
end while !page_token.blank?
|
46
|
+
raise InitializationError, 'GoogleDrive driver requires a :client_id argument' unless config[:client_id]
|
47
|
+
raise InitializationError, 'GoogleDrive driver requires a :client_secret argument' unless config[:client_secret]
|
40
48
|
end
|
41
49
|
|
50
|
+
# Retrieve the file details
|
51
|
+
# @param file [Google::Apis::DriveV3::File] the Google Drive File
|
52
|
+
# @param path [String] path for the resource details (unused)
|
53
|
+
# @return [BrowseEverything::FileEntry] file entry for the resource node
|
42
54
|
def details(file, _path = '')
|
43
55
|
mime_folder = file.mime_type == 'application/vnd.google-apps.folder'
|
44
56
|
BrowseEverything::FileEntry.new(
|
@@ -46,60 +58,181 @@ module BrowseEverything
|
|
46
58
|
"#{key}:#{file.id}",
|
47
59
|
file.name,
|
48
60
|
file.size.to_i,
|
49
|
-
file.modified_time ||
|
61
|
+
file.modified_time || Time.new,
|
50
62
|
mime_folder,
|
51
63
|
mime_folder ? 'directory' : file.mime_type
|
52
64
|
)
|
53
65
|
end
|
54
66
|
|
67
|
+
# Lists the files given a Google Drive context
|
68
|
+
# @param drive [Google::Apis::DriveV3::DriveService] the Google Drive context
|
69
|
+
# @param request_params [RequestParameters] the object containing the parameters for the Google Drive API request
|
70
|
+
# @param path [String] the path (default to the root)
|
71
|
+
# @return [Array<BrowseEverything::FileEntry>] file entries for the path
|
72
|
+
def list_files(drive, request_params, path: '')
|
73
|
+
drive.list_files(request_params.to_h) do |file_list, error|
|
74
|
+
# Raise an exception if there was an error Google API's
|
75
|
+
if error.present?
|
76
|
+
# In order to properly trigger reauthentication, the token must be cleared
|
77
|
+
# Additionally, the error is not automatically raised from the Google Client
|
78
|
+
@token = nil
|
79
|
+
raise error
|
80
|
+
end
|
81
|
+
|
82
|
+
@entries += file_list.files.map do |gdrive_file|
|
83
|
+
details(gdrive_file, path)
|
84
|
+
end
|
85
|
+
|
86
|
+
request_params.page_token = file_list.next_page_token
|
87
|
+
end
|
88
|
+
|
89
|
+
@entries += list_files(drive, request_params, path: path) if request_params.page_token.present?
|
90
|
+
end
|
91
|
+
|
92
|
+
# Retrieve the files for any given resource on Google Drive
|
93
|
+
# @param path [String] the root or Folder path for which to list contents
|
94
|
+
# @return [Array<BrowseEverything::FileEntry>] file entries for the path
|
95
|
+
def contents(path = '')
|
96
|
+
@entries = []
|
97
|
+
drive_service.batch do |drive|
|
98
|
+
request_params = Auth::Google::RequestParameters.new
|
99
|
+
request_params.q += " and '#{path}' in parents " if path.present?
|
100
|
+
list_files(drive, request_params, path: path)
|
101
|
+
end
|
102
|
+
|
103
|
+
@sorter.call(@entries)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Retrieve a link for a resource
|
107
|
+
# @param id [String] identifier for the resource
|
108
|
+
# @return [Array<String, Hash>] authorized link to the resource
|
55
109
|
def link_for(id)
|
56
|
-
file =
|
57
|
-
auth_header = { 'Authorization' => "Bearer #{
|
110
|
+
file = drive_service.get_file(id, fields: 'id, name, size')
|
111
|
+
auth_header = { 'Authorization' => "Bearer #{credentials.access_token}" }
|
58
112
|
extras = {
|
59
113
|
auth_header: auth_header,
|
60
114
|
expires: 1.hour.from_now,
|
61
115
|
file_name: file.name,
|
62
116
|
file_size: file.size.to_i
|
63
117
|
}
|
64
|
-
[
|
118
|
+
[download_url(id), extras]
|
65
119
|
end
|
66
120
|
|
121
|
+
# Provides a URL for authorizing against Google Drive
|
122
|
+
# @return [String] the URL
|
67
123
|
def auth_link
|
68
|
-
|
124
|
+
Addressable::URI.parse(authorizer.get_authorization_url)
|
69
125
|
end
|
70
126
|
|
127
|
+
# Whether or not the current provider is authorized
|
128
|
+
# @return [true,false]
|
71
129
|
def authorized?
|
72
|
-
token.present?
|
130
|
+
@token.present?
|
131
|
+
end
|
132
|
+
|
133
|
+
# Client ID for authorizing against the Google API's
|
134
|
+
# @return [Google::Auth::ClientId]
|
135
|
+
def client_id
|
136
|
+
@client_id ||= Google::Auth::ClientId.from_hash(client_secrets)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Token store file used for authorizing against the Google API's
|
140
|
+
# (This is fundamentally used to temporarily cache access tokens)
|
141
|
+
# @return [Google::Auth::Stores::FileTokenStore]
|
142
|
+
def token_store
|
143
|
+
Google::Auth::Stores::FileTokenStore.new(file: file_token_store_path)
|
144
|
+
end
|
145
|
+
|
146
|
+
def session
|
147
|
+
AuthenticationFactory.new(
|
148
|
+
self.class.authentication_klass,
|
149
|
+
client_id,
|
150
|
+
scope,
|
151
|
+
token_store,
|
152
|
+
callback
|
153
|
+
)
|
73
154
|
end
|
74
155
|
|
156
|
+
delegate :authenticate, to: :session
|
157
|
+
|
158
|
+
# Authorization Object for Google API
|
159
|
+
# @return [Google::Auth::UserAuthorizer]
|
160
|
+
def authorizer
|
161
|
+
@authorizer ||= authenticate
|
162
|
+
end
|
163
|
+
|
164
|
+
# Request to authorize the provider
|
165
|
+
# This is *the* method which, passing an HTTP request, redeems an authorization code for an access token
|
166
|
+
# @return [String] a new access token
|
167
|
+
def authorize!
|
168
|
+
@credentials = authorizer.get_credentials_from_code(user_id: user_id, code: code)
|
169
|
+
@token = @credentials.access_token
|
170
|
+
@code = nil # The authorization code can only be redeemed for an access token once
|
171
|
+
@token
|
172
|
+
end
|
173
|
+
|
174
|
+
# This is the method accessed by the BrowseEverythingController for authorizing using an authorization code
|
175
|
+
# @param params [Hash] HTTP response passed to the OAuth callback
|
176
|
+
# @param _data [Object,nil] an unused parameter
|
177
|
+
# @return [String] a new access token
|
75
178
|
def connect(params, _data)
|
76
|
-
|
77
|
-
|
179
|
+
@code = params[:code]
|
180
|
+
authorize!
|
78
181
|
end
|
79
182
|
|
80
|
-
|
81
|
-
|
82
|
-
|
183
|
+
# Construct a new object for interfacing with the Google Drive API
|
184
|
+
# @return [Google::Apis::DriveV3::DriveService]
|
185
|
+
def drive_service
|
186
|
+
Google::Apis::DriveV3::DriveService.new.tap do |s|
|
187
|
+
s.authorization = credentials
|
83
188
|
end
|
84
189
|
end
|
85
190
|
|
86
191
|
private
|
87
192
|
|
88
|
-
def
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
193
|
+
def client_secrets
|
194
|
+
{
|
195
|
+
Google::Auth::ClientId::WEB_APP => {
|
196
|
+
Google::Auth::ClientId::CLIENT_ID => config[:client_id],
|
197
|
+
Google::Auth::ClientId::CLIENT_SECRET => config[:client_secret]
|
198
|
+
}
|
199
|
+
}
|
200
|
+
end
|
201
|
+
|
202
|
+
# This is required for using the googleauth Gem
|
203
|
+
# @see http://www.rubydoc.info/gems/googleauth/Google/Auth/Stores/FileTokenStore FileTokenStore for googleauth
|
204
|
+
# @return [Tempfile] temporary file within which to cache credentials
|
205
|
+
def file_token_store_path
|
206
|
+
Tempfile.new('gdrive.yaml')
|
207
|
+
end
|
208
|
+
|
209
|
+
def scope
|
210
|
+
Google::Apis::DriveV3::AUTH_DRIVE
|
211
|
+
end
|
212
|
+
|
213
|
+
# Provides the user ID for caching access tokens
|
214
|
+
# (This is a hack which attempts to anonymize the access tokens)
|
215
|
+
# @return [String] the ID for the user
|
216
|
+
def user_id
|
217
|
+
'current_user'
|
218
|
+
end
|
219
|
+
|
220
|
+
# Please see https://developers.google.com/drive/v3/web/manage-downloads
|
221
|
+
# @param id [String] the ID for the Google Drive File
|
222
|
+
# @return [String] the URL for the file download
|
223
|
+
def download_url(id)
|
224
|
+
"https://www.googleapis.com/drive/v3/files/#{id}?alt=media"
|
94
225
|
end
|
95
226
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
227
|
+
# Restore the credentials for the Google API
|
228
|
+
# @param access_token [String] the access token redeemed using an authorization code
|
229
|
+
# @return Credentials credentials restored from a cached access token
|
230
|
+
def restore_credentials(access_token)
|
231
|
+
client = Auth::Google::Credentials.new
|
232
|
+
client.client_id = client_id.id
|
233
|
+
client.client_secret = client_id.secret
|
234
|
+
client.update_token!('access_token' => access_token)
|
235
|
+
@credentials = client
|
103
236
|
end
|
104
237
|
end
|
105
238
|
end
|
@@ -1,11 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'aws-sdk-s3'
|
4
|
+
require_relative 'authentication_factory'
|
2
5
|
|
3
6
|
module BrowseEverything
|
4
7
|
module Driver
|
5
8
|
class S3 < Base
|
6
|
-
DEFAULTS = { response_type: :signed_url }.freeze
|
7
|
-
RESPONSE_TYPES = [
|
8
|
-
CONFIG_KEYS = [
|
9
|
+
DEFAULTS = { response_type: :signed_url, expires_in: 14400 }.freeze
|
10
|
+
RESPONSE_TYPES = %i[signed_url public_url s3_uri].freeze
|
11
|
+
CONFIG_KEYS = %i[bucket].freeze
|
12
|
+
|
13
|
+
class << self
|
14
|
+
attr_accessor :authentication_klass
|
15
|
+
|
16
|
+
def default_authentication_klass
|
17
|
+
Aws::S3::Client
|
18
|
+
end
|
19
|
+
end
|
9
20
|
|
10
21
|
attr_reader :entries
|
11
22
|
|
@@ -14,8 +25,9 @@ module BrowseEverything
|
|
14
25
|
warn '[DEPRECATION] Amazon S3 driver: `:signed_url` is deprecated. Please use `:response_type` instead.'
|
15
26
|
config[:response_type] = :public_url
|
16
27
|
end
|
17
|
-
|
18
|
-
|
28
|
+
merged_config = DEFAULTS.merge(config)
|
29
|
+
self.class.authentication_klass ||= self.class.default_authentication_klass
|
30
|
+
super(merged_config, *args)
|
19
31
|
end
|
20
32
|
|
21
33
|
def icon
|
@@ -23,85 +35,36 @@ module BrowseEverything
|
|
23
35
|
end
|
24
36
|
|
25
37
|
def validate_config
|
26
|
-
if config.values_at(:app_key, :app_secret).compact.length == 1
|
27
|
-
|
28
|
-
end
|
29
|
-
unless RESPONSE_TYPES.include?(config[:response_type].to_sym)
|
30
|
-
raise BrowseEverything::InitializationError, "Amazon S3 driver: Valid response types: #{RESPONSE_TYPES.join(',')}"
|
31
|
-
end
|
38
|
+
raise InitializationError, 'Amazon S3 driver: If either :app_key or :app_secret is provided, both must be.' if config.values_at(:app_key, :app_secret).compact.length == 1
|
39
|
+
raise InitializationError, "Amazon S3 driver: Valid response types: #{RESPONSE_TYPES.join(',')}" unless RESPONSE_TYPES.include?(config[:response_type].to_sym)
|
32
40
|
return if CONFIG_KEYS.all? { |key| config[key].present? }
|
33
|
-
raise
|
41
|
+
raise InitializationError, "Amazon S3 driver requires #{CONFIG_KEYS.join(',')}"
|
34
42
|
end
|
35
43
|
|
44
|
+
# Retrieve the entries from the S3 Bucket
|
36
45
|
# @return [Array<BrowseEverything::FileEntry>]
|
37
|
-
# Appends / to the path before querying S3
|
38
46
|
def contents(path = '')
|
39
47
|
path = File.join(path, '') unless path.empty?
|
40
48
|
init_entries(path)
|
41
49
|
generate_listing(path)
|
42
|
-
|
43
|
-
end
|
44
|
-
|
45
|
-
def generate_listing(path)
|
46
|
-
listing = client.list_objects(bucket: config[:bucket], delimiter: '/', prefix: full_path(path))
|
47
|
-
add_directories(listing)
|
48
|
-
add_files(listing, path)
|
49
|
-
end
|
50
|
-
|
51
|
-
def add_directories(listing)
|
52
|
-
listing.common_prefixes.each do |prefix|
|
53
|
-
entries << entry_for(from_base(prefix.prefix), 0, Time.current, true)
|
54
|
-
end
|
50
|
+
@sorter.call(@entries)
|
55
51
|
end
|
56
52
|
|
57
|
-
def
|
58
|
-
|
59
|
-
key = from_base(entry.key)
|
60
|
-
unless strip(key) == strip(path)
|
61
|
-
entries << entry_for(key, entry.size, entry.last_modified, false)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def sort_entries
|
67
|
-
entries.sort do |a, b|
|
68
|
-
if b.container?
|
69
|
-
a.container? ? a.name.downcase <=> b.name.downcase : 1
|
70
|
-
else
|
71
|
-
a.container? ? -1 : a.name.downcase <=> b.name.downcase
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def init_entries(path)
|
77
|
-
@entries = if path.empty?
|
78
|
-
[]
|
79
|
-
else
|
80
|
-
[BrowseEverything::FileEntry.new(Pathname(path).join('..').to_s, '', '..',
|
81
|
-
0, Time.current, true)]
|
82
|
-
end
|
83
|
-
end
|
53
|
+
def link_for(path)
|
54
|
+
obj = bucket.object(full_path(path))
|
84
55
|
|
85
|
-
|
86
|
-
|
87
|
-
|
56
|
+
extras = {
|
57
|
+
file_name: File.basename(path),
|
58
|
+
expires: (config[:expires_in] if config[:response_type] == :signed_url)
|
59
|
+
}.compact
|
88
60
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
entry.last_modified, false
|
95
|
-
)
|
96
|
-
end
|
61
|
+
url = case config[:response_type].to_sym
|
62
|
+
when :signed_url then obj.presigned_url(:get, expires_in: config[:expires_in])
|
63
|
+
when :public_url then obj.public_url
|
64
|
+
when :s3_uri then "s3://#{obj.bucket_name}/#{obj.key}"
|
65
|
+
end
|
97
66
|
|
98
|
-
|
99
|
-
obj = bucket.object(full_path(path))
|
100
|
-
case config[:response_type].to_sym
|
101
|
-
when :signed_url then obj.presigned_url(:get, expires_in: 14400)
|
102
|
-
when :public_url then obj.public_url
|
103
|
-
when :s3_uri then "s3://#{obj.bucket_name}/#{obj.key}"
|
104
|
-
end
|
67
|
+
[url, extras]
|
105
68
|
end
|
106
69
|
|
107
70
|
def authorized?
|
@@ -112,10 +75,6 @@ module BrowseEverything
|
|
112
75
|
@bucket ||= Aws::S3::Bucket.new(config[:bucket], client: client)
|
113
76
|
end
|
114
77
|
|
115
|
-
def client
|
116
|
-
@client ||= Aws::S3::Client.new(aws_config)
|
117
|
-
end
|
118
|
-
|
119
78
|
private
|
120
79
|
|
121
80
|
def strip(path)
|
@@ -136,6 +95,53 @@ module BrowseEverything
|
|
136
95
|
result[:region] = config[:region] if config.key?(:region)
|
137
96
|
result
|
138
97
|
end
|
98
|
+
|
99
|
+
def session
|
100
|
+
AuthenticationFactory.new(
|
101
|
+
self.class.authentication_klass,
|
102
|
+
aws_config
|
103
|
+
)
|
104
|
+
end
|
105
|
+
|
106
|
+
def authenticate
|
107
|
+
session.authenticate
|
108
|
+
end
|
109
|
+
|
110
|
+
def client
|
111
|
+
@client ||= authenticate
|
112
|
+
end
|
113
|
+
|
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
|
+
def entry_for(name, size, date, dir)
|
124
|
+
BrowseEverything::FileEntry.new(name, [key, name].join(':'), File.basename(name), size, date, dir)
|
125
|
+
end
|
126
|
+
|
127
|
+
def add_directories(listing)
|
128
|
+
listing.common_prefixes.each do |prefix|
|
129
|
+
@entries << entry_for(from_base(prefix.prefix), 0, Time.current, true)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def add_files(listing, path)
|
134
|
+
listing.contents.each do |entry|
|
135
|
+
key = from_base(entry.key)
|
136
|
+
@entries << entry_for(key, entry.size, entry.last_modified, false) unless strip(key) == strip(path)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def generate_listing(path)
|
141
|
+
listing = client.list_objects(bucket: config[:bucket], delimiter: '/', prefix: full_path(path))
|
142
|
+
add_directories(listing)
|
143
|
+
add_files(listing, path)
|
144
|
+
end
|
139
145
|
end
|
140
146
|
end
|
141
147
|
end
|