Skip to content

Commit fc459a2

Browse files
author
Mattia Roccoberton
committed
Adjust some internal methods
1 parent ab501b4 commit fc459a2

3 files changed

Lines changed: 106 additions & 55 deletions

File tree

lib/active_storage/service/db_service.rb

Lines changed: 64 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# frozen_string_literal: true
22

33
module ActiveStorage
4+
# Wraps a DB table as an Active Storage service. See ActiveStorage::Service
5+
# for the generic API documentation that applies to all services.
46
class Service::DBService < Service
57
def initialize(**)
68
@chunk_size = ENV.fetch('ASDB_CHUNK_SIZE') { 1.megabytes }
@@ -22,22 +24,27 @@ def download(key, &block)
2224
else
2325
instrument :download, key: key do
2426
record = ::ActiveStorageDB::File.find_by(ref: key)
25-
record&.data || raise(ActiveStorage::FileNotFoundError)
27+
raise(ActiveStorage::FileNotFoundError) unless record
28+
29+
record.data
2630
end
2731
end
2832
end
2933

3034
def download_chunk(key, range)
3135
instrument :download_chunk, key: key, range: range do
3236
chunk_select = "SUBSTRING(data FROM #{range.begin + 1} FOR #{range.size}) AS chunk"
33-
::ActiveStorageDB::File.select(chunk_select).find_by(ref: key)&.chunk ||
34-
raise(ActiveStorage::FileNotFoundError)
37+
record = ::ActiveStorageDB::File.select(chunk_select).find_by(ref: key)
38+
raise(ActiveStorage::FileNotFoundError) unless record
39+
40+
record.chunk
3541
end
3642
end
3743

3844
def delete(key)
3945
instrument :delete, key: key do
4046
::ActiveStorageDB::File.find_by(ref: key)&.destroy
47+
# Ignore files already deleted
4148
end
4249
end
4350

@@ -55,31 +62,14 @@ def exist?(key)
5562
end
5663
end
5764

58-
def url(key, expires_in:, filename:, disposition:, content_type:)
59-
instrument :url, key: key do |payload|
60-
content_disposition = content_disposition_with(type: disposition, filename: filename)
61-
verified_key_with_expiration = ActiveStorage.verifier.generate(
62-
{
63-
key: key,
64-
disposition: content_disposition,
65-
content_type: content_type
66-
},
67-
expires_in: expires_in,
68-
purpose: :blob_key
69-
)
70-
current_uri = URI.parse(current_host)
71-
generated_url = url_helpers.service_url(
72-
verified_key_with_expiration,
73-
protocol: current_uri.scheme,
74-
host: current_uri.host,
75-
port: current_uri.port,
76-
disposition: content_disposition,
77-
content_type: content_type,
78-
filename: filename
79-
)
80-
payload[:url] = generated_url
81-
82-
generated_url
65+
if Rails::VERSION::MAJOR == 6 && Rails::VERSION::MINOR == 0
66+
def url(key, expires_in:, filename:, disposition:, content_type:)
67+
instrument :url, key: key do |payload|
68+
opts = { expires_in: expires_in, filename: filename, content_type: content_type, disposition: disposition }
69+
generate_url(key, opts).tap do |generated_url|
70+
payload[:url] = generated_url
71+
end
72+
end
8373
end
8474
end
8575

@@ -90,15 +80,16 @@ def url_for_direct_upload(key, expires_in:, content_type:, content_length:, chec
9080
key: key,
9181
content_type: content_type,
9282
content_length: content_length,
93-
checksum: checksum
83+
checksum: checksum,
84+
service_name: respond_to?(:name) ? name : 'db'
9485
},
9586
expires_in: expires_in,
9687
purpose: :blob_token
9788
)
98-
generated_url = url_helpers.update_service_url(verified_token_with_expiration, host: current_host)
99-
payload[:url] = generated_url
10089

101-
generated_url
90+
url_helpers.update_service_url(verified_token_with_expiration, url_options).tap do |generated_url|
91+
payload[:url] = generated_url
92+
end
10293
end
10394
end
10495

@@ -119,6 +110,39 @@ def current_host
119110
end
120111
end
121112

113+
def private_url(key, expires_in:, filename:, content_type:, disposition:, **)
114+
generate_url(key, expires_in: expires_in, filename: filename, content_type: content_type, disposition: disposition)
115+
end
116+
117+
def public_url(key, filename:, content_type: nil, disposition: :attachment, **)
118+
generate_url(key, expires_in: nil, filename: filename, content_type: content_type, disposition: disposition)
119+
end
120+
121+
def generate_url(key, expires_in:, filename:, content_type:, disposition:)
122+
content_disposition = content_disposition_with(type: disposition, filename: filename)
123+
verified_key_with_expiration = ActiveStorage.verifier.generate(
124+
{
125+
key: key,
126+
disposition: content_disposition,
127+
content_type: content_type,
128+
service_name: respond_to?(:name) ? name : 'db'
129+
},
130+
expires_in: expires_in,
131+
purpose: :blob_key
132+
)
133+
134+
current_uri = URI.parse(current_host)
135+
url_helpers.service_url(
136+
verified_key_with_expiration,
137+
protocol: current_uri.scheme,
138+
host: current_uri.host,
139+
port: current_uri.port,
140+
disposition: content_disposition,
141+
content_type: content_type,
142+
filename: filename
143+
)
144+
end
145+
122146
def ensure_integrity_of(key, checksum)
123147
file = ::ActiveStorageDB::File.find_by(ref: key)
124148
return if Digest::MD5.base64digest(file.data) == checksum
@@ -140,5 +164,13 @@ def stream(key)
140164
def url_helpers
141165
@url_helpers ||= ::ActiveStorageDB::Engine.routes.url_helpers
142166
end
167+
168+
def url_options
169+
if ActiveStorage::Current.respond_to? :url_options
170+
ActiveStorage::Current.url_options
171+
else
172+
{ host: ActiveStorage::Current.host }
173+
end
174+
end
143175
end
144176
end

