From 31939e3dc93053cb4557a66792a772244dfe1c20 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 16 Sep 2020 19:25:52 +0100 Subject: [PATCH] Update to improve test cases and get JOI to work as expected. --- .../src/builderStore/store/workflow/index.js | 1 + .../src/api/routes/tests/workflow.spec.js | 91 +++++++++++++++++-- packages/server/src/api/routes/workflow.js | 30 +++--- .../server/src/middleware/joi-validator.js | 9 +- .../server/src/workflows/steps/createUser.js | 1 + packages/server/src/workflows/steps/delay.js | 1 + .../src/workflows/steps/deleteRecord.js | 1 + packages/server/src/workflows/steps/filter.js | 1 + .../server/src/workflows/steps/saveRecord.js | 1 + .../server/src/workflows/steps/sendEmail.js | 1 + packages/server/src/workflows/thread.js | 2 +- packages/server/src/workflows/triggers.js | 2 + 12 files changed, 115 insertions(+), 26 deletions(-) diff --git a/packages/builder/src/builderStore/store/workflow/index.js b/packages/builder/src/builderStore/store/workflow/index.js index 6a4db7afaf..2533988735 100644 --- a/packages/builder/src/builderStore/store/workflow/index.js +++ b/packages/builder/src/builderStore/store/workflow/index.js @@ -23,6 +23,7 @@ const workflowActions = store => ({ create: async ({ name }) => { const workflow = { name, + type: "workflow", definition: { steps: [], }, diff --git a/packages/server/src/api/routes/tests/workflow.spec.js b/packages/server/src/api/routes/tests/workflow.spec.js index 5991d06d9d..a631955ec2 100644 --- a/packages/server/src/api/routes/tests/workflow.spec.js +++ b/packages/server/src/api/routes/tests/workflow.spec.js @@ -2,6 +2,7 @@ const { createClientDatabase, createApplication, createInstance, + createModel, defaultHeaders, supertest, insertDocument, @@ -19,24 +20,24 @@ const TEST_WORKFLOW = { }, definition: { - triggers: [ - + trigger: {}, + steps: [ ], - next: { - stepId: "abc123", - type: "SERVER", - conditions: { - } - } - } + }, + type: "workflow", } +let ACTION_DEFINITIONS = {} +let TRIGGER_DEFINITIONS = {} +let LOGIC_DEFINITIONS = {} + describe("/workflows", () => { let request let server let app let instance let workflow + let model beforeAll(async () => { ({ request, server } = await supertest()) @@ -45,8 +46,9 @@ describe("/workflows", () => { }) beforeEach(async () => { + if (workflow) await destroyDocument(workflow.id) instance = await createInstance(request, app._id) - if (workflow) await destroyDocument(workflow.id); + model = await createModel(request, app._id, instance._id) }) afterAll(async () => { @@ -58,9 +60,77 @@ describe("/workflows", () => { type: "workflow", ...TEST_WORKFLOW }); + workflow = { ...workflow, ...TEST_WORKFLOW } } + describe("get definitions", () => { + it("returns a list of definitions for actions", async () => { + const res = await request + .get(`/api/workflows/action/list`) + .set(defaultHeaders(app._id, instance._id)) + .expect('Content-Type', /json/) + .expect(200) + + expect(Object.keys(res.body).length).not.toEqual(0) + ACTION_DEFINITIONS = res.body + }) + + it("returns a list of definitions for triggers", async () => { + const res = await request + .get(`/api/workflows/trigger/list`) + .set(defaultHeaders(app._id, instance._id)) + .expect('Content-Type', /json/) + .expect(200) + + expect(Object.keys(res.body).length).not.toEqual(0) + TRIGGER_DEFINITIONS = res.body + }) + + it("returns a list of definitions for actions", async () => { + const res = await request + .get(`/api/workflows/logic/list`) + .set(defaultHeaders(app._id, instance._id)) + .expect('Content-Type', /json/) + .expect(200) + + expect(Object.keys(res.body).length).not.toEqual(0) + LOGIC_DEFINITIONS = res.body + }) + + it("returns all of the definitions in one", async () => { + const res = await request + .get(`/api/workflows/definitions/list`) + .set(defaultHeaders(app._id, instance._id)) + .expect('Content-Type', /json/) + .expect(200) + + expect(Object.keys(res.body.action).length).toEqual(Object.keys(ACTION_DEFINITIONS).length) + expect(Object.keys(res.body.trigger).length).toEqual(Object.keys(TRIGGER_DEFINITIONS).length) + expect(Object.keys(res.body.logic).length).toEqual(Object.keys(LOGIC_DEFINITIONS).length) + }) + }) + describe("create", () => { + it("should setup the workflow fully", () => { + let trigger = TRIGGER_DEFINITIONS["RECORD_SAVED"] + trigger.inputs.modelId = model._id + trigger.id = "wadiawdo34" + let saveAction = ACTION_DEFINITIONS["SAVE_RECORD"] + saveAction.inputs.record = { + modelId: model._id, + name: "Testing", + } + saveAction.id = "awde444wk" + let deleteAction = ACTION_DEFINITIONS["DELETE_RECORD"] + deleteAction.inputs.id = "{{blocks[1].id}}" + deleteAction.inputs.revision = "{{blocks[1].revision}}" + deleteAction.id = "78MOt8nQO" + + TEST_WORKFLOW.definition.steps.push(saveAction) + TEST_WORKFLOW.definition.steps.push(deleteAction) + TEST_WORKFLOW.definition.trigger = trigger + }) + it("returns a success message when the workflow is successfully created", async () => { const res = await request .post(`/api/workflows`) @@ -91,6 +161,7 @@ describe("/workflows", () => { workflow._id = workflow.id workflow._rev = workflow.rev workflow.name = "Updated Name"; + workflow.type = "workflow" const res = await request .put(`/api/workflows`) diff --git a/packages/server/src/api/routes/workflow.js b/packages/server/src/api/routes/workflow.js index e6a61ae5f5..1101b3ab61 100644 --- a/packages/server/src/api/routes/workflow.js +++ b/packages/server/src/api/routes/workflow.js @@ -23,18 +23,20 @@ function generateStepSchema(allowStepTypes) { }).unknown(true) } -// prettier-ignore -const workflowValidator = joiValidator.body(Joi.object({ - live: Joi.bool(), - id: Joi.string().required(), - rev: Joi.string().required(), - name: Joi.string().required(), - type: Joi.string().valid("workflow").required(), - definition: Joi.object({ - steps: Joi.array().required().items(generateStepSchema(["ACTION", "LOGIC"])), - trigger: generateStepSchema(["TRIGGER"]).required(), - }).required().unknown(true), -}).unknown(true)) +function generateValidator(existing = false) { + // prettier-ignore + return joiValidator.body(Joi.object({ + live: Joi.bool(), + id: existing ? Joi.string().required() : Joi.string(), + rev: existing ? Joi.string().required() : Joi.string(), + name: Joi.string().required(), + type: Joi.string().valid("workflow").required(), + definition: Joi.object({ + steps: Joi.array().required().items(generateStepSchema(["ACTION", "LOGIC"])), + trigger: generateStepSchema(["TRIGGER"]), + }).required().unknown(true), + }).unknown(true)) +} router .get( @@ -62,13 +64,13 @@ router .put( "/api/workflows", authorized(BUILDER), - workflowValidator, + generateValidator(true), controller.update ) .post( "/api/workflows", authorized(BUILDER), - workflowValidator, + generateValidator(false), controller.create ) .post("/api/workflows/:id/trigger", controller.trigger) diff --git a/packages/server/src/middleware/joi-validator.js b/packages/server/src/middleware/joi-validator.js index 04880c81e9..b204d70c84 100644 --- a/packages/server/src/middleware/joi-validator.js +++ b/packages/server/src/middleware/joi-validator.js @@ -2,9 +2,16 @@ function validate(schema, property) { // Return a Koa middleware function return (ctx, next) => { if (schema) { - const { error } = schema.validate(ctx[property]) + let params = + ctx[property] != null + ? ctx[property] + : ctx.request[property] != null + ? ctx.request[property] + : null + const { error } = schema.validate(params) if (error) { ctx.throw(400, `Invalid ${property} - ${error.message}`) + return } } return next() diff --git a/packages/server/src/workflows/steps/createUser.js b/packages/server/src/workflows/steps/createUser.js index 8b7d47ed09..454e1ae2bb 100644 --- a/packages/server/src/workflows/steps/createUser.js +++ b/packages/server/src/workflows/steps/createUser.js @@ -7,6 +7,7 @@ module.exports.definition = { icon: "ri-user-add-fill", name: "Create User", type: "ACTION", + stepId: "CREATE_USER", inputs: {}, schema: { inputs: { diff --git a/packages/server/src/workflows/steps/delay.js b/packages/server/src/workflows/steps/delay.js index 728d75d257..b8e1cfbed7 100644 --- a/packages/server/src/workflows/steps/delay.js +++ b/packages/server/src/workflows/steps/delay.js @@ -5,6 +5,7 @@ module.exports.definition = { icon: "ri-time-fill", tagline: "Delay for {{inputs.time}} milliseconds", description: "Delay the workflow until an amount of time has passed", + stepId: "DELAY", inputs: {}, schema: { inputs: { diff --git a/packages/server/src/workflows/steps/deleteRecord.js b/packages/server/src/workflows/steps/deleteRecord.js index abf44b1c9b..a1c6ba2115 100644 --- a/packages/server/src/workflows/steps/deleteRecord.js +++ b/packages/server/src/workflows/steps/deleteRecord.js @@ -6,6 +6,7 @@ module.exports.definition = { name: "Delete Record", tagline: "Delete a {{inputs.enriched.model.name}} record", type: "ACTION", + stepId: "DELETE_RECORD", inputs: {}, schema: { inputs: { diff --git a/packages/server/src/workflows/steps/filter.js b/packages/server/src/workflows/steps/filter.js index 080af957e1..b5c353a5ad 100644 --- a/packages/server/src/workflows/steps/filter.js +++ b/packages/server/src/workflows/steps/filter.js @@ -11,6 +11,7 @@ module.exports.definition = { icon: "ri-git-branch-line", description: "Filter any workflows which do not meet certain conditions", type: "LOGIC", + stepId: "FILTER", inputs: {}, schema: { inputs: { diff --git a/packages/server/src/workflows/steps/saveRecord.js b/packages/server/src/workflows/steps/saveRecord.js index 2d88a74cde..1befe182f9 100644 --- a/packages/server/src/workflows/steps/saveRecord.js +++ b/packages/server/src/workflows/steps/saveRecord.js @@ -6,6 +6,7 @@ module.exports.definition = { icon: "ri-save-3-fill", description: "Save a record to your database", type: "ACTION", + stepId: "SAVE_RECORD", inputs: {}, schema: { inputs: { diff --git a/packages/server/src/workflows/steps/sendEmail.js b/packages/server/src/workflows/steps/sendEmail.js index f262cd3a1b..cb2ec007e3 100644 --- a/packages/server/src/workflows/steps/sendEmail.js +++ b/packages/server/src/workflows/steps/sendEmail.js @@ -7,6 +7,7 @@ module.exports.definition = { icon: "ri-mail-open-fill", name: "Send Email", type: "ACTION", + stepId: "SEND_EMAIL", inputs: {}, schema: { inputs: { diff --git a/packages/server/src/workflows/thread.js b/packages/server/src/workflows/thread.js index 177e7cbe52..8d754ded56 100644 --- a/packages/server/src/workflows/thread.js +++ b/packages/server/src/workflows/thread.js @@ -25,7 +25,7 @@ class Orchestrator { constructor(workflow, triggerOutput) { this._instanceId = triggerOutput.instanceId // block zero is never used as the mustache is zero indexed for customer facing - this._context = { blocks: [{}, triggerOutput] } + this._context = { blocks: [{}], trigger: triggerOutput } this._workflow = workflow } diff --git a/packages/server/src/workflows/triggers.js b/packages/server/src/workflows/triggers.js index 3149c52a65..a0f74fd756 100644 --- a/packages/server/src/workflows/triggers.js +++ b/packages/server/src/workflows/triggers.js @@ -11,6 +11,7 @@ const BUILTIN_DEFINITIONS = { icon: "ri-save-line", tagline: "Record is added to {{inputs.enriched.model.name}}", description: "Fired when a record is saved to your database", + stepId: "RECORD_SAVED", inputs: {}, schema: { inputs: { @@ -42,6 +43,7 @@ const BUILTIN_DEFINITIONS = { icon: "ri-delete-bin-line", tagline: "Record is deleted from {{inputs.enriched.model.name}}", description: "Fired when a record is deleted from your database", + stepId: "RECORD_DELETED", inputs: {}, schema: { inputs: {