Skip to content

Commit f45f2f4

Browse files
committed
(#917) Upload envelope graphs to S3 during publishing
1 parent faf4eb2 commit f45f2f4

File tree

6 files changed

+172
-3
lines changed

6 files changed

+172
-3
lines changed

app/api/v1/publish.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require 'policies/envelope_policy'
22
require 'services/publish_interactor'
3+
require 'services/sync_envelope_graph_with_s3'
34

45
module API
56
module V1
@@ -34,7 +35,7 @@ module Publish
3435
optional :published_by, type: String
3536
use :update_if_exists
3637
end
37-
post do
38+
post do # rubocop:todo Metrics/BlockLength
3839
authorize Envelope, :create?
3940

4041
secondary_token_header = request.headers['Secondary-Token']
@@ -60,6 +61,7 @@ module Publish
6061
)
6162

6263
if interactor.success?
64+
SyncEnvelopeGraphWithS3.add(interactor.envelope)
6365
present interactor.envelope, with: API::Entities::Envelope
6466
update_if_exists? ? status(:ok) : status(:created)
6567
else
@@ -92,6 +94,7 @@ module Publish
9294
delete do
9395
authorize Envelope, :destroy?
9496
@envelope.destroy
97+
SyncEnvelopeGraphWithS3.remove(@envelope)
9598
body ''
9699
end
97100

app/jobs/publish_envelope_job.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ def perform(publish_request_id)
88
publish_result = PublishInteractor.call(**publish_args)
99

1010
if publish_result.success?
11+
SyncEnvelopeGraphWithS3.add(publish_result.envelope)
1112
publish_request.complete(publish_result.envelope.id)
1213
else
1314
publish_request.fail(publish_result.error)
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Uploads or deletes an envelope graph from the S3 bucket
2+
class SyncEnvelopeGraphWithS3
3+
attr_reader :envelope
4+
5+
delegate :envelope_community, :envelope_ceterms_ctid, to: :envelope
6+
7+
def initialize(envelope)
8+
@envelope = envelope
9+
end
10+
11+
class << self
12+
def add(envelope)
13+
new(envelope).add
14+
end
15+
16+
def remove(envelope)
17+
new(envelope).remove
18+
end
19+
end
20+
21+
def add
22+
return unless s3_bucket_name
23+
24+
s3_object.put(body: envelope.processed_resource.to_json, content_type: 'application/json')
25+
s3_object.public_url
26+
end
27+
28+
def remove
29+
return unless s3_bucket_name
30+
31+
s3_object.delete
32+
end
33+
34+
def s3_bucket
35+
@s3_bucket ||= s3_resource.bucket(s3_bucket_name)
36+
end
37+
38+
def s3_bucket_name
39+
ENV['ENVELOPE_GRAPHS_BUCKET'].presence
40+
end
41+
42+
def s3_key
43+
"#{envelope_community.name}/#{envelope_ceterms_ctid}.json"
44+
end
45+
46+
def s3_object
47+
s3_bucket.object(s3_key)
48+
end
49+
50+
def s3_resource
51+
@s3_resource ||= Aws::S3::Resource.new(region: ENV['AWS_REGION'].presence)
52+
end
53+
end

spec/api/v1/publish_spec.rb

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
end
9999

100100
context 'default community' do # rubocop:todo RSpec/ContextWording, RSpec/MultipleMemoizedHelpers
101+
let(:community) { ce_registry }
101102
let(:user2) { create(:user) }
102103

103104
# rubocop:todo RSpec/NestedGroups
@@ -131,8 +132,18 @@
131132
# rubocop:todo RSpec/NestedGroups
132133
context 'new envelope' do # rubocop:todo RSpec/ContextWording, RSpec/NestedGroups
133134
# rubocop:enable RSpec/NestedGroups
134-
# rubocop:todo RSpec/ExampleLength
135+
# rubocop:todo RSpec/ExampleLength, RSpec/MultipleExpectations
135136
it 'returns the newly created envelope with a 201 Created HTTP status code' do
137+
expect(SyncEnvelopeGraphWithS3).to receive(:add) do |envelope| # rubocop:todo RSpec/MessageSpies
138+
expect(envelope.envelope_community).to eq(community)
139+
expect(envelope.process_resource).to eq(JSON(resource_json))
140+
end.exactly(:twice)
141+
142+
expect(SyncEnvelopeGraphWithS3).to receive(:add) do |envelope| # rubocop:todo RSpec/MessageSpies
143+
expect(envelope.envelope_community).to eq(community)
144+
expect(envelope.process_resource).to eq(JSON(updated_resource_json))
145+
end
146+
136147
# New envelope
137148
travel_to now do
138149
post "/resources/organizations/#{organization._ctid}/documents",
@@ -199,7 +210,12 @@
199210
envelope
200211
end
201212

