Skip to content

Commit

Permalink
Batch Aggregation mailer (#4352)
Browse files Browse the repository at this point in the history
* Add aggregation blob storage base URL to templates

* Call mailer worker if status is updated

* Mailer and mailer worker (and specs)

* Hound

* Prefer deliver_now
  • Loading branch information
zwolf authored Jul 1, 2024
1 parent 4eca730 commit ec4591d
Show file tree
Hide file tree
Showing 9 changed files with 163 additions and 0 deletions.
6 changes: 6 additions & 0 deletions app/controllers/api/v1/aggregations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,10 @@ def create
rescue AggregationClient::ConnectionError
json_api_render(:service_unavailable, 'The aggregation service is unavailable or not responding')
end

def update
super do |agg|
AggregationCompletedMailerWorker.perform_async(agg.id) if update_params[:status]
end
end
end
19 changes: 19 additions & 0 deletions app/mailers/aggregation_completed_mailer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

class AggregationCompletedMailer < ApplicationMailer
layout false

def aggregation_complete(agg)
@user = User.find(agg.user_id)
@email_to = @user.email
base_url = ENV.fetch('AGGREGATION_STORAGE_BASE_URL', '')
@zip_url = "#{base_url}/#{agg.uuid}/#{agg.uuid}.zip"
@reductions_url = "#{base_url}/#{agg.uuid}/reductions.csv"

@success = agg.completed?
agg_status = @success ? 'was successful!' : 'failed'
subject = "Your workflow aggregation #{agg_status}"

mail(to: @email_to, subject: subject)
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
Hello,

Your workflow aggregation has finished processing.

<% if @success %>
The workflow was aggregated successfully.

Click here to download a zip file containing the following:
* Workflow classifications export
* Workflow export
* Extracts (one file per task)
* Reductions

<%= @zip_url %>

Click here to download the reductions file by itself:

<%= @reductions_url %>

<% else %>
The workflow failed to aggregate. Please contact us or try again.
<% end %>

If you have questions or if there were any issues with your aggregation, please email [email protected].

Cheers,
The Zooniverse Team

This is an automated email, please do not respond.

To manage your Zooniverse email subscription preferences visit https://zooniverse.org/settings

To unsubscribe to all Zooniverse messages please visit https://zooniverse.org/unsubscribe
Please be aware that the above link will unsubscribe you from ALL Zooniverse emails.
12 changes: 12 additions & 0 deletions app/workers/aggregation_completed_mailer_worker.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# frozen_string_literal: true

class AggregationCompletedMailerWorker
include Sidekiq::Worker

sidekiq_options queue: :data_high

def perform(agg_id)
aggregation = Aggregation.find(agg_id)
AggregationCompletedMailer.aggregation_complete(aggregation).deliver_now
end
end
1 change: 1 addition & 0 deletions kubernetes/deployment-production.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ data:
TALK_API_APPLICATION: '1'
USER_SUBJECT_LIMIT: '10000'
AGGREGATION_HOST: http://aggregation-caesar/
AGGREGATION_STORAGE_BASE_URL: https://aggregationdata.blob.core.windows.net
---
apiVersion: apps/v1
kind: Deployment
Expand Down
1 change: 1 addition & 0 deletions kubernetes/deployment-staging.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ data:
TALK_API_APPLICATION: '1'
USER_SUBJECT_LIMIT: '100'
AGGREGATION_HOST: http://aggregation-staging-app/
AGGREGATION_STORAGE_BASE_URL: https://aggregationdata.blob.core.windows.net
---
apiVersion: apps/v1
kind: Deployment
Expand Down
20 changes: 20 additions & 0 deletions spec/controllers/api/v1/aggregations_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,26 @@
end

it_behaves_like 'is updatable'

context 'with the mailer worker' do
before do
default_request scopes: scopes, user_id: authorized_user.id
allow(AggregationCompletedMailerWorker).to receive(:perform_async).with(resource.id)
end

let(:params) { update_params.merge(id: resource.id) }

it 'calls the mailer worker' do
put :update, params: params
expect(AggregationCompletedMailerWorker).to have_received(:perform_async).with(resource.id)
end

it 'does not call the mailer if status is not an updated param' do
params[:aggregations].delete(:status)
put :update, params: params
expect(AggregationCompletedMailerWorker).not_to have_received(:perform_async)
end
end
end

describe '#destroy' do
Expand Down
52 changes: 52 additions & 0 deletions spec/mailers/aggregation_completed_mailer_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe AggregationCompletedMailer, type: :mailer do
let(:base_url) { 'https://example.com' }

before do
allow(ENV).to receive(:fetch).with('AGGREGATION_STORAGE_BASE_URL', '').and_return(base_url)
end

describe '#aggregation_complete' do
let(:mail) { described_class.aggregation_complete(aggregation) }

context 'with a successful aggregation' do
let(:aggregation) { create(:aggregation, status: 'completed', uuid: 'asdf123asdf') }
let(:uuid) { aggregation.uuid }

it 'mails the user' do
expect(mail.to).to match_array([aggregation.user.email])
end

it 'includes the subject' do
expect(mail.subject).to include('Your workflow aggregation was successful!')
end

it 'includes the zip file link' do
expect(mail.body.encoded).to include("#{base_url}/#{uuid}/#{uuid}.zip")
end

it 'includes the reductions file link' do
expect(mail.body.encoded).to include("#{base_url}/#{uuid}/reductions.csv")
end

it 'comes from [email protected]' do
expect(mail.from).to include('[email protected]')
end

it 'has the success statement' do
expect(mail.body.encoded).to include('The workflow was aggregated successfully.')
end
end

context 'with a failed aggregation' do
let(:aggregation) { create(:aggregation, status: 'failed') }

it 'reports the failures statement' do
expect(mail.body.encoded).to include('The workflow failed to aggregate.')
end
end
end
end
18 changes: 18 additions & 0 deletions spec/workers/aggregation_completed_mailer_worker_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe AggregationCompletedMailerWorker do
let(:aggregation) { create(:aggregation) }

it 'delivers the mail' do
expect { described_class.new.perform(aggregation.id) }.to change { ActionMailer::Base.deliveries.count }.by(1)
end

context 'with missing attributes' do
it 'is missing an aggregation and does not send' do
expect { described_class.new.perform(nil) }.to not_change { ActionMailer::Base.deliveries.count }
.and raise_error(ActiveRecord::RecordNotFound)
end
end
end

0 comments on commit ec4591d

Please sign in to comment.