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.
- 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
|