spec/requests/file_controller_spec.rb

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ def create_blob_before_direct_upload(byte_size:, checksum:, filename: 'hello.txt
2121
end
2222

2323
let(:blob) { create_blob(filename: 'img.jpg', content_type: 'image/jpg') }
24-
let(:host) { 'http://test.example.com:3001' }
2524
let(:url_options) do
2625
{
2726
protocol: 'http://',
2827
host: 'test.example.com',
2928
port: '3001',
3029
}
3130
end
31+
let(:host) { "#{url_options[:protocol]}#{url_options[:host]}:#{url_options[:port]}" }
3232
let(:engine_url_helpers) { ::ActiveStorageDB::Engine.routes.url_helpers }
3333

3434
before do
@@ -137,5 +137,19 @@ def create_blob_before_direct_upload(byte_size:, checksum:, filename: 'hello.txt
137137
expect(response).to have_http_status(:not_found)
138138
end
139139
end
140+
141+
context 'when the integrity check fails' do
142+
let(:invalid_file) { create(:active_storage_db_file, data: 'Some other data') }
143+
144+
before do
145+
allow(::ActiveStorageDB::File).to receive(:find_by).and_return(invalid_file)
146+
end
147+
148+
it 'fails to upload' do
149+
put blob.service_url_for_direct_upload, params: data, headers: { 'Content-Type' => 'text/plain' }
150+
151+
expect(response).to have_http_status(:unprocessable_entity)
152+
end
153+
end
140154
end
141155
end

spec/service/active_storage/service/db_service_spec.rb

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
RSpec.describe ActiveStorage::Service::DBService do
44
let(:fixture_data) { build(:active_storage_db_file).data }
55
let(:content_type) { 'image/png' }
6-
let(:host) { 'http://test.example.com:3001' }
76
let(:url_options) do
87
{
98
protocol: 'http://',
109
host: 'test.example.com',
1110
port: '3001',
1211
}
1312
end
13+
let(:host) { "#{url_options[:protocol]}#{url_options[:host]}:#{url_options[:port]}" }
1414

1515
let(:checksum) { Digest::MD5.base64digest(fixture_data) }
1616
let(:key) { SecureRandom.base58(24) }
@@ -22,6 +22,32 @@
2222
allow(ENV).to receive(:fetch).with('ASDB_CHUNK_SIZE').and_return(100)
2323
end
2424

25+
describe 'public URL generation' do
26+
let(:config) do
27+
{
28+
db: {
29+
service: 'db',
30+
public: true
31+
}
32+
}
33+
end
34+
35+
before do
36+
if ActiveStorage::Current.respond_to? :url_options
37+
allow(ActiveStorage::Current).to receive(:url_options).and_return(url_options)
38+
else
39+
allow(ActiveStorage::Current).to receive(:host).and_return(host)
40+
end
41+
end
42+
43+
it 'returns a public URL' do
44+
filename = ActiveStorage::Filename.new('some_filename')
45+
service = ActiveStorage::Service.configure(:db, config)
46+
url = service.url('some_key', filename: filename, disposition: :inline, content_type: 'text/plain', expires_in: nil)
47+
expect(url).to match %r{#{host}/active_storage_db/files/}
48+
end
49+
end
50+
2551
describe '.delete' do
2652
subject(:delete) { service.delete(key) }
2753

@@ -137,27 +163,6 @@
137163
end
138164
end
139165

140-
describe '.url' do
141-
subject do
142-
service.url(key, expires_in: 5.minutes, disposition: :inline, filename: filename, content_type: content_type)
143-
end
144-
145-
let(:filename) { ActiveStorage::Filename.new('avatar.png') }
146-
147-
before do
148-
upload
149-
if ActiveStorage::Current.respond_to? :url_options
150-
allow(ActiveStorage::Current).to receive(:url_options).and_return(url_options)
151-
else
152-
allow(ActiveStorage::Current).to receive(:host).and_return(host)
153-
end
154-
end
155-
156-
after { service.delete(key) }
157-
158-
it { is_expected.to start_with "#{url_options[:protocol]}#{url_options[:host]}" }
159-
end
160-
161166
describe '.url_for_direct_upload' do
162167
subject do
163168
service.url_for_direct_upload(

0 commit comments

Comments
 (0)