202-
it 'updates the envelope' do # rubocop:todo RSpec/ExampleLength
213+
it 'updates the envelope' do # rubocop:todo RSpec/ExampleLength, RSpec/MultipleExpectations
214+
allow(SyncEnvelopeGraphWithS3).to receive(:add) do |envelope|
215+
expect(envelope.envelope_community).to eq(community)
216+
expect(envelope.process_resource).to eq(JSON(updated_resource_json))
217+
end
218+
203219
travel_to now do
204220
expect do
205221
post "/resources/organizations/#{organization._ctid}/documents",
@@ -233,6 +249,11 @@
233249
before do
234250
create(:organization_publisher, organization: organization, publisher: user.publisher)
235251

252+
allow(SyncEnvelopeGraphWithS3).to receive(:add) do |envelope|
253+
expect(envelope.envelope_community).to eq(community) # rubocop:todo RSpec/ExpectInHook
254+
expect(envelope.process_resource).to eq(JSON(resource_json)) # rubocop:todo RSpec/ExpectInHook
255+
end
256+
236257
travel_to now do
237258
post "/resources/organizations/#{organization._ctid}/documents",
238259
resource_json,
@@ -282,6 +303,11 @@
282303
super_publisher_user = create(:user, publisher: super_publisher)
283304
token = "Token #{super_publisher_user.auth_tokens.first.value}"
284305

306+
allow(SyncEnvelopeGraphWithS3).to receive(:add) do |envelope|
307+
expect(envelope.envelope_community).to eq(community) # rubocop:todo RSpec/ExpectInHook
308+
expect(envelope.process_resource).to eq(JSON(resource_json)) # rubocop:todo RSpec/ExpectInHook
309+
end
310+
285311
travel_to now do
286312
post "/resources/organizations/#{organization._ctid}/documents",
287313
resource_json, 'Authorization' => token
@@ -366,6 +392,11 @@
366392
publisher: user.publisher
367393
)
368394

395+
allow(SyncEnvelopeGraphWithS3).to receive(:add) do |envelope|
396+
expect(envelope.envelope_community).to eq(community) # rubocop:todo RSpec/ExpectInHook
397+
expect(envelope.process_resource).to eq(JSON(resource_json)) # rubocop:todo RSpec/ExpectInHook
398+
end
399+
369400
travel_to now do
370401
post "/resources/organizations/#{organization._ctid}/documents?" \
371402
"published_by=#{publishing_organization._ctid}",
@@ -436,6 +467,11 @@
436467
end
437468

438469
it 'creates an envelope with provisional publication status' do
470+
allow(SyncEnvelopeGraphWithS3).to receive(:add) do |envelope|
471+
expect(envelope.envelope_community).to eq(community)
472+
expect(envelope.process_resource).to eq(JSON(resource_json))
473+
end
474+
439475
expect do
440476
post "/resources/organizations/#{organization._ctid}/documents",
441477
resource_json, 'Authorization' => "Token #{user.auth_token.value}"
@@ -518,6 +554,8 @@
518554
# rubocop:enable RSpec/NestedGroups
519555
# rubocop:todo RSpec/MultipleExpectations
520556
it 'deletes the envelope' do # rubocop:todo RSpec/ExampleLength, RSpec/MultipleExpectations
557+
allow(SyncEnvelopeGraphWithS3).to receive(:remove).with(envelope)
558+
521559
# rubocop:enable RSpec/MultipleExpectations
522560
expect do
523561
delete_envelope
@@ -591,6 +629,8 @@
591629
# rubocop:enable RSpec/NestedGroups
592630
# rubocop:todo RSpec/MultipleExpectations
593631
it 'deletes the envelope' do # rubocop:todo RSpec/ExampleLength, RSpec/MultipleExpectations
632+
allow(SyncEnvelopeGraphWithS3).to receive(:remove).with(envelope)
633+
594634
# rubocop:enable RSpec/MultipleExpectations
595635
expect do
596636
delete_envelope

