Update to improve test cases and get JOI to work as expected.

This commit is contained in:
mike12345567 2020-09-16 19:25:52 +01:00
parent 5997550fbb
commit 31939e3dc9
12 changed files with 115 additions and 26 deletions

View File

@ -23,6 +23,7 @@ const workflowActions = store => ({
create: async ({ name }) => { create: async ({ name }) => {
const workflow = { const workflow = {
name, name,
type: "workflow",
definition: { definition: {
steps: [], steps: [],
}, },

View File

@ -2,6 +2,7 @@ const {
createClientDatabase, createClientDatabase,
createApplication, createApplication,
createInstance, createInstance,
createModel,
defaultHeaders, defaultHeaders,
supertest, supertest,
insertDocument, insertDocument,
@ -19,24 +20,24 @@ const TEST_WORKFLOW = {
}, },
definition: { definition: {
triggers: [ trigger: {},
steps: [
], ],
next: { },
stepId: "abc123", type: "workflow",
type: "SERVER",
conditions: {
}
}
}
} }
let ACTION_DEFINITIONS = {}
let TRIGGER_DEFINITIONS = {}
let LOGIC_DEFINITIONS = {}
describe("/workflows", () => { describe("/workflows", () => {
let request let request
let server let server
let app let app
let instance let instance
let workflow let workflow
let model
beforeAll(async () => { beforeAll(async () => {
({ request, server } = await supertest()) ({ request, server } = await supertest())
@ -45,8 +46,9 @@ describe("/workflows", () => {
}) })
beforeEach(async () => { beforeEach(async () => {
if (workflow) await destroyDocument(workflow.id)
instance = await createInstance(request, app._id) instance = await createInstance(request, app._id)
if (workflow) await destroyDocument(workflow.id); model = await createModel(request, app._id, instance._id)
}) })
afterAll(async () => { afterAll(async () => {
@ -58,9 +60,77 @@ describe("/workflows", () => {
type: "workflow", type: "workflow",
...TEST_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", () => { 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 () => { it("returns a success message when the workflow is successfully created", async () => {
const res = await request const res = await request
.post(`/api/workflows`) .post(`/api/workflows`)
@ -91,6 +161,7 @@ describe("/workflows", () => {
workflow._id = workflow.id workflow._id = workflow.id
workflow._rev = workflow.rev workflow._rev = workflow.rev
workflow.name = "Updated Name"; workflow.name = "Updated Name";
workflow.type = "workflow"
const res = await request const res = await request
.put(`/api/workflows`) .put(`/api/workflows`)

View File

@ -23,18 +23,20 @@ function generateStepSchema(allowStepTypes) {
}).unknown(true) }).unknown(true)
} }
function generateValidator(existing = false) {
// prettier-ignore // prettier-ignore
const workflowValidator = joiValidator.body(Joi.object({ return joiValidator.body(Joi.object({
live: Joi.bool(), live: Joi.bool(),
id: Joi.string().required(), id: existing ? Joi.string().required() : Joi.string(),
rev: Joi.string().required(), rev: existing ? Joi.string().required() : Joi.string(),
name: Joi.string().required(), name: Joi.string().required(),
type: Joi.string().valid("workflow").required(), type: Joi.string().valid("workflow").required(),
definition: Joi.object({ definition: Joi.object({
steps: Joi.array().required().items(generateStepSchema(["ACTION", "LOGIC"])), steps: Joi.array().required().items(generateStepSchema(["ACTION", "LOGIC"])),
trigger: generateStepSchema(["TRIGGER"]).required(), trigger: generateStepSchema(["TRIGGER"]),
}).required().unknown(true), }).required().unknown(true),
}).unknown(true)) }).unknown(true))
}
router router
.get( .get(
@ -62,13 +64,13 @@ router
.put( .put(
"/api/workflows", "/api/workflows",
authorized(BUILDER), authorized(BUILDER),
workflowValidator, generateValidator(true),
controller.update controller.update
) )
.post( .post(
"/api/workflows", "/api/workflows",
authorized(BUILDER), authorized(BUILDER),
workflowValidator, generateValidator(false),
controller.create controller.create
) )
.post("/api/workflows/:id/trigger", controller.trigger) .post("/api/workflows/:id/trigger", controller.trigger)

View File

@ -2,9 +2,16 @@ function validate(schema, property) {
// Return a Koa middleware function // Return a Koa middleware function
return (ctx, next) => { return (ctx, next) => {
if (schema) { 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) { if (error) {
ctx.throw(400, `Invalid ${property} - ${error.message}`) ctx.throw(400, `Invalid ${property} - ${error.message}`)
return
} }
} }
return next() return next()

View File

@ -7,6 +7,7 @@ module.exports.definition = {
icon: "ri-user-add-fill", icon: "ri-user-add-fill",
name: "Create User", name: "Create User",
type: "ACTION", type: "ACTION",
stepId: "CREATE_USER",
inputs: {}, inputs: {},
schema: { schema: {
inputs: { inputs: {

View File

@ -5,6 +5,7 @@ module.exports.definition = {
icon: "ri-time-fill", icon: "ri-time-fill",
tagline: "Delay for {{inputs.time}} milliseconds", tagline: "Delay for {{inputs.time}} milliseconds",
description: "Delay the workflow until an amount of time has passed", description: "Delay the workflow until an amount of time has passed",
stepId: "DELAY",
inputs: {}, inputs: {},
schema: { schema: {
inputs: { inputs: {

View File

@ -6,6 +6,7 @@ module.exports.definition = {
name: "Delete Record", name: "Delete Record",
tagline: "Delete a {{inputs.enriched.model.name}} record", tagline: "Delete a {{inputs.enriched.model.name}} record",
type: "ACTION", type: "ACTION",
stepId: "DELETE_RECORD",
inputs: {}, inputs: {},
schema: { schema: {
inputs: { inputs: {

View File

@ -11,6 +11,7 @@ module.exports.definition = {
icon: "ri-git-branch-line", icon: "ri-git-branch-line",
description: "Filter any workflows which do not meet certain conditions", description: "Filter any workflows which do not meet certain conditions",
type: "LOGIC", type: "LOGIC",
stepId: "FILTER",
inputs: {}, inputs: {},
schema: { schema: {
inputs: { inputs: {

View File

@ -6,6 +6,7 @@ module.exports.definition = {
icon: "ri-save-3-fill", icon: "ri-save-3-fill",
description: "Save a record to your database", description: "Save a record to your database",
type: "ACTION", type: "ACTION",
stepId: "SAVE_RECORD",
inputs: {}, inputs: {},
schema: { schema: {
inputs: { inputs: {

View File

@ -7,6 +7,7 @@ module.exports.definition = {
icon: "ri-mail-open-fill", icon: "ri-mail-open-fill",
name: "Send Email", name: "Send Email",
type: "ACTION", type: "ACTION",
stepId: "SEND_EMAIL",
inputs: {}, inputs: {},
schema: { schema: {
inputs: { inputs: {

View File

@ -25,7 +25,7 @@ class Orchestrator {
constructor(workflow, triggerOutput) { constructor(workflow, triggerOutput) {
this._instanceId = triggerOutput.instanceId this._instanceId = triggerOutput.instanceId
// block zero is never used as the mustache is zero indexed for customer facing // 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 this._workflow = workflow
} }

View File

@ -11,6 +11,7 @@ const BUILTIN_DEFINITIONS = {
icon: "ri-save-line", icon: "ri-save-line",
tagline: "Record is added to {{inputs.enriched.model.name}}", tagline: "Record is added to {{inputs.enriched.model.name}}",
description: "Fired when a record is saved to your database", description: "Fired when a record is saved to your database",
stepId: "RECORD_SAVED",
inputs: {}, inputs: {},
schema: { schema: {
inputs: { inputs: {
@ -42,6 +43,7 @@ const BUILTIN_DEFINITIONS = {
icon: "ri-delete-bin-line", icon: "ri-delete-bin-line",
tagline: "Record is deleted from {{inputs.enriched.model.name}}", tagline: "Record is deleted from {{inputs.enriched.model.name}}",
description: "Fired when a record is deleted from your database", description: "Fired when a record is deleted from your database",
stepId: "RECORD_DELETED",
inputs: {}, inputs: {},
schema: { schema: {
inputs: { inputs: {