diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 79c5727eb1..b08465a54b 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -692,6 +692,7 @@ Rails/ApplicationJob: - 'app/jobs/heartbeat_job.rb' - 'app/jobs/order_cycle_closing_job.rb' - 'app/jobs/order_cycle_notification_job.rb' + - 'app/jobs/report_job.rb' - 'app/jobs/subscription_confirm_job.rb' - 'app/jobs/subscription_placement_job.rb' diff --git a/app/controllers/admin/reports_controller.rb b/app/controllers/admin/reports_controller.rb index f6a30505d3..2dc18e4092 100644 --- a/app/controllers/admin/reports_controller.rb +++ b/app/controllers/admin/reports_controller.rb @@ -24,17 +24,17 @@ module Admin if report_format.present? export_report else - render_report + show_report end end private def export_report - send_data @report.render_as(report_format), filename: report_filename + send_data render_report_as(report_format), filename: report_filename end - def render_report + def show_report assign_view_data render "show" end @@ -45,12 +45,26 @@ module Admin @report_subtype = report_subtype @report_title = report_title @rendering_options = rendering_options - @table = @report.to_html if render_data? + @table = render_report_as(:html) if render_data? @data = Reporting::FrontendData.new(spree_current_user) end def render_data? request.post? end + + def render_report_as(format) + if OpenFoodNetwork::FeatureToggle.enabled?(:background_reports, spree_current_user) + job = ReportJob.perform_later( + report_class, spree_current_user, params, format + ) + sleep 1 until job.done? + + # This result has been rendered by Rails in safe mode already. + job.result.html_safe # rubocop:disable Rails/OutputSafety + else + @report.render_as(format) + end + end end end diff --git a/app/jobs/report_job.rb b/app/jobs/report_job.rb new file mode 100644 index 0000000000..7143b3637c --- /dev/null +++ b/app/jobs/report_job.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +# Renders a report and saves it to a temporary file. +class ReportJob < ActiveJob::Base + def perform(report_class, user, params, format) + report = report_class.new(user, params, render: true) + result = report.render_as(format) + write(result) + end + + def done? + @done ||= File.file?(filename) + end + + def result + @result ||= read_result + end + + private + + def write(result) + File.write(filename, result) + end + + def read_result + File.read(filename) + ensure + File.unlink(filename) + end + + def filename + Rails.root.join("tmp/report-#{job_id}") + end +end diff --git a/lib/reporting/report_renderer.rb b/lib/reporting/report_renderer.rb index c58917a5ae..d301c5a614 100644 --- a/lib/reporting/report_renderer.rb +++ b/lib/reporting/report_renderer.rb @@ -4,7 +4,7 @@ require 'spreadsheet_architect' module Reporting class ReportRenderer - REPORT_FORMATS = [:csv, :json, :xlsx, :pdf].freeze + REPORT_FORMATS = [:csv, :json, :html, :xlsx, :pdf].freeze def initialize(report) @report = report diff --git a/spec/jobs/report_job_spec.rb b/spec/jobs/report_job_spec.rb new file mode 100644 index 0000000000..9211ee0e44 --- /dev/null +++ b/spec/jobs/report_job_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe ReportJob do + let(:report_args) { [report_class, user, params, format] } + let(:report_class) { Reporting::Reports::UsersAndEnterprises::Base } + let(:user) { enterprise.owner } + let(:enterprise) { create(:enterprise) } + let(:params) { {} } + let(:format) { :csv } + + it "generates a report" do + job = ReportJob.new + job.perform(*report_args) + expect_csv_report(job) + end + + it "enqueues a job for asynch processing" do + job = ReportJob.perform_later(*report_args) + expect(job.done?).to eq false + + # This performs the job in the same process but that's good enought for + # testing the job code. I hope that we can rely on the job worker. + ActiveJob::Base.queue_adapter.perform_enqueued_jobs = true + job.retry_job + + expect(job.done?).to eq true + expect_csv_report(job) + end + + def expect_csv_report(job) + table = CSV.parse(job.result) + expect(table[0][1]).to eq "Relationship" + expect(table[1][1]).to eq "owns" + end +end