Skip to content

Commit

Permalink
feat: Validate and diff flow
Browse files Browse the repository at this point in the history
  • Loading branch information
DafyddLlyr committed Nov 13, 2023
1 parent 376a80d commit 1c58cec
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 57 deletions.
45 changes: 42 additions & 3 deletions api.planx.uk/modules/flows/docs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,35 @@ components:
type: string
required: true
alteredNodes:
type: array
items:
$ref: "#/components/schemas/Node"
oneOf:
- type: array
items:
$ref: "#/components/schemas/Node"
- type: "null"
updatedFlow:
$ref: "#/components/schemas/FlowData"
required: false
ValidateAndDiff:
content:
application/json:
schema:
type: object
properties:
message:
type: string
required: false
description:
type: string
required: false
alteredNodes:
oneOf:
- type: array
items:
$ref: "#/components/schemas/Node"
- type: "null"
updatedFlow:
$ref: "#/components/schemas/FlowData"
required: false
paths:
/flows/{flowId}/copy:
post:
Expand Down Expand Up @@ -202,3 +227,17 @@ paths:
$ref: "#/components/responses/PublishFlow"
"500":
$ref: "#/components/responses/ErrorMessage"
/flows/{flowId}/diff:
post:
summary: Diff and validate a flow
description: Validate and view the diff between the current unpublished version of a flow and the most recently published version
tags: ["flows"]
security:
- bearerAuth: []
parameters:
- $ref: "#/components/parameters/flowId"
responses:
"200":
$ref: "#/components/responses/ValidateAndDiff"
"500":
$ref: "#/components/responses/ErrorMessage"
2 changes: 1 addition & 1 deletion api.planx.uk/modules/flows/publish/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { publishFlow } from "./service";

interface PublishFlowResponse {
message: string;
alteredNodes?: Node[];
alteredNodes: Node[] | null;
}

export const publishFlowSchema = z.object({
Expand Down
1 change: 1 addition & 0 deletions api.planx.uk/modules/flows/publish/publish.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ describe("publish", () => {
.expect(200)
.then((res) => {
expect(res.body).toEqual({
alteredNodes: null,
message: "No new changes to publish",
});
});
Expand Down
2 changes: 1 addition & 1 deletion api.planx.uk/modules/flows/publish/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const publishFlow = async (flowId: string, summary?: string) => {
const mostRecent = await getMostRecentPublishedFlow(flowId);
const delta = jsondiffpatch.diff(mostRecent, flattenedFlow);

if (!delta) return;
if (!delta) return null;

const { client: $client } = getClient();
const response = await $client.request<PublishFlow>(
Expand Down
12 changes: 10 additions & 2 deletions api.planx.uk/modules/flows/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ import {
findAndReplaceSchema,
} from "./findReplace/controller";
import { moveFlowController, moveFlowSchema } from "./moveFlow/controller";
import { validateAndDiffFlow } from "./validate/service";
import {
validateAndDiffFlowController,
validateAndDiffSchema,
} from "./validate/controller";
import { publishFlowSchema } from "./publish/controller";
const router = Router();

Expand Down Expand Up @@ -54,7 +57,12 @@ router.post(
publishFlowController,
);

router.post("/:flowId/diff", useTeamEditorAuth, validateAndDiffFlow);
router.post(
"/:flowId/diff",
useTeamEditorAuth,
validate(validateAndDiffSchema),
validateAndDiffFlowController,
);

interface FlowSchema {
node: string;
Expand Down
32 changes: 32 additions & 0 deletions api.planx.uk/modules/flows/validate/controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Node } from "@opensystemslab/planx-core/types";
import { ValidatedRequestHandler } from "../../../shared/middleware/validate";
import { z } from "zod";
import { validateAndDiffFlow } from "./service";

interface ValidateAndDiffResponse {
message: string;
alteredNodes: Node[] | null;
description?: string;
}

export const validateAndDiffSchema = z.object({
params: z.object({
flowId: z.string(),
}),
});

export type ValidateAndDiffFlowController = ValidatedRequestHandler<
typeof validateAndDiffSchema,
ValidateAndDiffResponse
>;

export const validateAndDiffFlowController: ValidateAndDiffFlowController =
async (_req, res, next) => {
try {
const { flowId } = res.locals.parsedReq.params;
const result = await validateAndDiffFlow(flowId);
return res.json(result);
} catch (error) {
return next(error);
}
};
91 changes: 41 additions & 50 deletions api.planx.uk/modules/flows/validate/service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as jsondiffpatch from "jsondiffpatch";
import { Request, Response, NextFunction } from "express";
import { dataMerged, getMostRecentPublishedFlow } from "../../../helpers";
import intersection from "lodash/intersection";
import {
Expand All @@ -9,61 +8,53 @@ import {
} from "@opensystemslab/planx-core/types";
import type { Entry } from "type-fest";

const validateAndDiffFlow = async (
req: Request,
res: Response,
next: NextFunction,
): Promise<Response | NextFunction | void> => {
try {
const flattenedFlow = await dataMerged(req.params.flowId);

const {
isValid: sectionsAreValid,
const validateAndDiffFlow = async (flowId: string) => {
const flattenedFlow = await dataMerged(flowId);

const {
isValid: sectionsAreValid,
message: sectionsValidationMessage,
description: sectionsValidationDescription,
} = validateSections(flattenedFlow);
if (!sectionsAreValid) {
return {
alteredNodes: null,
message: sectionsValidationMessage,
description: sectionsValidationDescription,
} = validateSections(flattenedFlow);
if (!sectionsAreValid) {
return res.json({
alteredNodes: null,
message: sectionsValidationMessage,
description: sectionsValidationDescription,
});
}
};
}

const {
isValid: payIsValid,
const {
isValid: payIsValid,
message: payValidationMessage,
description: payValidationDescription,
} = validateInviteToPay(flattenedFlow);
if (!payIsValid) {
return {
alteredNodes: null,
message: payValidationMessage,
description: payValidationDescription,
} = validateInviteToPay(flattenedFlow);
if (!payIsValid) {
return res.json({
alteredNodes: null,
message: payValidationMessage,
description: payValidationDescription,
});
}

const mostRecent = await getMostRecentPublishedFlow(req.params.flowId);
const delta = jsondiffpatch.diff(mostRecent, flattenedFlow);

if (delta) {
const alteredNodes = Object.keys(delta).map((key) => ({
id: key,
...flattenedFlow[key],
}));

return res.json({
alteredNodes,
});
} else {
return res.json({
alteredNodes: null,
message: "No new changes to publish",
});
}
} catch (error) {
return next(error);
};
}

const mostRecent = await getMostRecentPublishedFlow(flowId);
const delta = jsondiffpatch.diff(mostRecent, flattenedFlow);

if (!delta)
return {
alteredNodes: null,
message: "No new changes to publish",
};

const alteredNodes = Object.keys(delta).map((key) => ({
id: key,
...flattenedFlow[key],
}));

return {
alteredNodes,
message: "Changes valid",
};
};

type ValidationResponse = {
Expand Down

0 comments on commit 1c58cec

Please sign in to comment.