Skip to content

Commit 5671fb6

Browse files
committed
Also save assets to bucket so they can be served from CDN
Many assets from the asset library are rendered on the page at once which will cause a lot of requests to editor API. This is being stored in the editor-assets r2 bucket. Note that region isn't used by has to be set to a valid value.
1 parent 524e0cb commit 5671fb6

2 files changed

Lines changed: 103 additions & 5 deletions

File tree

lib/scratch_asset_importer.rb

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,67 @@ def initialize(asset_name, asset_base_url)
3232
end
3333

3434
def import
35+
create_scratch_asset
36+
save_to_editor_asset_bucket
37+
rescue StandardError => e
38+
Rails.logger.error("Failed to import asset #{asset_name}: #{e.message}")
39+
end
40+
41+
def asset
42+
@asset ||= begin
43+
sleep(ASSET_FETCHING_DELAY)
44+
connection.get("#{asset_name}/get/")
45+
end
46+
end
47+
48+
def create_scratch_asset
3549
return if ScratchAsset.global_assets.exists?(filename: asset_name)
3650

37-
sleep(ASSET_FETCHING_DELAY)
38-
asset = connection.get("#{asset_name}/get/")
51+
io = StringIO.new(asset.body)
52+
3953
ScratchAsset.create!(filename: asset_name, project_id: nil, uploaded_user_id: nil)
4054
.file
41-
.attach(io: StringIO.new(asset.body), filename: asset_name)
42-
rescue StandardError => e
43-
Rails.logger.error("Failed to import asset #{asset_name}: #{e.message}")
55+
.attach(io:, filename: asset_name)
56+
end
57+
58+
def save_to_editor_asset_bucket
59+
return unless save_to_editor_asset_bucket?
60+
61+
body = StringIO.new(asset.body)
62+
63+
s3_client.put_object(
64+
bucket: ENV.fetch('EDITOR_ASSETS_BUCKET'),
65+
key: asset_key,
66+
body:,
67+
content_type: asset_content_type
68+
)
69+
end
70+
71+
def save_to_editor_asset_bucket?
72+
return false unless ENV['EDITOR_ASSETS_BUCKET']
73+
74+
s3_client.head_object(bucket: ENV.fetch('EDITOR_ASSETS_BUCKET'), key: asset_key)
75+
false
76+
rescue Aws::S3::Errors::NotFound
77+
true
78+
end
79+
80+
def asset_key
81+
"internalapi/asset/#{asset_name}/get/"
82+
end
83+
84+
def asset_content_type
85+
extension = File.extname(asset_name).delete('.')
86+
Mime::Type.lookup_by_extension(extension).to_s
87+
end
88+
89+
def s3_client
90+
@s3_client ||= Aws::S3::Client.new(
91+
access_key_id: ENV.fetch('EDITOR_ASSETS_ACCESS_KEY_ID'),
92+
secret_access_key: ENV.fetch('EDITOR_ASSETS_SECRET_ACCESS_KEY'),
93+
endpoint: ENV.fetch('EDITOR_ASSETS_ENDPOINT'),
94+
region: 'auto'
95+
)
4496
end
4597

4698
def connection

spec/lib/scratch_asset_importer_spec.rb

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,51 @@
5959
expect(ScratchAsset.find_by(filename: '123abc.png')).not_to be_present
6060
expect(ScratchAsset.find_by(filename: '456xyz.png')).to be_present
6161
end
62+
63+
describe 'syncing to editor asset bucket' do
64+
let(:s3_client) { instance_double(Aws::S3::Client) }
65+
66+
around do |example|
67+
editor_asset_env_vars = {
68+
EDITOR_ASSETS_BUCKET: 'test-bucket',
69+
EDITOR_ASSETS_ACCESS_KEY_ID: 'test-access-key-id',
70+
EDITOR_ASSETS_SECRET_ACCESS_KEY: 'test-secret-access-key',
71+
EDITOR_ASSETS_ENDPOINT: 'https://r2.example.com'
72+
}
73+
ClimateControl.modify(editor_asset_env_vars) do
74+
example.run
75+
end
76+
end
77+
78+
before do
79+
allow(Aws::S3::Client).to receive(:new).and_return(s3_client)
80+
allow(s3_client).to receive(:head_object).and_raise(Aws::S3::Errors::NotFound.new(nil, nil))
81+
allow(s3_client).to receive(:put_object)
82+
end
83+
84+
it 'saves asset to editor asset bucket if it does not exist' do
85+
image = Rails.root.join('spec/fixtures/files/test_image_1.png').read
86+
stub_request(:get, 'https://example.net/internalapi/asset/123abc.png/get/').to_return(status: 200, body: image)
87+
88+
described_class.import_all(['123abc.png'], 'https://example.net/internalapi/asset/')
89+
90+
expect(s3_client).to have_received(:put_object).with(
91+
bucket: 'test-bucket',
92+
key: 'internalapi/asset/123abc.png/get/',
93+
body: instance_of(StringIO),
94+
content_type: 'image/png'
95+
)
96+
end
97+
98+
it 'does not save asset to editor asset bucket if it already exists' do
99+
image = Rails.root.join('spec/fixtures/files/test_image_1.png').read
100+
stub_request(:get, 'https://example.net/internalapi/asset/123abc.png/get/').to_return(status: 200, body: image)
101+
102+
allow(s3_client).to receive(:head_object).with(bucket: 'test-bucket', key: 'internalapi/asset/123abc.png/get/').and_return(true)
103+
104+
described_class.import_all(['123abc.png'], 'https://example.net/internalapi/asset/')
105+
expect(s3_client).not_to have_received(:put_object)
106+
end
107+
end
62108
end
63109
end

0 commit comments

Comments
 (0)