spec/jobs/publish_envelope_job_spec.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@
2727

2828
# rubocop:todo RSpec/MultipleExpectations
2929
it 'creates an envelope and marks the request as completed successfully' do
30+
expect(SyncEnvelopeGraphWithS3).to receive(:add) do |envelope| # rubocop:todo RSpec/MessageSpies
31+
expect(envelope.envelope_community).to eq(envelope_community)
32+
# rubocop:todo Layout/LineLength
33+
expect(envelope.processed_resource).to eq(JSON(JSON(publish_request.request_params).fetch('raw_resource')))
34+
# rubocop:enable Layout/LineLength
35+
end
36+
3037
# rubocop:enable RSpec/MultipleExpectations
3138
subject.new.perform(publish_request.id) # rubocop:todo RSpec/NamedSubject
3239
publish_request.reload
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
RSpec.describe SyncEnvelopeGraphWithS3 do # rubocop:todo RSpec/MultipleMemoizedHelpers
2+
let(:envelope) { create(:envelope, :from_cer) }
3+
let(:s3_bucket) { double('s3_bucket') } # rubocop:todo RSpec/VerifiedDoubles
4+
let(:s3_bucket_name) { Faker::Lorem.word }
5+
let(:s3_object) { double('s3_object') } # rubocop:todo RSpec/VerifiedDoubles
6+
let(:s3_region) { 'aws-s3_region-test' }
7+
let(:s3_resource) { double('s3_resource') } # rubocop:todo RSpec/VerifiedDoubles
8+
let(:s3_url) { Faker::Internet.url }
9+
10+
context 'without bucket' do # rubocop:todo RSpec/MultipleMemoizedHelpers
11+
describe '.add' do # rubocop:todo RSpec/MultipleMemoizedHelpers
12+
it 'does nothing' do
13+
expect { described_class.add(envelope) }.not_to raise_error
14+
end
15+
end
16+
17+
describe '.remove' do # rubocop:todo RSpec/MultipleMemoizedHelpers
18+
it 'does nothing' do
19+
expect { described_class.remove(envelope) }.not_to raise_error
20+
end
21+
end
22+
end
23+
24+
context 'with bucket' do # rubocop:todo RSpec/MultipleMemoizedHelpers
25+
before do
26+
ENV['AWS_REGION'] = s3_region
27+
ENV['ENVELOPE_GRAPHS_BUCKET'] = s3_bucket_name
28+
29+
allow(Aws::S3::Resource).to receive(:new)
30+
.with(region: s3_region)
31+
.and_return(s3_resource)
32+
33+
allow(s3_resource).to receive(:bucket).with(s3_bucket_name).and_return(s3_bucket)
34+
35+
allow(s3_bucket).to receive(:object)
36+
.with("ce_registry/#{envelope.envelope_ceterms_ctid}.json")
37+
.and_return(s3_object)
38+
end
39+
40+
describe '.add' do # rubocop:todo RSpec/MultipleMemoizedHelpers
41+
before do
42+
allow(s3_object).to receive(:put).with(
43+
body: envelope.processed_resource.to_json,
44+
content_type: 'application/json'
45+
)
46+
47+
allow(s3_object).to receive(:public_url).and_return(s3_url)
48+
end
49+
50+
it 'uploads the s3_resource to S3' do
51+
expect(described_class.add(envelope)).to eq(s3_url)
52+
end
53+
end
54+
55+
describe '.remove' do # rubocop:todo RSpec/MultipleMemoizedHelpers
56+
before do
57+
allow(s3_object).to receive(:delete)
58+
end
59+
60+
it 'uploads the s3_resource to S3' do
61+
expect { described_class.remove(envelope) }.not_to raise_error
62+
end
63+
end
64+
end
65+
end

0 commit comments

Comments
 (0)