diff --git a/api.planx.uk/modules/send/s3/index.test.ts b/api.planx.uk/modules/send/s3/index.test.ts index 9db576188e..352c8c07ef 100644 --- a/api.planx.uk/modules/send/s3/index.test.ts +++ b/api.planx.uk/modules/send/s3/index.test.ts @@ -53,6 +53,14 @@ describe(`uploading an application to S3`, () => { slug: "unsupported-team", }, }); + + queryMock.mockQuery({ + name: "CreateS3Application", + matchOnVariables: false, + data: { + insertS3Application: { id: 1 }, + }, + }); }); it("requires auth", async () => { diff --git a/api.planx.uk/modules/send/s3/index.ts b/api.planx.uk/modules/send/s3/index.ts index f301729334..39aed61780 100644 --- a/api.planx.uk/modules/send/s3/index.ts +++ b/api.planx.uk/modules/send/s3/index.ts @@ -1,16 +1,19 @@ +import axios from "axios"; import type { NextFunction, Request, Response } from "express"; +import { gql } from "graphql-request"; import { $api } from "../../../client"; +import { Passport } from "../../../types"; import { uploadPrivateFile } from "../../file/service/uploadFile"; import { markSessionAsSubmitted } from "../../saveAndReturn/service/utils"; -import axios from "axios"; import { isApplicationTypeSupported } from "../utils/helpers"; -import { Passport } from "../../../types"; -export async function sendToS3( - req: Request, - res: Response, - next: NextFunction, -) { +interface CreateS3Application { + insertS3Application: { + id: string; + }; +} + +const sendToS3 = async (req: Request, res: Response, next: NextFunction) => { // `/upload-submission/:localAuthority` is only called via Hasura's scheduled event webhook, so body is wrapped in a "payload" key const { payload } = req.body; const localAuthority = req.params.localAuthority; @@ -58,8 +61,7 @@ export async function sendToS3( ); // Send a notification with the file URL to the Power Automate webook - let webhookResponseStatus: number | undefined; - await axios({ + const webhookRequest = { method: "POST", url: powerAutomateWebhookURL, adapter: "http", @@ -73,10 +75,49 @@ export async function sendToS3( file: fileUrl, payload: doValidation ? "Validated ODP Schema" : "Discretionary", }, - }) - .then((res) => { - // TODO Create & update audit table entry here + }; + let webhookResponseStatus: number | undefined; + await axios(webhookRequest) + .then(async (res) => { webhookResponseStatus = res.status; + + // Mark session as submitted so that reminder and expiry emails are not triggered + markSessionAsSubmitted(sessionId); + + // Create an audit entry + const applicationId = await $api.client.request( + gql` + mutation CreateS3Application( + $session_id: String! + $team_slug: String! + $webhook_request: jsonb! + $webhook_response: jsonb = {} + ) { + insertS3Application: insert_s3_applications_one( + object: { + session_id: $session_id + team_slug: $team_slug + webhook_request: $webhook_request + webhook_response: $webhook_response + } + ) { + id + } + } + `, + { + session_id: sessionId, + team_slug: localAuthority, + webhook_request: webhookRequest, + webhook_response: res, + }, + ); + + return { + application: { + ...applicationId.insertS3Application, + }, + }; }) .catch((error) => { throw new Error( @@ -84,9 +125,6 @@ export async function sendToS3( ); }); - // Mark session as submitted so that reminder and expiry emails are not triggered - markSessionAsSubmitted(sessionId); - return res.status(200).send({ message: `Successfully uploaded submission to S3: ${fileUrl}`, payload: doValidation ? "Validated ODP Schema" : "Discretionary", @@ -100,4 +138,6 @@ export async function sendToS3( }`, }); } -} +}; + +export { sendToS3 }; diff --git a/hasura.planx.uk/metadata/tables.yaml b/hasura.planx.uk/metadata/tables.yaml index 49a7278c64..a20b9f2d82 100644 --- a/hasura.planx.uk/metadata/tables.yaml +++ b/hasura.planx.uk/metadata/tables.yaml @@ -1414,6 +1414,46 @@ - role: api permission: filter: {} +- table: + name: s3_applications + schema: public + insert_permissions: + - role: api + permission: + check: {} + columns: + - id + - webhook_request + - webhook_response + - session_id + - team_slug + - created_at + comment: "" + select_permissions: + - role: api + permission: + columns: + - id + - webhook_request + - webhook_response + - session_id + - team_slug + - created_at + filter: {} + comment: "" + update_permissions: + - role: api + permission: + columns: + - id + - webhook_request + - webhook_response + - session_id + - team_slug + - created_at + filter: {} + check: null + comment: "" - table: name: sessions schema: public diff --git a/hasura.planx.uk/migrations/1720597665798_create_table_s3_applications/down.sql b/hasura.planx.uk/migrations/1720597665798_create_table_s3_applications/down.sql new file mode 100644 index 0000000000..d2db0a6dcf --- /dev/null +++ b/hasura.planx.uk/migrations/1720597665798_create_table_s3_applications/down.sql @@ -0,0 +1 @@ +DROP TABLE "public.s3_applications" CASCADE; diff --git a/hasura.planx.uk/migrations/1720597665798_create_table_s3_applications/up.sql b/hasura.planx.uk/migrations/1720597665798_create_table_s3_applications/up.sql new file mode 100644 index 0000000000..b5995aeb1c --- /dev/null +++ b/hasura.planx.uk/migrations/1720597665798_create_table_s3_applications/up.sql @@ -0,0 +1,11 @@ +CREATE TABLE "public"."s3_applications" ( + "id" serial NOT NULL, + "session_id" text NOT NULL, + "team_slug" text NOT NULL, + "webhook_request" JSONB NOT NULL, + "webhook_response" JSONB NOT NULL, + "created_at" timestamptz NOT NULL DEFAULT now(), + PRIMARY KEY ("id") +); + +COMMENT ON TABLE "public"."s3_applications" IS 'Stores a receipt of applications submitted using the Upload to AWS S3 method with notifications via Power Automate webhook';