diff --git a/app/controllers/reports_controller.rb b/app/controllers/reports_controller.rb index f083a4ce7..22a4332e7 100644 --- a/app/controllers/reports_controller.rb +++ b/app/controllers/reports_controller.rb @@ -19,55 +19,18 @@ def questions_with_answer_type render template: "reports/questions_with_answer_type", locals: { answer_type:, questions: } end - def questions_with_add_another_answer - forms = Reports::FormDocumentsService.live_form_documents - questions = Reports::FeatureReportService.new(forms).questions_with_add_another_answer - - if params[:format] == "csv" - send_data Reports::QuestionsCsvReportService.new(questions).csv, - type: "text/csv; charset=iso-8859-1", - disposition: "attachment; filename=#{csv_filename('live_questions_with_add_another_answer_report')}" - else - render template: "reports/feature_report", locals: { report: params[:action], records: questions } - end - end - - def forms_with_routes - forms = Reports::FormDocumentsService.live_form_documents - forms = Reports::FeatureReportService.new(forms).forms_with_routes - - if params[:format] == "csv" - send_data Reports::FormsCsvReportService.new(forms).csv, - type: "text/csv; charset=iso-8859-1", - disposition: "attachment; filename=#{csv_filename('live_forms_with_routes_report')}" - else - render template: "reports/feature_report", locals: { report: params[:action], records: forms } - end - end - - def forms_with_payments - forms = Reports::FormDocumentsService.live_form_documents - forms = Reports::FeatureReportService.new(forms).forms_with_payments - - if params[:format] == "csv" - send_data Reports::FormsCsvReportService.new(forms).csv, - type: "text/csv; charset=iso-8859-1", - disposition: "attachment; filename=#{csv_filename('live_forms_with_payments_report')}" - else - render template: "reports/feature_report", locals: { report: params[:action], records: forms } - end - end + def feature_report + report = params[:report].underscore - def forms_with_csv_submission_enabled forms = Reports::FormDocumentsService.live_form_documents - forms = Reports::FeatureReportService.new(forms).forms_with_csv_submission_enabled + records = Reports::FeatureReportService.new(forms).report report if params[:format] == "csv" - send_data Reports::FormsCsvReportService.new(forms).csv, + send_data Reports::CsvReportService.new(records).csv, type: "text/csv; charset=iso-8859-1", - disposition: "attachment; filename=#{csv_filename('live_forms_with_csv_submission_enabled_report')}" + disposition: "attachment; filename=#{csv_filename("live_#{report}_report")}" else - render template: "reports/feature_report", locals: { report: params[:action], records: forms } + render template: "reports/feature_report", locals: { report:, records: } end end diff --git a/app/services/reports/csv_report_service.rb b/app/services/reports/csv_report_service.rb new file mode 100644 index 000000000..942d8a1cd --- /dev/null +++ b/app/services/reports/csv_report_service.rb @@ -0,0 +1,38 @@ +class Reports::CsvReportService + delegate :csv, to: :@csv_service + + def initialize(records) + record_type = detect_record_type(records) + @csv_service = csv_service_class(record_type).new(records) + end + +private + + class NilCsvService + def initialize(_records); end + + def csv + "" + end + end + + def csv_service_class(record_type) + return NilCsvService if record_type.nil? + + "Reports::#{record_type.capitalize}CsvReportService".constantize + end + + def detect_record_type(records) + return if records.blank? + + type = records.first.fetch("type", "form") + + if type == "form" + :forms + elsif type == "question_page" + :questions + else + raise "type of records '#{type}' is not one of 'forms', 'question_page'" unless type + end + end +end diff --git a/app/services/reports/feature_report_service.rb b/app/services/reports/feature_report_service.rb index f8ebbb1b1..49f1346b0 100644 --- a/app/services/reports/feature_report_service.rb +++ b/app/services/reports/feature_report_service.rb @@ -1,11 +1,37 @@ class Reports::FeatureReportService + def self.define_report(name, &block) + @reports ||= [] + @reports << name + + define_method(name, &block) + end + + def self.matches_report?(name) + @reports.include? name.to_sym + end + + class Constraint + def self.matches?(request) + name = request.params[:report].underscore + Reports::FeatureReportService.matches_report? name + end + end + + private_class_method :define_report + attr_reader :form_documents def initialize(form_documents) @form_documents = form_documents end - def report + def report(name = nil) + unless name.nil? + raise "'#{name}' is not a defined report" unless self.class.matches_report?(name) + + return send(name) + end + report = { total_forms: 0, forms_with_payment: 0, @@ -54,7 +80,7 @@ def questions_with_answer_type(answer_type) end end - def questions_with_add_another_answer + define_report :questions_with_add_another_answer do form_documents.flat_map do |form| form["content"]["steps"] .select { |step| step["data"]["is_repeatable"] } @@ -62,19 +88,19 @@ def questions_with_add_another_answer end end - def forms_with_routes + define_report :forms_with_routes do form_documents .select { |form| Reports::FormDocumentsService.has_routes?(form) } .map { |form| form_with_routes_details(form) } end - def forms_with_payments + define_report :forms_with_payments do form_documents .select { |form| Reports::FormDocumentsService.has_payments?(form) } .map { |form| form_details(form) } end - def forms_with_csv_submission_enabled + define_report :forms_with_csv_submission_enabled do form_documents .select { |form| Reports::FormDocumentsService.has_csv_submission_enabled?(form) } .map { |form| form_details(form) } diff --git a/app/views/reports/features.html.erb b/app/views/reports/features.html.erb index 725646a7c..ee7368fda 100644 --- a/app/views/reports/features.html.erb +++ b/app/views/reports/features.html.erb @@ -12,19 +12,19 @@ <% end %> <%= summary_list.with_row do |row| %> <%= row.with_key(text: t(".features.live_forms_with_routes")) %> - <%= row.with_value(text: govuk_link_to(data[:forms_with_routing], report_forms_with_routes_path, no_visited_state: true)) %> + <%= row.with_value(text: govuk_link_to(data[:forms_with_routing], feature_report_path(report: "forms-with-routes"), no_visited_state: true)) %> <% end %> <%= summary_list.with_row do |row| %> <%= row.with_key(text: t(".features.live_forms_with_payments")) %> - <%= row.with_value(text: govuk_link_to(data[:forms_with_payment], report_forms_with_payments_path, no_visited_state: true)) %> + <%= row.with_value(text: govuk_link_to(data[:forms_with_payment], feature_report_path(report: "forms-with-payments"), no_visited_state: true)) %> <% end %> <%= summary_list.with_row do |row| %> <%= row.with_key(text: t(".features.live_forms_with_add_another_answer")) %> - <%= row.with_value(text: govuk_link_to(data[:forms_with_add_another_answer], report_questions_with_add_another_answer_path, no_visited_state: true)) %> + <%= row.with_value(text: govuk_link_to(data[:forms_with_add_another_answer], feature_report_path(report: "questions-with-add-another-answer"), no_visited_state: true)) %> <% end %> <%= summary_list.with_row do |row| %> <%= row.with_key(text: t(".features.live_forms_with_csv_submission_enabled")) %> - <%= row.with_value(text: govuk_link_to(data[:forms_with_csv_submission_enabled], report_forms_with_csv_submission_enabled_path, no_visited_state: true)) %> + <%= row.with_value(text: govuk_link_to(data[:forms_with_csv_submission_enabled], feature_report_path(report: "forms-with-csv-submission-enabled"), no_visited_state: true)) %> <% end %> <% end %> diff --git a/config/routes.rb b/config/routes.rb index f5733054a..554cf79c7 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -197,11 +197,8 @@ scope "/features" do get "/", to: "reports#features", as: :report_features - get "questions-with-answer-type/:answer_type", to: "reports#questions_with_answer_type", as: :report_questions_with_answer_type - get "questions-with-add-another-answer", to: "reports#questions_with_add_another_answer", as: :report_questions_with_add_another_answer - get "forms-with-routes", to: "reports#forms_with_routes", as: :report_forms_with_routes - get "forms-with-payments", to: "reports#forms_with_payments", as: :report_forms_with_payments - get "forms-with-csv-submission-enabled", to: "reports#forms_with_csv_submission_enabled", as: :report_forms_with_csv_submission_enabled + get "/questions-with-answer-type/:answer_type", to: "reports#questions_with_answer_type", as: :report_questions_with_answer_type + get "/:report", constraints: Reports::FeatureReportService::Constraint, to: "reports#feature_report", as: :feature_report end get "users", to: "reports#users", as: :report_users diff --git a/spec/requests/reports_controller_spec.rb b/spec/requests/reports_controller_spec.rb index 68321ff9e..7fcdce372 100644 --- a/spec/requests/reports_controller_spec.rb +++ b/spec/requests/reports_controller_spec.rb @@ -193,7 +193,7 @@ before do login_as_standard_user - get report_questions_with_add_another_answer_path + get feature_report_path(report: "questions-with-add-another-answer") end it "returns http code 403" do @@ -209,7 +209,7 @@ before do login_as_organisation_admin_user - get report_questions_with_add_another_answer_path + get feature_report_path(report: "questions-with-add-another-answer") end it "returns http code 403" do @@ -225,7 +225,7 @@ before do login_as_super_admin_user - get report_questions_with_add_another_answer_path + get feature_report_path(report: "questions-with-add-another-answer") end it "returns http code 200" do @@ -251,7 +251,7 @@ before do login_as_standard_user - get report_forms_with_routes_path + get feature_report_path(report: "forms-with-routes") end it "returns http code 403" do @@ -267,7 +267,7 @@ before do login_as_organisation_admin_user - get report_forms_with_routes_path + get feature_report_path(report: "forms-with-routes") end it "returns http code 403" do @@ -283,7 +283,7 @@ before do login_as_super_admin_user - get report_forms_with_routes_path + get feature_report_path(report: "forms-with-routes") end it "returns http code 200" do @@ -309,7 +309,7 @@ before do login_as_standard_user - get report_forms_with_payments_path + get feature_report_path(report: "forms-with-payments") end it "returns http code 403" do @@ -325,7 +325,7 @@ before do login_as_organisation_admin_user - get report_forms_with_payments_path + get feature_report_path(report: "forms-with-payments") end it "returns http code 403" do @@ -341,7 +341,7 @@ before do login_as_super_admin_user - get report_forms_with_payments_path + get feature_report_path(report: "forms-with-payments") end it "returns http code 200" do @@ -367,7 +367,7 @@ before do login_as_standard_user - get report_forms_with_csv_submission_enabled_path + get feature_report_path(report: "forms-with-csv-submission-enabled") end it "returns http code 403" do @@ -383,7 +383,7 @@ before do login_as_organisation_admin_user - get report_forms_with_csv_submission_enabled_path + get feature_report_path(report: "forms-with-csv-submission-enabled") end it "returns http code 403" do @@ -399,7 +399,7 @@ before do login_as_super_admin_user - get report_forms_with_csv_submission_enabled_path + get feature_report_path(report: "forms-with-csv-submission-enabled") end it "returns http code 200" do @@ -736,7 +736,7 @@ travel_to Time.utc(2025, 5, 15, 15, 31, 57) - get report_forms_with_routes_path(format: :csv) + get feature_report_path(report: "forms-with-routes", format: :csv) end it_behaves_like "csv response" @@ -762,7 +762,7 @@ travel_to Time.utc(2025, 5, 15, 15, 31, 57) - get report_forms_with_payments_path(format: :csv) + get feature_report_path(report: "forms-with-payments", format: :csv) end it_behaves_like "csv response" @@ -787,7 +787,7 @@ travel_to Time.utc(2025, 5, 15, 15, 31, 57) - get report_forms_with_csv_submission_enabled_path(format: :csv) + get feature_report_path(report: "forms-with-csv-submission-enabled", format: :csv) end it_behaves_like "csv response" @@ -834,7 +834,7 @@ travel_to Time.utc(2025, 5, 15, 15, 31, 57) - get report_questions_with_add_another_answer_path(format: :csv) + get feature_report_path(report: "questions-with-add-another-answer", format: :csv) end it_behaves_like "csv response" diff --git a/spec/routing/reports_routing_spec.rb b/spec/routing/reports_routing_spec.rb index c2aad0ddd..77ab3808d 100644 --- a/spec/routing/reports_routing_spec.rb +++ b/spec/routing/reports_routing_spec.rb @@ -7,58 +7,62 @@ expect(get: "/reports/features").to route_to("reports#features") end - it "routes to #questions_with_answer_type" do + it "routes to questions_with_answer_type" do expect(get: "/reports/features/questions-with-answer-type/text").to route_to("reports#questions_with_answer_type", answer_type: "text") end - it "routes to #questions_with_add_another_answer" do - expect(get: "/reports/features/questions-with-add-another-answer").to route_to("reports#questions_with_add_another_answer") + it "routes to #feature_report with questions_with_add_another_answer" do + expect(get: "/reports/features/questions-with-add-another-answer").to route_to("reports#feature_report", report: "questions-with-add-another-answer") end - it "routes to #questions_with_add_another_answer with csv format" do - expect(get: "/reports/features/questions-with-add-another-answer.csv").to route_to("reports#questions_with_add_another_answer", format: "csv") + it "routes to #feature_report with questions_with_add_another_answer and csv format" do + expect(get: "/reports/features/questions-with-add-another-answer.csv").to route_to("reports#feature_report", report: "questions-with-add-another-answer", format: "csv") end - it "routes to #forms_with_routes" do - expect(get: "/reports/features/forms-with-routes").to route_to("reports#forms_with_routes") + it "routes to #feature_report with forms_with_routes" do + expect(get: "/reports/features/forms-with-routes").to route_to("reports#feature_report", report: "forms-with-routes") end - it "routes to #forms_with_routes with csv format" do - expect(get: "/reports/features/forms-with-routes.csv").to route_to("reports#forms_with_routes", format: "csv") + it "routes to #feature_report with forms_with_routes and csv format" do + expect(get: "/reports/features/forms-with-routes.csv").to route_to("reports#feature_report", report: "forms-with-routes", format: "csv") end - it "routes to #forms_with_payments" do - expect(get: "/reports/features/forms-with-payments").to route_to("reports#forms_with_payments") + it "routes to #feature_report with forms_with_payments" do + expect(get: "/reports/features/forms-with-payments").to route_to("reports#feature_report", report: "forms-with-payments") end - it "routes to #forms_with_payments with csv format" do - expect(get: "/reports/features/forms-with-payments.csv").to route_to("reports#forms_with_payments", format: "csv") + it "routes to #feature_report with forms_with_payments and csv format" do + expect(get: "/reports/features/forms-with-payments.csv").to route_to("reports#feature_report", report: "forms-with-payments", format: "csv") end - it "routes to #forms_with_csv_submission_enabled" do - expect(get: "/reports/features/forms-with-csv-submission-enabled").to route_to("reports#forms_with_csv_submission_enabled") + it "routes to #feature_report with forms_with_csv_submission_enabled" do + expect(get: "/reports/features/forms-with-csv-submission-enabled").to route_to("reports#feature_report", report: "forms-with-csv-submission-enabled") end - it "routes to #forms_with_csv_submission_enabled with csv format" do - expect(get: "/reports/features/forms-with-csv-submission-enabled.csv").to route_to("reports#forms_with_csv_submission_enabled", format: "csv") + it "routes to #feature_report with forms_with_csv_submission_enabled and csv format" do + expect(get: "/reports/features/forms-with-csv-submission-enabled.csv").to route_to("reports#feature_report", report: "forms-with-csv-submission-enabled", format: "csv") + end + + it "does not route to #feature_report if param does not match defined report" do + expect(get: "/reports/features/foobar").not_to route_to("reports#feature_report", report: "foobar") end end describe "path helpers" do it "routes to #questions_with_add_another_answer as csv" do - expect(get: report_questions_with_add_another_answer_path(format: :csv)).to route_to("reports#questions_with_add_another_answer", format: "csv") + expect(get: feature_report_path(report: "questions-with-add-another-answer", format: :csv)).to route_to("reports#feature_report", report: "questions-with-add-another-answer", format: "csv") end it "routes to #forms_with_routes as csv" do - expect(get: report_forms_with_routes_path(format: :csv)).to route_to("reports#forms_with_routes", format: "csv") + expect(get: feature_report_path(report: "forms-with-routes", format: :csv)).to route_to("reports#feature_report", report: "forms-with-routes", format: "csv") end it "routes to #forms_with_payments as csv" do - expect(get: report_forms_with_payments_path(format: :csv)).to route_to("reports#forms_with_payments", format: "csv") + expect(get: feature_report_path(report: "forms-with-payments", format: :csv)).to route_to("reports#feature_report", report: "forms-with-payments", format: "csv") end it "routes to #forms_with_csv_submission_enabled as csv" do - expect(get: report_forms_with_csv_submission_enabled_path(format: :csv)).to route_to("reports#forms_with_csv_submission_enabled", format: "csv") + expect(get: feature_report_path(report: "forms-with-csv-submission-enabled", format: :csv)).to route_to("reports#feature_report", report: "forms-with-csv-submission-enabled", format: "csv") end end end diff --git a/spec/services/reports/csv_report_service_spec.rb b/spec/services/reports/csv_report_service_spec.rb new file mode 100644 index 000000000..2a6106f17 --- /dev/null +++ b/spec/services/reports/csv_report_service_spec.rb @@ -0,0 +1,34 @@ +require "rails_helper" + +RSpec.describe Reports::CsvReportService do + subject(:csv_report_service) do + described_class.new(records) + end + + describe "#csv" do + context "when records is an empty list" do + let(:records) { [] } + + it "returns an empty string" do + expect(csv_report_service.csv).to eq "" + end + end + + context "when records is a list of form documents" do + let(:records) { JSON.parse(file_fixture("form_documents_response.json").read) } + + it "returns a CSV of forms" do + expect(csv_report_service.csv).to eq Reports::FormsCsvReportService.new(records).csv + end + end + + context "when records is a list of question page documents" do + let(:records) { Reports::FeatureReportService.new(form_documents).questions } + let(:form_documents) { JSON.parse(file_fixture("form_documents_response.json").read) } + + it "returns a CSV of questions" do + expect(csv_report_service.csv).to eq Reports::QuestionsCsvReportService.new(records).csv + end + end + end +end diff --git a/spec/services/reports/feature_report_service_spec.rb b/spec/services/reports/feature_report_service_spec.rb index 949228e2d..caec6b18a 100644 --- a/spec/services/reports/feature_report_service_spec.rb +++ b/spec/services/reports/feature_report_service_spec.rb @@ -11,6 +11,32 @@ GroupForm.create!(form_id: 4, group:) end + describe "Constraint" do + it "returns true if given the name of a defined report in slug format" do + expect(described_class::Constraint.matches?(OpenStruct.new(params: { report: "forms-with-routes" }))).to be true + expect(described_class::Constraint.matches?(OpenStruct.new(params: { report: "questions-with-add-another-answer" }))).to be true + end + + it "returns false if names does not match a defined report" do + expect(described_class.matches_report?(OpenStruct.new(params: { report: "report" }))).to be false + expect(described_class.matches_report?(OpenStruct.new(params: { report: "inspect" }))).to be false + expect(described_class.matches_report?(OpenStruct.new(params: { report: "display" }))).to be false + end + end + + describe ".matches_report?" do + it "returns true if given the name of a defined report" do + expect(described_class.matches_report?(:forms_with_routes)).to be true + expect(described_class.matches_report?(:questions_with_add_another_answer)).to be true + end + + it "returns false if names does not match a defined report" do + expect(described_class.matches_report?(:report)).to be false + expect(described_class.matches_report?(:inspect)).to be false + expect(described_class.matches_report?(:display)).to be false + end + end + describe "#report" do it "returns the feature report" do report = described_class.new(form_documents).report @@ -44,6 +70,29 @@ }, }) end + + context "with the name of a feature report" do + it "returns that report" do + report = [] + feature_report_service = described_class.new(form_documents) + allow(feature_report_service).to receive(:forms_with_routes).and_return report + + expect(feature_report_service.report(:forms_with_routes)).to be report + + expect(feature_report_service).to have_received(:forms_with_routes) + end + end + + context "with the name of a method that is not a feature report" do + it "raises an error" do + feature_report_service = described_class.new(form_documents) + allow(feature_report_service).to receive(:display) + + expect { feature_report_service.report(:display) }.to raise_error(/not a defined report/) + + expect(feature_report_service).not_to have_received(:display) + end + end end describe "#questions" do diff --git a/spec/views/reports/feature_report.html.erb_spec.rb b/spec/views/reports/feature_report.html.erb_spec.rb index 9d8cd48ca..095b9b3f4 100644 --- a/spec/views/reports/feature_report.html.erb_spec.rb +++ b/spec/views/reports/feature_report.html.erb_spec.rb @@ -5,7 +5,7 @@ let(:records) { [] } before do - controller.request.path_parameters[:action] = report + controller.request.path_parameters[:report] = report.dasherize render locals: { report:, records: } end @@ -30,7 +30,7 @@ end it "has a link to download the CSV" do - expect(rendered).to have_link("Download data about all live forms with CSV submission enabled as a CSV file", href: report_forms_with_csv_submission_enabled_path(format: :csv)) + expect(rendered).to have_link("Download data about all live forms with CSV submission enabled as a CSV file", href: feature_report_path(report: "forms-with-csv-submission-enabled", format: :csv)) end describe "questions table" do @@ -76,7 +76,7 @@ end it "has a link to download the CSV" do - expect(rendered).to have_link("Download data about all live forms with payments as a CSV file", href: report_forms_with_payments_path(format: :csv)) + expect(rendered).to have_link("Download data about all live forms with payments as a CSV file", href: feature_report_path(report: "forms-with-payments", format: :csv)) end describe "questions table" do @@ -122,7 +122,7 @@ end it "has a link to download the CSV" do - expect(rendered).to have_link("Download data about all live forms with routes as a CSV file", href: report_forms_with_routes_path(format: :csv)) + expect(rendered).to have_link("Download data about all live forms with routes as a CSV file", href: feature_report_path(report: "forms-with-routes", format: :csv)) end describe "questions table" do @@ -171,7 +171,7 @@ end it "has a link to download the CSV" do - expect(rendered).to have_link("Download all questions with add another answer in live forms as a CSV file", href: report_questions_with_add_another_answer_path(format: :csv)) + expect(rendered).to have_link("Download all questions with add another answer in live forms as a CSV file", href: feature_report_path(report: "questions-with-add-another-answer", format: :csv)) end describe "questions table" do