diff --git a/app/controllers/api/form_documents_controller.rb b/app/controllers/api/form_documents_controller.rb index 421f85cd9..82fc2260e 100644 --- a/app/controllers/api/form_documents_controller.rb +++ b/app/controllers/api/form_documents_controller.rb @@ -10,7 +10,17 @@ def group private def form_document - @form_document ||= FormDocument.find_by!(form_document_params) + form_id, tag, language = form_document_params.values_at(:form_id, :tag, :language) + + return FormDocument.find_by!(form_id:, tag:, language:) if tag == "draft" + + latest_form_document = FormDocument.latest_live_or_archived(form_id:, language:) + + raise NotFoundError unless latest_form_document&.tag == tag + + CurrentLoggingAttributes.form_document_version = latest_form_document&.version + + latest_form_document end def form_document_params diff --git a/app/models/current_logging_attributes.rb b/app/models/current_logging_attributes.rb index 91724511b..e335f54b0 100644 --- a/app/models/current_logging_attributes.rb +++ b/app/models/current_logging_attributes.rb @@ -11,6 +11,7 @@ class CurrentLoggingAttributes < ActiveSupport::CurrentAttributes :acting_as_user_email, :acting_as_user_organisation_slug, :form_id, + :form_document_version, :page_id, :answer_type, :auth0_session_id, diff --git a/app/models/form_document.rb b/app/models/form_document.rb index dd3262259..25fa387bd 100644 --- a/app/models/form_document.rb +++ b/app/models/form_document.rb @@ -3,4 +3,11 @@ class FormDocument < ApplicationRecord validates :tag, presence: true validates :language, presence: true, inclusion: { in: %w[en cy] } + + def self.latest_live_or_archived(form_id:, language:) + FormDocument.where(form_id: form_id, language: language) + .where.not(version: nil) + .order(version: :desc) + .first + end end diff --git a/spec/models/form_document_spec.rb b/spec/models/form_document_spec.rb index 3948058e8..015a1f7d0 100644 --- a/spec/models/form_document_spec.rb +++ b/spec/models/form_document_spec.rb @@ -65,4 +65,71 @@ form = create(:form) # also creates a draft form document expect { create(:form_document, :draft, form: form, language: "cy") }.not_to raise_error end + + describe ".latest_live_or_archived" do + let(:form) { create :form } + + context "when the form only has a draft form document" do + it "returns nil" do + expect(form.draft_form_document).to be_present + expect(described_class.latest_live_or_archived(form_id: form.id, language: "en")).to be_nil + end + end + + context "when there is one form document with a version" do + let!(:form_document) { create :form_document, :live, form:, language: "en", version: 1 } + + it "returns the form document" do + expect(described_class.latest_live_or_archived(form_id: form.id, language: "en")).to eq(form_document) + end + + it "returns nil when no form document for the language exists" do + expect(described_class.latest_live_or_archived(form_id: form.id, language: "cy")).to be_nil + end + end + + context "when there are multiple form documents with different versions" do + let(:latest_document) { create :form_document, :archived, form:, language: "en", version: 3 } + + before do + create :form_document, :live, form:, language: "en", version: 1 + latest_document + create :form_document, :live, form:, language: "en", version: 2 + end + + it "returns the latest form document with the highest version" do + expect(described_class.latest_live_or_archived(form_id: form.id, language: "en")).to eq(latest_document) + end + end + + context "when there are form documents for different languages" do + let(:latest_english_document) { create :form_document, form:, language: "en", version: 2 } + let(:latest_welsh_document) { create :form_document, form:, language: "cy", version: 1 } + + before do + create :form_document, form:, language: "en", version: 1 + latest_english_document + latest_welsh_document + end + + it "returns the latest English document regardless of Welsh versions" do + expect(described_class.latest_live_or_archived(form_id: form.id, language: "en")).to eq(latest_english_document) + end + + it "returns the latest Welsh document regardless of English versions" do + expect(described_class.latest_live_or_archived(form_id: form.id, language: "cy")).to eq(latest_welsh_document) + end + end + + context "when documents from other forms exist" do + let(:other_form) { create :form } + let!(:other_form_document) { create :form_document, :live, form: other_form, language: "en", version: 2 } + let!(:this_form_document) { create :form_document, :live, form:, language: "en", version: 1 } + + it "only returns documents from the current form" do + expect(described_class.latest_live_or_archived(form_id: form.id, language: "en")).to eq(this_form_document) + expect(described_class.latest_live_or_archived(form_id: other_form.id, language: "en")).to eq(other_form_document) + end + end + end end diff --git a/spec/requests/api/form_documents_controller_spec.rb b/spec/requests/api/form_documents_controller_spec.rb index d616e477d..c970a87a4 100644 --- a/spec/requests/api/form_documents_controller_spec.rb +++ b/spec/requests/api/form_documents_controller_spec.rb @@ -34,51 +34,72 @@ end context "when the tag is live" do - let(:live_form_name) { "Live form" } - let(:form) { create(:form, :live, name: live_form_name) } + let(:form) { create(:form, :live) } before do - # change the form object so we can be sure we're returning the live form document - form.name = "Draft form" - form.save! - - get "/api/v2/forms/#{form.id}/live", headers: + create :form_document, :live, form: form, version: 2, content: { name: "v2 form" } + create :form_document, :live, form: form, version: 3, content: { name: "v3 form" } end it "returns http success" do + get("/api/v2/forms/#{form.id}/live", headers:) expect(response).to have_http_status(:success) end - it "returns the live form document" do + it "returns the latest live form document" do + get("/api/v2/forms/#{form.id}/live", headers:) expect(response.parsed_body).to include({ - form_id: form.id.to_s, - name: live_form_name, + name: "v3 form", }) end + + it "logs the returned form document version", :capture_logging do + get("/api/v2/forms/#{form.id}/live", headers:) + expect(log_line["form_document_version"]).to eq 3 + end + + context "and the most recent version is archived" do + before do + create :form_document, :archived, form: form, version: 4 + end + + it "returns http not found" do + get("/api/v2/forms/#{form.id}/live", headers:) + expect(response).to have_http_status(:not_found) + end + end end context "when the tag is archived" do - let(:archived_form_name) { "Archived form" } - let(:form) { create(:form, :archived, name: archived_form_name) } + let(:form) { create(:form, :archived) } before do - # change the form object so we can be sure we're returning the archived form document - form.name = "Draft form" - form.save! - - get "/api/v2/forms/#{form.id}/archived" + create :form_document, :archived, form: form, version: 2, content: { name: "v2 form" } + create :form_document, :archived, form: form, version: 3, content: { name: "v3 form" } end it "returns http success" do + get("/api/v2/forms/#{form.id}/archived") expect(response).to have_http_status(:success) end it "returns the archived form document" do + get("/api/v2/forms/#{form.id}/archived") expect(response.parsed_body).to include({ - form_id: form.id.to_s, - name: archived_form_name, + name: "v3 form", }) end + + context "and the most recent version is live" do + before do + create :form_document, :live, form: form, version: 4 + end + + it "returns http not found" do + get("/api/v2/forms/#{form.id}/archived") + expect(response).to have_http_status(:not_found) + end + end end end @@ -123,14 +144,16 @@ let(:form) { create :form } before do - create :form_document, :live, form: form, language: "en", content: { form_id: form.id.to_s, language: "en" } - create :form_document, :live, form: form, language: "cy", content: { form_id: form.id.to_s, language: "cy" } + create :form_document, :live, form: form, language: "en", version: 1 + create :form_document, :live, form: form, language: "en", version: 2, content: { name: "Live form v2", language: "en" } + create :form_document, :live, form: form, language: "cy", version: 1 + create :form_document, :live, form: form, language: "cy", version: 2, content: { name: "Welsh live form v2", language: "cy" } end it "when not given a language, defaults to english returns the live form document in english" do get("/api/v2/forms/#{form.id}/live", headers:) expect(response.parsed_body).to include({ - form_id: form.id.to_s, + name: "Live form v2", language: "en", }) end @@ -138,7 +161,7 @@ it "when given welsh param returns the live form document in welsh" do get("/api/v2/forms/#{form.id}/live?language=cy", headers:) expect(response.parsed_body).to include({ - form_id: form.id.to_s, + name: "Welsh live form v2", language: "cy", }) end