From a984c159924f79020ca7637429202586a449a369 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Mon, 13 Nov 2023 19:52:23 +0000 Subject: [PATCH] feat: Download flow schema --- api.planx.uk/modules/flows/docs.yaml | 17 ++++++ .../flows/downloadSchema/controller.ts | 41 ++++++++++++++ .../modules/flows/downloadSchema/service.ts | 35 ++++++++++++ .../modules/flows/publish/controller.ts | 5 +- api.planx.uk/modules/flows/routes.ts | 54 ++++--------------- .../modules/flows/validate/controller.ts | 7 ++- 6 files changed, 112 insertions(+), 47 deletions(-) create mode 100644 api.planx.uk/modules/flows/downloadSchema/controller.ts create mode 100644 api.planx.uk/modules/flows/downloadSchema/service.ts diff --git a/api.planx.uk/modules/flows/docs.yaml b/api.planx.uk/modules/flows/docs.yaml index 508bd52263..76d25589ce 100644 --- a/api.planx.uk/modules/flows/docs.yaml +++ b/api.planx.uk/modules/flows/docs.yaml @@ -241,3 +241,20 @@ paths: $ref: "#/components/responses/ValidateAndDiff" "500": $ref: "#/components/responses/ErrorMessage" + /flows/{flowId}/download-schema: + post: + summary: Download flow schema + description: Download a CSV file representing the flow's schema + tags: ["flows"] + security: + - bearerAuth: [] + parameters: + - $ref: "#/components/parameters/flowId" + responses: + "200": + content: + text/csv: + schema: + type: string + "500": + $ref: "#/components/responses/ErrorMessage" diff --git a/api.planx.uk/modules/flows/downloadSchema/controller.ts b/api.planx.uk/modules/flows/downloadSchema/controller.ts new file mode 100644 index 0000000000..95113bacc4 --- /dev/null +++ b/api.planx.uk/modules/flows/downloadSchema/controller.ts @@ -0,0 +1,41 @@ +import { z } from "zod"; +import { ValidatedRequestHandler } from "../../../shared/middleware/validate"; +import { stringify } from "csv-stringify"; +import { getFlowSchema } from "./service"; +import { ServerError } from "../../../errors"; + +interface DownloadFlowSchemaResponse { + message: string; + alteredNodes: Node[] | null; + description?: string; +} + +export const downloadFlowSchema = z.object({ + params: z.object({ + flowId: z.string(), + }), +}); + +export type DownloadFlowSchemaController = ValidatedRequestHandler< + typeof downloadFlowSchema, + DownloadFlowSchemaResponse +>; + +export const downloadFlowSchemaController: DownloadFlowSchemaController = + async (_res, res, next) => { + try { + const { flowId } = res.locals.parsedReq.params; + const flowSchema = await getFlowSchema(flowId); + + // Build a CSV and stream it + stringify(flowSchema, { header: true }).pipe(res); + res.header("Content-type", "text/csv"); + res.attachment(`${flowId}.csv`); + } catch (error) { + return next( + new ServerError({ + message: `Failed to download flow schema: ${error}`, + }), + ); + } + }; diff --git a/api.planx.uk/modules/flows/downloadSchema/service.ts b/api.planx.uk/modules/flows/downloadSchema/service.ts new file mode 100644 index 0000000000..8c5ae5211c --- /dev/null +++ b/api.planx.uk/modules/flows/downloadSchema/service.ts @@ -0,0 +1,35 @@ +import { $public } from "../../../client"; +import { gql } from "graphql-request"; + +interface FlowSchema { + node: string; + type: string; + text: string; + planx_variable: string; +} + +export const getFlowSchema = async (flowId: string) => { + const { flowSchema } = await $public.client.request<{ + flowSchema: FlowSchema[]; + }>( + gql` + query ($flow_id: String!) { + flowSchema: get_flow_schema(args: { published_flow_id: $flow_id }) { + node + type + text + planx_variable + } + } + `, + { flow_id: flowId }, + ); + + if (!flowSchema.length) { + throw Error( + "Can't find a schema for this flow. Make sure it's published or try a different flow id.", + ); + } + + return flowSchema; +}; diff --git a/api.planx.uk/modules/flows/publish/controller.ts b/api.planx.uk/modules/flows/publish/controller.ts index dc485741a0..2eb95a8ef2 100644 --- a/api.planx.uk/modules/flows/publish/controller.ts +++ b/api.planx.uk/modules/flows/publish/controller.ts @@ -2,6 +2,7 @@ import { Node } from "@opensystemslab/planx-core/types"; import { ValidatedRequestHandler } from "../../../shared/middleware/validate"; import { z } from "zod"; import { publishFlow } from "./service"; +import { ServerError } from "../../../errors"; interface PublishFlowResponse { message: string; @@ -37,6 +38,8 @@ export const publishFlowController: PublishFlowController = async ( message: alteredNodes ? "Changes published" : "No new changes to publish", }); } catch (error) { - return next(error); + return next( + new ServerError({ message: `Failed to publish flow: ${error}` }), + ); } }; diff --git a/api.planx.uk/modules/flows/routes.ts b/api.planx.uk/modules/flows/routes.ts index 6d4aeba7ac..d74ad9422c 100644 --- a/api.planx.uk/modules/flows/routes.ts +++ b/api.planx.uk/modules/flows/routes.ts @@ -1,9 +1,6 @@ import { Router } from "express"; import { usePlatformAdminAuth, useTeamEditorAuth } from "../auth/middleware"; import { publishFlowController } from "./publish/controller"; -import { $public } from "../../client"; -import { gql } from "graphql-request"; -import { stringify } from "csv-stringify"; import { copyFlowController, copyFlowSchema } from "./copyFlow/controller"; import { validate } from "../../shared/middleware/validate"; import { @@ -20,6 +17,10 @@ import { validateAndDiffSchema, } from "./validate/controller"; import { publishFlowSchema } from "./publish/controller"; +import { + downloadFlowSchema, + downloadFlowSchemaController, +} from "./downloadSchema/controller"; const router = Router(); router.post( @@ -64,47 +65,10 @@ router.post( validateAndDiffFlowController, ); -interface FlowSchema { - node: string; - type: string; - text: string; - planx_variable: string; -} - -router.get("/:flowId/download-schema", async (req, res, next) => { - try { - const { flowSchema } = await $public.client.request<{ - flowSchema: FlowSchema[]; - }>( - gql` - query ($flow_id: String!) { - flowSchema: get_flow_schema(args: { published_flow_id: $flow_id }) { - node - type - text - planx_variable - } - } - `, - { flow_id: req.params.flowId }, - ); - - if (!flowSchema.length) { - next({ - status: 404, - message: - "Can't find a schema for this flow. Make sure it's published or try a different flow id.", - }); - } else { - // build a CSV and stream it - stringify(flowSchema, { header: true }).pipe(res); - - res.header("Content-type", "text/csv"); - res.attachment(`${req.params.flowId}.csv`); - } - } catch (err) { - next(err); - } -}); +router.get( + "/:flowId/download-schema", + validate(downloadFlowSchema), + downloadFlowSchemaController, +); export default router; diff --git a/api.planx.uk/modules/flows/validate/controller.ts b/api.planx.uk/modules/flows/validate/controller.ts index f705b4c781..5020a798a5 100644 --- a/api.planx.uk/modules/flows/validate/controller.ts +++ b/api.planx.uk/modules/flows/validate/controller.ts @@ -2,6 +2,7 @@ import { Node } from "@opensystemslab/planx-core/types"; import { ValidatedRequestHandler } from "../../../shared/middleware/validate"; import { z } from "zod"; import { validateAndDiffFlow } from "./service"; +import { ServerError } from "../../../errors"; interface ValidateAndDiffResponse { message: string; @@ -27,6 +28,10 @@ export const validateAndDiffFlowController: ValidateAndDiffFlowController = const result = await validateAndDiffFlow(flowId); return res.json(result); } catch (error) { - return next(error); + return next( + new ServerError({ + message: `Failed to validate and diff flow: ${error}`, + }), + ); } };