From 6f83d3a6786486770df443212af6b48770fef2a5 Mon Sep 17 00:00:00 2001 From: Stephen Daly Date: Tue, 5 May 2026 16:02:32 +0100 Subject: [PATCH] Add API endpoint to get the group for a form Add an API endpoint that returns the group details for the form with the given ID. This includes the group admin users, and the organisation details including the organisation admin users as embedded resources. This endpoint will be used by forms-runner to get the group and organisation details for the form and the contact details for the group and organisation admin users in order to send email notifications when submissions bounce. --- .../api/form_documents_controller.rb | 8 ++++ app/models/group.rb | 10 ++++ app/models/organisation.rb | 8 ++++ app/models/user.rb | 5 ++ config/routes.rb | 1 + .../api/form_documents_controller_spec.rb | 47 ++++++++++++++++++- 6 files changed, 78 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/form_documents_controller.rb b/app/controllers/api/form_documents_controller.rb index 130ca37a7a..421f85cd95 100644 --- a/app/controllers/api/form_documents_controller.rb +++ b/app/controllers/api/form_documents_controller.rb @@ -3,6 +3,10 @@ def show render json: form_document.content end + def group + render json: Form.find_by!(id: form_id).group + end + private def form_document @@ -17,4 +21,8 @@ def form_document_params permitted_params end + + def form_id + params.require(:form_id) + end end diff --git a/app/models/group.rb b/app/models/group.rb index d31e31c0ee..36b6a8a545 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -34,9 +34,19 @@ def to_param external_id end + def as_json(options = {}) + options[:only] ||= %i[name external_id] + options[:methods] ||= %i[group_admin_users organisation] + super(options) + end + private def set_external_id self.external_id = ExternalIdProvider.generate_unique_id_for(Group) end + + def group_admin_users + users.group_admins + end end diff --git a/app/models/organisation.rb b/app/models/organisation.rb index 7f284f0f40..1209911eee 100644 --- a/app/models/organisation.rb +++ b/app/models/organisation.rb @@ -20,4 +20,12 @@ def name_with_abbreviation def admin_users users.organisation_admin end + + alias_method :organisation_admin_users, :admin_users + + def as_json(options = {}) + options[:only] ||= %i[id name] + options[:methods] ||= %i[organisation_admin_users] + super(options) + end end diff --git a/app/models/user.rb b/app/models/user.rb index 6fdcfa394f..8a09814101 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -159,6 +159,11 @@ def signed_in! update!(last_signed_in_at: Time.zone.now) end + def as_json(options = {}) + options[:only] ||= %i[name email] + super(options) + end + private def requires_name? diff --git a/config/routes.rb b/config/routes.rb index aaf272dbe3..a11ef802e0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -272,6 +272,7 @@ scope "api/v2", as: "api_v2" do scope "forms/:form_id" do get "/:tag", to: "api/form_documents#show", as: :form_document, constraints: { tag: /draft|live|archived/ } + get "/group", to: "api/form_documents#group", as: :form_group end end diff --git a/spec/requests/api/form_documents_controller_spec.rb b/spec/requests/api/form_documents_controller_spec.rb index a95b90e5c7..62109997b3 100644 --- a/spec/requests/api/form_documents_controller_spec.rb +++ b/spec/requests/api/form_documents_controller_spec.rb @@ -3,7 +3,7 @@ RSpec.describe Api::FormDocumentsController, type: :request do let(:headers) { { "ACCEPT": "application/json" } } - describe "GET /show" do + describe "#show" do context "when the form exists" do context "when the tag is draft" do let(:draft_form_name) { "Draft form" } @@ -151,6 +151,51 @@ end end + describe "#group" do + context "when the form exists" do + let(:form) { create(:form) } + let(:group) { create(:group, organisation: test_org) } + let(:group_admin) { create(:user) } + + before do + organisation_admin_user + create(:membership, user: group_admin, group:, role: :group_admin) + create(:membership, user: create(:user), group:, role: :editor) + + group.group_forms.create!(form_id: form.id) + end + + it "returns the group" do + get("/api/v2/forms/#{form.id}/group", headers:) + + expect(response).to have_http_status(:success) + expect(response.parsed_body).to eq({ + "external_id" => group.external_id, + "name" => group.name, + "group_admin_users" => [ + { "name" => group_admin.name, "email" => group_admin.email }, + ], + "organisation" => { + "id" => test_org.id, + "name" => test_org.name, + "organisation_admin_users" => [ + { "name" => organisation_admin_user.name, "email" => organisation_admin_user.email }, + ], + }, + }) + end + end + + context "when the form doesn't exist" do + it "returns http not found" do + get("/api/v2/forms/non-existent/group", headers:) + + expect(response).to have_http_status(:not_found) + expect(response.headers["Content-Type"]).to eq("application/json; charset=utf-8") + end + end + end + describe "logging", :capture_logging do let(:trace_id) { "Root=1-63441c4a-abcdef012345678912345678" } let(:request_id) { "a-request-id" }