-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9670291
commit eebd986
Showing
9 changed files
with
512 additions
and
57 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { z } from "zod"; | ||
import type { ValidatedRequestHandler } from "../../../shared/middleware/validate.js"; | ||
import { ServerError } from "../../../errors/index.js"; | ||
import { createFlow } from "../../../helpers.js"; | ||
import type { Flow } from "../../../types.js"; | ||
|
||
interface CreateFlowResponse { | ||
message: string; | ||
id: Flow["id"]; | ||
slug: Flow["slug"]; | ||
} | ||
|
||
export const createFlowSchema = z.object({ | ||
body: z.object({ | ||
teamId: z.number(), | ||
slug: z.string(), | ||
name: z.string(), | ||
data: z.any(), // FlowGraph | ||
}), | ||
}); | ||
|
||
export type CreateFlowController = ValidatedRequestHandler< | ||
typeof createFlowSchema, | ||
CreateFlowResponse | ||
>; | ||
|
||
export const createFlowController: CreateFlowController = async ( | ||
_req, | ||
res, | ||
next, | ||
) => { | ||
try { | ||
const { teamId, slug, name, data } = res.locals.parsedReq.body; | ||
|
||
// createFlow automatically handles the associated operation and initial publish | ||
const { id } = await createFlow(teamId, slug, name, data); | ||
|
||
res.status(200).send({ | ||
message: `Successfully created flow ${slug}`, | ||
id, | ||
slug, | ||
}); | ||
} catch (error) { | ||
return next( | ||
new ServerError({ message: `Failed to create flow. Error: ${error}` }), | ||
); | ||
} | ||
}; |
157 changes: 157 additions & 0 deletions
157
api.planx.uk/modules/flows/createFlow/createFlow.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
import supertest from "supertest"; | ||
|
||
import { queryMock } from "../../../tests/graphqlQueryMock.js"; | ||
import { authHeader } from "../../../tests/mockJWT.js"; | ||
import app from "../../../server.js"; | ||
import type { Flow } from "../../../types.js"; | ||
|
||
const mockNewFlowData: Flow["data"] = { | ||
_root: { | ||
edges: [], | ||
}, | ||
}; | ||
|
||
const validBody = { | ||
teamId: 1, | ||
slug: "my-new-flow", | ||
name: "My new flow", | ||
data: mockNewFlowData, | ||
}; | ||
|
||
const invalidBody = { | ||
slug: "my-new-flow", | ||
data: mockNewFlowData, | ||
}; | ||
|
||
beforeEach(() => { | ||
queryMock.mockQuery({ | ||
name: "GetFlowData", | ||
matchOnVariables: false, | ||
data: { | ||
flow: { | ||
data: mockNewFlowData, | ||
}, | ||
}, | ||
}); | ||
|
||
queryMock.mockQuery({ | ||
name: "InsertFlow", | ||
matchOnVariables: false, | ||
data: { | ||
flow: { | ||
id: "2", | ||
}, | ||
}, | ||
}); | ||
|
||
queryMock.mockQuery({ | ||
name: "InsertOperation", | ||
matchOnVariables: false, | ||
data: { | ||
operation: { | ||
id: 1, | ||
}, | ||
}, | ||
}); | ||
|
||
queryMock.mockQuery({ | ||
name: "PublishFlow", | ||
matchOnVariables: false, | ||
data: { | ||
publishedFlow: { | ||
data: mockNewFlowData, | ||
}, | ||
}, | ||
}); | ||
|
||
queryMock.mockQuery({ | ||
name: "GetMostRecentPublishedFlow", | ||
matchOnVariables: false, | ||
data: { | ||
flow: { | ||
publishedFlows: [ | ||
{ | ||
data: mockNewFlowData, | ||
}, | ||
], | ||
}, | ||
}, | ||
}); | ||
}); | ||
|
||
const auth = authHeader({ role: "teamEditor" }); | ||
|
||
it("returns an error if authorization headers are not set", async () => { | ||
await supertest(app) | ||
.post("/flows/create") | ||
.send(validBody) | ||
.expect(401) | ||
.then((res) => { | ||
expect(res.body).toEqual({ | ||
error: "No authorization token was found", | ||
}); | ||
}); | ||
}); | ||
|
||
it("returns an error if the user does not have the correct role", async () => { | ||
await supertest(app) | ||
.post("/flows/create") | ||
.send(validBody) | ||
.set(authHeader({ role: "teamViewer" })) | ||
.expect(403); | ||
}); | ||
|
||
it("returns an error if required properties are missing in the request body", async () => { | ||
await supertest(app) | ||
.post("/flows/create") | ||
.send(invalidBody) | ||
.set(auth) | ||
.expect(400) | ||
.then((res) => { | ||
expect(res.body).toHaveProperty("issues"); | ||
expect(res.body).toHaveProperty("name", "ZodError"); | ||
}); | ||
}); | ||
|
||
it("returns an error when the service errors", async () => { | ||
queryMock.reset(); | ||
queryMock.mockQuery({ | ||
name: "InsertFlow", | ||
variables: { | ||
team_id: 1, | ||
slug: "my-new-flow", | ||
name: "My new flow", | ||
data: mockNewFlowData, | ||
}, | ||
data: {}, | ||
graphqlErrors: [ | ||
{ | ||
message: "Something went wrong", | ||
}, | ||
], | ||
}); | ||
|
||
await supertest(app) | ||
.post("/flows/create") | ||
.send(validBody) | ||
.set(auth) | ||
.expect(500) | ||
.then((res) => { | ||
expect(res.body.error).toMatch(/Failed to create flow/); | ||
}); | ||
}); | ||
|
||
it("successfully creates a new flow", async () => { | ||
await supertest(app) | ||
.post("/flows/create") | ||
.send(validBody) | ||
.set(auth) | ||
.expect(200) | ||
.then((res) => { | ||
expect(res.body).toEqual({ | ||
message: `Successfully created flow ${validBody.slug}`, | ||
id: "2", | ||
slug: validBody.slug, | ||
}); | ||
}); | ||
}); |
47 changes: 47 additions & 0 deletions
47
api.planx.uk/modules/flows/createFlowFromTemplate/controller.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { z } from "zod"; | ||
import type { ValidatedRequestHandler } from "../../../shared/middleware/validate.js"; | ||
import { ServerError } from "../../../errors/index.js"; | ||
import type { Flow } from "../../../types.js"; | ||
import { createFlowFromTemplate } from "./service.js"; | ||
|
||
interface CreateFlowFromTemplateResponse { | ||
message: string; | ||
id: Flow["id"]; | ||
slug: Flow["slug"]; | ||
} | ||
|
||
export const createFlowFromTemplateSchema = z.object({ | ||
params: z.object({ | ||
templateId: z.string(), | ||
}), | ||
body: z.object({ | ||
teamId: z.number(), | ||
}), | ||
}); | ||
|
||
export type CreateFlowFromTemplateController = ValidatedRequestHandler< | ||
typeof createFlowFromTemplateSchema, | ||
CreateFlowFromTemplateResponse | ||
>; | ||
|
||
export const createFlowFromTemplateController: CreateFlowFromTemplateController = | ||
async (_req, res, next) => { | ||
try { | ||
const { templateId } = res.locals.parsedReq.params; | ||
const { teamId } = res.locals.parsedReq.body; | ||
|
||
const { id, slug } = await createFlowFromTemplate(templateId, teamId); | ||
|
||
res.status(200).send({ | ||
message: `Successfully created flow from template ${slug}`, | ||
id, | ||
slug, | ||
}); | ||
} catch (error) { | ||
return next( | ||
new ServerError({ | ||
message: `Failed to create flow from template. Error: ${error}`, | ||
}), | ||
); | ||
} | ||
}; |
Oops, something went wrong.