diff --git a/api.planx.uk/modules/admin/session/zip.ts b/api.planx.uk/modules/admin/session/zip.ts index 5a9c5d38e2..df90d3999e 100644 --- a/api.planx.uk/modules/admin/session/zip.ts +++ b/api.planx.uk/modules/admin/session/zip.ts @@ -12,10 +12,15 @@ import { buildSubmissionExportZip } from "../../send/utils/exportZip"; * parameters: * - $ref: '#/components/parameters/sessionId' * - in: query - * name: includeXML + * name: includeOneAppXML * type: boolean * required: false * description: If the OneApp XML file should be included in the zip + * - in: query + * name: includeDigitalPlanningJSON + * type: boolean + * required: false + * description: If the Digital Planning JSON file should be included in the zip (only generated for supported application types) * security: * - bearerAuth: [] */ @@ -28,6 +33,7 @@ export async function generateZip( const zip = await buildSubmissionExportZip({ sessionId: req.params.sessionId, includeOneAppXML: req.query.includeXML === "true", + includeDigitalPlanningJSON: req.query.includeJSON === "false", }); res.download(zip.filename, () => { zip.remove(); diff --git a/api.planx.uk/modules/send/downloadApplicationFiles/index.ts b/api.planx.uk/modules/send/downloadApplicationFiles/index.ts index ce5781483a..0429e01caf 100644 --- a/api.planx.uk/modules/send/downloadApplicationFiles/index.ts +++ b/api.planx.uk/modules/send/downloadApplicationFiles/index.ts @@ -38,7 +38,7 @@ export async function downloadApplicationFiles( } // create the submission zip - const zip = await buildSubmissionExportZip({ sessionId }); + const zip = await buildSubmissionExportZip({ sessionId, includeDigitalPlanningJSON: true }); // Send it to the client const zipData = zip.toBuffer(); diff --git a/api.planx.uk/modules/send/utils/exportZip.test.ts b/api.planx.uk/modules/send/utils/exportZip.test.ts index 9c5703016d..f0bde23113 100644 --- a/api.planx.uk/modules/send/utils/exportZip.test.ts +++ b/api.planx.uk/modules/send/utils/exportZip.test.ts @@ -100,17 +100,6 @@ describe("buildSubmissionExportZip", () => { expect(mockAddFile).toHaveBeenCalledWith("Overview.htm", expect.anything()); }); - test("ODP schema json is added to the zip", async () => { - const schema = expectedPlanningPermissionPayload; - const expectedBuffer = Buffer.from(JSON.stringify(schema, null, 2)); - - await buildSubmissionExportZip({ sessionId: "1234" }); - expect(mockAddFile).toHaveBeenCalledWith( - "application.json", - expectedBuffer, - ); - }); - test("boundary GeoJSON is added to zip", async () => { const geojson = { type: "Feature", @@ -176,58 +165,6 @@ describe("buildSubmissionExportZip", () => { ); }); - test("ODP schema json is excluded if unsupported application type", async () => { - // set-up mock session passport overwriting "application.type" - const lowcalSessionUnsupportedAppType: Partial = { - ...mockLowcalSession, - id: "1234", - data: { - ...mockLowcalSession.data, - id: "1234", - passport: { - data: { - ...mockLowcalSession.data!.passport.data, - "application.type": ["listedBuildingConsent"], - }, - }, - }, - }; - mockGetSessionById.mockResolvedValueOnce(lowcalSessionUnsupportedAppType); - - await buildSubmissionExportZip({ sessionId: "1234" }); - - expect(mockAddFile).not.toHaveBeenCalledWith( - "application.json", - expect.anything(), - ); - }); - - test("ODP schema json is excluded if no application type", async () => { - // set-up mock session passport overwriting "application.type" - const lowcalSessionUnsupportedAppType: Partial = { - ...mockLowcalSession, - id: "1234", - data: { - ...mockLowcalSession.data, - id: "1234", - passport: { - data: { - ...mockLowcalSession.data!.passport.data, - "application.type": undefined, - }, - }, - }, - }; - mockGetSessionById.mockResolvedValueOnce(lowcalSessionUnsupportedAppType); - - await buildSubmissionExportZip({ sessionId: "1234" }); - - expect(mockAddFile).not.toHaveBeenCalledWith( - "application.json", - expect.anything(), - ); - }); - test("a document template is added when the template is supported", async () => { await buildSubmissionExportZip({ sessionId: "1234" }); expect(mockAddLocalFile).toHaveBeenCalledWith( @@ -273,13 +210,95 @@ describe("buildSubmissionExportZip", () => { }), ).rejects.toThrow(/Failed to generate OneApp XML/); }); + }); + + describe("includeDigitalPlanningJSON", () => { + test("ODP schema json is added to the zip", async () => { + await buildSubmissionExportZip({ + sessionId: "1234", + includeDigitalPlanningJSON: true, + }); + expect(mockAddFile).toHaveBeenCalledWith( + "application.json", + expect.anything(), + ); + }); + + test("ODP schema json is excluded if no query param", async () => { + await buildSubmissionExportZip({ sessionId: "1234" }); + expect(mockAddFile).not.toHaveBeenCalledWith( + "application.json", + expect.anything(), + ); + }); + + test("ODP schema json is excluded if unsupported application type", async () => { + // set-up mock session passport overwriting "application.type" + const lowcalSessionUnsupportedAppType: Partial = { + ...mockLowcalSession, + id: "1234", + data: { + ...mockLowcalSession.data, + id: "1234", + passport: { + data: { + ...mockLowcalSession.data!.passport.data, + "application.type": ["listedBuildingConsent"], + }, + }, + }, + }; + mockGetSessionById.mockResolvedValueOnce(lowcalSessionUnsupportedAppType); + + await buildSubmissionExportZip({ + sessionId: "1234", + includeDigitalPlanningJSON: true, + }); + + expect(mockAddFile).not.toHaveBeenCalledWith( + "application.json", + expect.anything(), + ); + }); + + test("ODP schema json is excluded if no application type", async () => { + // set-up mock session passport overwriting "application.type" + const lowcalSessionUnsupportedAppType: Partial = { + ...mockLowcalSession, + id: "1234", + data: { + ...mockLowcalSession.data, + id: "1234", + passport: { + data: { + ...mockLowcalSession.data!.passport.data, + "application.type": undefined, + }, + }, + }, + }; + mockGetSessionById.mockResolvedValueOnce(lowcalSessionUnsupportedAppType); + + await buildSubmissionExportZip({ + sessionId: "1234", + includeDigitalPlanningJSON: true, + }); + + expect(mockAddFile).not.toHaveBeenCalledWith( + "application.json", + expect.anything(), + ); + }); it("throws an error when ODP schema generation fails", async () => { mockGenerateDigitalPlanningDataPayload.mockRejectedValueOnce( new Error("validation test error"), ); await expect( - buildSubmissionExportZip({ sessionId: "1234" }), + buildSubmissionExportZip({ + sessionId: "1234", + includeDigitalPlanningJSON: true, + }), ).rejects.toThrow(/Failed to generate ODP Schema JSON/); }); }); diff --git a/api.planx.uk/modules/send/utils/exportZip.ts b/api.planx.uk/modules/send/utils/exportZip.ts index 4527b44ee9..e81fa1d404 100644 --- a/api.planx.uk/modules/send/utils/exportZip.ts +++ b/api.planx.uk/modules/send/utils/exportZip.ts @@ -20,9 +20,11 @@ import type { PlanXExportData } from "@opensystemslab/planx-core/types"; export async function buildSubmissionExportZip({ sessionId, includeOneAppXML = false, + includeDigitalPlanningJSON = false, }: { sessionId: string; includeOneAppXML?: boolean; + includeDigitalPlanningJSON?: boolean; }): Promise { // create zip const zip = new ExportZip(sessionId); @@ -56,6 +58,7 @@ export async function buildSubmissionExportZip({ const supportedApplicationPrefixes = ["ldc", "pa", "pp"]; const applicationType = passport.data?.["application.type"]?.[0]; if ( + includeDigitalPlanningJSON && applicationType && supportedApplicationPrefixes.includes(applicationType.split(".")?.[0]) ) {