Create observable reports blob early

This will allow us to check for completion of the report later in case
websockets fail.
This commit is contained in:
Maikel Linke
2024-08-09 17:06:50 +10:00
parent 037eb456c0
commit 19ef047193
6 changed files with 46 additions and 10 deletions

View File

@@ -70,10 +70,12 @@ module Admin
block: "start"
).broadcast
blob = ReportBlob.create_for_upload_later!(report_filename)
ReportJob.perform_later(
report_class:, user: spree_current_user, params:,
format: report_format,
filename: report_filename,
blob:,
channel: ScopedChannel.for_id(params[:uuid]),
)

View File

@@ -9,12 +9,12 @@ class ReportJob < ApplicationJob
NOTIFICATION_TIME = 5.seconds
def perform(report_class:, user:, params:, format:, filename:, channel: nil)
def perform(report_class:, user:, params:, format:, blob:, channel: nil)
start_time = Time.zone.now
report = report_class.new(user, params, render: true)
result = report.render_as(format)
blob = ReportBlob.create!(filename, result)
blob.store(result)
execution_time = Time.zone.now - start_time

View File

@@ -5,7 +5,7 @@ class ReportBlob < ActiveStorage::Blob
# AWS S3 limits URL expiry to one week.
LIFETIME = 1.week
def self.create!(filename, content)
def self.create_locally!(filename, content)
create_and_upload!(
io: StringIO.new(content),
filename:,
@@ -15,11 +15,34 @@ class ReportBlob < ActiveStorage::Blob
)
end
def self.create_for_upload_later!(filename)
# ActiveStorage discourages modifying a blob later but we need a blob
# before we know anything about the report file. It enables us to use the
# same blob in the controller to read the result.
create_before_direct_upload!(
filename:,
byte_size: 0,
checksum: "0",
content_type: content_type(filename),
service_name: :local,
).tap do |blob|
ActiveStorage::PurgeJob.set(wait: LIFETIME).perform_later(blob)
end
end
def self.content_type(filename)
MIME::Types.of(filename).first&.to_s || "application/octet-stream"
end
def store(content)
io = StringIO.new(content)
upload(io, identify: false)
save!
end
def result
return if checksum == "0"
@result ||= download.force_encoding(Encoding::UTF_8)
end

View File

@@ -6,13 +6,14 @@ RSpec.describe ReportJob do
include CableReady::Broadcaster
let(:report_args) {
{ report_class:, user:, params:, format:, filename: }
{ report_class:, user:, params:, format:, blob: }
}
let(:report_class) { Reporting::Reports::UsersAndEnterprises::Base }
let(:user) { enterprise.owner }
let(:enterprise) { create(:enterprise) }
let(:params) { {} }
let(:format) { :csv }
let(:blob) { ReportBlob.create_for_upload_later!(filename) }
let(:filename) { "report.csv" }
it "generates a report" do
@@ -25,11 +26,12 @@ RSpec.describe ReportJob do
it "enqueues a job for async processing" do
expect {
ReportJob.perform_later(**report_args)
}.not_to change { ActiveStorage::Blob.count }
}.not_to change { blob.checksum }
expect {
perform_enqueued_jobs(only: ReportJob)
}.to change { ActiveStorage::Blob.count }
blob.reload
}.to change { blob.checksum }
expect_csv_report
end
@@ -44,7 +46,8 @@ RSpec.describe ReportJob do
expect {
perform_enqueued_jobs(only: ReportJob)
}.to change { ActiveStorage::Blob.count }
blob.reload
}.to change { blob.checksum }
end
it "triggers an email when the report is done" do

View File

@@ -10,7 +10,7 @@ RSpec.describe ReportMailer do
blob:,
).report_ready
}
let(:blob) { ReportBlob.create!("customers.csv", "report content") }
let(:blob) { ReportBlob.create_locally!("customers.csv", "report content") }
it "notifies about a report" do
expect(email.subject).to eq "Report ready"

View File

@@ -7,8 +7,16 @@ RSpec.describe ReportBlob, type: :model do
content = "This works. ✓"
expect do
blob = ReportBlob.create!("customers.html", content)
blob = ReportBlob.create_locally!("customers.html", content)
content = blob.result
end.not_to change { content.encoding }.from(Encoding::UTF_8)
end
it "can be created first and filled later" do
blob = ReportBlob.create_for_upload_later!("customers.html")
expect { blob.store("Hello") }
.to change { blob.checksum }.from("0")
.and change { blob.result }.from(nil).to("Hello")
end
end