automation events + tests
This commit is contained in:
parent
ec4e9df4c2
commit
266b34aaa0
5 changed files with 200 additions and 43 deletions
|
@ -125,9 +125,10 @@ exports.Events = {
|
||||||
AUTOMATION_CREATED: "automation:created",
|
AUTOMATION_CREATED: "automation:created",
|
||||||
AUTOMATION_DELETED: "automation:deleted",
|
AUTOMATION_DELETED: "automation:deleted",
|
||||||
AUTOMATION_TESTED: "automation:tested",
|
AUTOMATION_TESTED: "automation:tested",
|
||||||
AUTOMATION_RUN: "automation:run",
|
// AUTOMATION_RUN: "automation:run",
|
||||||
AUTOMATION_STEP_CREATED: "automation:step:created",
|
AUTOMATION_STEP_CREATED: "automation:step:created",
|
||||||
AUTOMATION_STEP_DELETED: "automation:step:deleted",
|
AUTOMATION_STEP_DELETED: "automation:step:deleted",
|
||||||
|
AUTOMATION_TRIGGER_UPDATED: "automation:trigger:updated",
|
||||||
|
|
||||||
// LICENSING
|
// LICENSING
|
||||||
LICENSING_QUOTA_EXCEEDED: "licensing:quota:exceeded",
|
LICENSING_QUOTA_EXCEEDED: "licensing:quota:exceeded",
|
||||||
|
|
|
@ -6,32 +6,33 @@ exports.created = () => {
|
||||||
events.processEvent(Events.AUTOMATION_CREATED, properties)
|
events.processEvent(Events.AUTOMATION_CREATED, properties)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
|
||||||
exports.deleted = () => {
|
exports.deleted = () => {
|
||||||
const properties = {}
|
const properties = {}
|
||||||
events.processEvent(Events.AUTOMATION_DELETED, properties)
|
events.processEvent(Events.AUTOMATION_DELETED, properties)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
|
||||||
exports.tested = () => {
|
exports.tested = () => {
|
||||||
const properties = {}
|
const properties = {}
|
||||||
events.processEvent(Events.AUTOMATION_TESTED, properties)
|
events.processEvent(Events.AUTOMATION_TESTED, properties)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
exports.run = () => {
|
// exports.run = () => {
|
||||||
const properties = {}
|
// const properties = {}
|
||||||
events.processEvent(Events.AUTOMATION_RUN, properties)
|
// events.processEvent(Events.AUTOMATION_RUN, properties)
|
||||||
}
|
// }
|
||||||
|
|
||||||
// TODO
|
|
||||||
exports.stepCreated = () => {
|
exports.stepCreated = () => {
|
||||||
const properties = {}
|
const properties = {}
|
||||||
events.processEvent(Events.AUTOMATION_STEP_CREATED, properties)
|
events.processEvent(Events.AUTOMATION_STEP_CREATED, properties)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
|
||||||
exports.stepDeleted = () => {
|
exports.stepDeleted = () => {
|
||||||
const properties = {}
|
const properties = {}
|
||||||
events.processEvent(Events.AUTOMATION_STEP_DELETED, properties)
|
events.processEvent(Events.AUTOMATION_STEP_DELETED, properties)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.triggerUpdated = () => {
|
||||||
|
const properties = {}
|
||||||
|
events.processEvent(Events.AUTOMATION_TRIGGER_UPDATED, properties)
|
||||||
|
}
|
||||||
|
|
|
@ -26,6 +26,15 @@ jest.mock("../../../events", () => {
|
||||||
SSOActivated: jest.fn(),
|
SSOActivated: jest.fn(),
|
||||||
SSODeactivated: jest.fn(),
|
SSODeactivated: jest.fn(),
|
||||||
},
|
},
|
||||||
|
automation: {
|
||||||
|
created: jest.fn(),
|
||||||
|
deleted: jest.fn(),
|
||||||
|
tested: jest.fn(),
|
||||||
|
// run: jest.fn(),
|
||||||
|
stepCreated: jest.fn(),
|
||||||
|
stepDeleted: jest.fn(),
|
||||||
|
triggerUpdated: jest.fn(),
|
||||||
|
},
|
||||||
datasource: {
|
datasource: {
|
||||||
created: jest.fn(),
|
created: jest.fn(),
|
||||||
updated: jest.fn(),
|
updated: jest.fn(),
|
||||||
|
|
|
@ -10,6 +10,7 @@ const { deleteEntityMetadata } = require("../../utilities")
|
||||||
const { MetadataTypes } = require("../../constants")
|
const { MetadataTypes } = require("../../constants")
|
||||||
const { setTestFlag, clearTestFlag } = require("../../utilities/redis")
|
const { setTestFlag, clearTestFlag } = require("../../utilities/redis")
|
||||||
const { getAppDB } = require("@budibase/backend-core/context")
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
|
const { events } = require("@budibase/backend-core")
|
||||||
|
|
||||||
const ACTION_DEFS = removeDeprecated(actions.ACTION_DEFINITIONS)
|
const ACTION_DEFS = removeDeprecated(actions.ACTION_DEFINITIONS)
|
||||||
const TRIGGER_DEFS = removeDeprecated(triggers.TRIGGER_DEFINITIONS)
|
const TRIGGER_DEFS = removeDeprecated(triggers.TRIGGER_DEFINITIONS)
|
||||||
|
@ -70,6 +71,10 @@ exports.create = async function (ctx) {
|
||||||
newAuto: automation,
|
newAuto: automation,
|
||||||
})
|
})
|
||||||
const response = await db.put(automation)
|
const response = await db.put(automation)
|
||||||
|
events.automation.created()
|
||||||
|
for (let step of automation.definition.steps) {
|
||||||
|
events.automation.stepCreated(step)
|
||||||
|
}
|
||||||
automation._rev = response.rev
|
automation._rev = response.rev
|
||||||
|
|
||||||
ctx.status = 200
|
ctx.status = 200
|
||||||
|
@ -82,6 +87,29 @@ exports.create = async function (ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getNewSteps = (oldAutomation, automation) => {
|
||||||
|
const oldStepIds = oldAutomation.definition.steps.map(s => s.id)
|
||||||
|
return automation.definition.steps.filter(s => !oldStepIds.includes(s.id))
|
||||||
|
}
|
||||||
|
|
||||||
|
const getDeletedSteps = (oldAutomation, automation) => {
|
||||||
|
const stepIds = automation.definition.steps.map(s => s.id)
|
||||||
|
return oldAutomation.definition.steps.filter(s => !stepIds.includes(s.id))
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleStepEvents = (oldAutomation, automation) => {
|
||||||
|
// new steps
|
||||||
|
const newSteps = getNewSteps(oldAutomation, automation)
|
||||||
|
for (let step of newSteps) {
|
||||||
|
events.automation.stepCreated(step)
|
||||||
|
}
|
||||||
|
|
||||||
|
// old steps
|
||||||
|
const deletedSteps = getDeletedSteps(oldAutomation, automation)
|
||||||
|
for (let step of deletedSteps) {
|
||||||
|
events.automation.stepDeleted(step)
|
||||||
|
}
|
||||||
|
}
|
||||||
exports.update = async function (ctx) {
|
exports.update = async function (ctx) {
|
||||||
const db = getAppDB()
|
const db = getAppDB()
|
||||||
let automation = ctx.request.body
|
let automation = ctx.request.body
|
||||||
|
@ -98,13 +126,14 @@ exports.update = async function (ctx) {
|
||||||
const oldAutoTrigger =
|
const oldAutoTrigger =
|
||||||
oldAutomation && oldAutomation.definition.trigger
|
oldAutomation && oldAutomation.definition.trigger
|
||||||
? oldAutomation.definition.trigger
|
? oldAutomation.definition.trigger
|
||||||
: {}
|
: undefined
|
||||||
const newAutoTrigger =
|
const newAutoTrigger =
|
||||||
automation && automation.definition.trigger
|
automation && automation.definition.trigger
|
||||||
? automation.definition.trigger
|
? automation.definition.trigger
|
||||||
: {}
|
: {}
|
||||||
// trigger has been updated, remove the test inputs
|
// trigger has been updated, remove the test inputs
|
||||||
if (oldAutoTrigger.id !== newAutoTrigger.id) {
|
if (oldAutoTrigger && oldAutoTrigger.id !== newAutoTrigger.id) {
|
||||||
|
events.automation.triggerUpdated()
|
||||||
await deleteEntityMetadata(
|
await deleteEntityMetadata(
|
||||||
ctx.appId,
|
ctx.appId,
|
||||||
MetadataTypes.AUTOMATION_TEST_INPUT,
|
MetadataTypes.AUTOMATION_TEST_INPUT,
|
||||||
|
@ -112,6 +141,8 @@ exports.update = async function (ctx) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleStepEvents(oldAutomation, automation)
|
||||||
|
|
||||||
ctx.status = 200
|
ctx.status = 200
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
message: `Automation ${automation._id} updated successfully.`,
|
message: `Automation ${automation._id} updated successfully.`,
|
||||||
|
@ -148,6 +179,7 @@ exports.destroy = async function (ctx) {
|
||||||
// delete metadata first
|
// delete metadata first
|
||||||
await cleanupAutomationMetadata(automationId)
|
await cleanupAutomationMetadata(automationId)
|
||||||
ctx.body = await db.remove(automationId, ctx.params.rev)
|
ctx.body = await db.remove(automationId, ctx.params.rev)
|
||||||
|
events.automation.deleted()
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getActionList = async function (ctx) {
|
exports.getActionList = async function (ctx) {
|
||||||
|
@ -215,4 +247,5 @@ exports.test = async function (ctx) {
|
||||||
})
|
})
|
||||||
await clearTestFlag(automation._id)
|
await clearTestFlag(automation._id)
|
||||||
ctx.body = response
|
ctx.body = response
|
||||||
|
events.automation.tested()
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ const { mocks } = require("@budibase/backend-core/testUtils")
|
||||||
mocks.date.mock()
|
mocks.date.mock()
|
||||||
const MAX_RETRIES = 4
|
const MAX_RETRIES = 4
|
||||||
const { TRIGGER_DEFINITIONS, ACTION_DEFINITIONS } = require("../../../automations")
|
const { TRIGGER_DEFINITIONS, ACTION_DEFINITIONS } = require("../../../automations")
|
||||||
|
const { events } = require("@budibase/backend-core")
|
||||||
|
|
||||||
describe("/automations", () => {
|
describe("/automations", () => {
|
||||||
let request = setup.getRequest()
|
let request = setup.getRequest()
|
||||||
|
@ -58,8 +59,9 @@ describe("/automations", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("create", () => {
|
describe("create", () => {
|
||||||
it("returns a success message when the automation is successfully created", async () => {
|
it("creates an automation with no steps", async () => {
|
||||||
const automation = newAutomation()
|
const automation = newAutomation()
|
||||||
|
automation.definition.steps = []
|
||||||
|
|
||||||
const res = await request
|
const res = await request
|
||||||
.post(`/api/automations`)
|
.post(`/api/automations`)
|
||||||
|
@ -71,6 +73,27 @@ describe("/automations", () => {
|
||||||
expect(res.body.message).toEqual("Automation created successfully")
|
expect(res.body.message).toEqual("Automation created successfully")
|
||||||
expect(res.body.automation.name).toEqual("My Automation")
|
expect(res.body.automation.name).toEqual("My Automation")
|
||||||
expect(res.body.automation._id).not.toEqual(null)
|
expect(res.body.automation._id).not.toEqual(null)
|
||||||
|
expect(events.automation.created).toBeCalledTimes(1)
|
||||||
|
expect(events.automation.stepCreated).not.toBeCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("creates an automation with steps", async () => {
|
||||||
|
const automation = newAutomation()
|
||||||
|
automation.definition.steps.push(automationStep())
|
||||||
|
jest.clearAllMocks()
|
||||||
|
|
||||||
|
const res = await request
|
||||||
|
.post(`/api/automations`)
|
||||||
|
.set(config.defaultHeaders())
|
||||||
|
.send(automation)
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect(200)
|
||||||
|
|
||||||
|
expect(res.body.message).toEqual("Automation created successfully")
|
||||||
|
expect(res.body.automation.name).toEqual("My Automation")
|
||||||
|
expect(res.body.automation._id).not.toEqual(null)
|
||||||
|
expect(events.automation.created).toBeCalledTimes(1)
|
||||||
|
expect(events.automation.stepCreated).toBeCalledTimes(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should apply authorization to endpoint", async () => {
|
it("should apply authorization to endpoint", async () => {
|
||||||
|
@ -97,8 +120,8 @@ describe("/automations", () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("trigger", () => {
|
describe("test", () => {
|
||||||
it("trigger the automation successfully", async () => {
|
it("tests the automation successfully", async () => {
|
||||||
let table = await config.createTable()
|
let table = await config.createTable()
|
||||||
let automation = newAutomation()
|
let automation = newAutomation()
|
||||||
automation.definition.trigger.inputs.tableId = table._id
|
automation.definition.trigger.inputs.tableId = table._id
|
||||||
|
@ -113,6 +136,7 @@ describe("/automations", () => {
|
||||||
automation = await config.createAutomation(automation)
|
automation = await config.createAutomation(automation)
|
||||||
await setup.delay(500)
|
await setup.delay(500)
|
||||||
const res = await testAutomation(config, automation)
|
const res = await testAutomation(config, automation)
|
||||||
|
expect(events.automation.tested).toBeCalledTimes(1)
|
||||||
// this looks a bit mad but we don't actually have a way to wait for a response from the automation to
|
// this looks a bit mad but we don't actually have a way to wait for a response from the automation to
|
||||||
// know that it has finished all of its actions - this is currently the best way
|
// know that it has finished all of its actions - this is currently the best way
|
||||||
// also when this runs in CI it is very temper-mental so for now trying to make run stable by repeating until it works
|
// also when this runs in CI it is very temper-mental so for now trying to make run stable by repeating until it works
|
||||||
|
@ -134,53 +158,141 @@ describe("/automations", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("update", () => {
|
describe("update", () => {
|
||||||
it("updates a automations data", async () => {
|
|
||||||
let automation = newAutomation()
|
|
||||||
await config.createAutomation(automation)
|
|
||||||
automation.name = "Updated Name"
|
|
||||||
|
|
||||||
const res = await request
|
const update = async (automation) => {
|
||||||
|
return request
|
||||||
.put(`/api/automations`)
|
.put(`/api/automations`)
|
||||||
.set(config.defaultHeaders())
|
.set(config.defaultHeaders())
|
||||||
.send(automation)
|
.send(automation)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
}
|
||||||
|
|
||||||
expect(res.body.message).toEqual(`Automation ${automation._id} updated successfully.`)
|
const updateWithPost = async (automation) => {
|
||||||
expect(res.body.automation.name).toEqual("Updated Name")
|
return request
|
||||||
})
|
|
||||||
|
|
||||||
it("should be able to update an automation trigger", async () => {
|
|
||||||
// create webhook automation
|
|
||||||
const webhookTrigger = automationTrigger(TRIGGER_DEFINITIONS.WEBHOOK)
|
|
||||||
let automation = newAutomation({ trigger: webhookTrigger })
|
|
||||||
|
|
||||||
let res = await request
|
|
||||||
.post(`/api/automations`)
|
.post(`/api/automations`)
|
||||||
.set(config.defaultHeaders())
|
.set(config.defaultHeaders())
|
||||||
.send(automation)
|
.send(automation)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
}
|
||||||
|
|
||||||
automation = res.body.automation
|
it("updates a automations name", async () => {
|
||||||
expect(automation._id).toBeDefined()
|
let automation = newAutomation()
|
||||||
expect(automation._rev).toBeDefined()
|
await config.createAutomation(automation)
|
||||||
|
automation.name = "Updated Name"
|
||||||
|
jest.clearAllMocks()
|
||||||
|
|
||||||
// change the trigger
|
const res = await update(automation)
|
||||||
automation.trigger = automationTrigger(TRIGGER_DEFINITIONS.ROW_SAVED)
|
|
||||||
|
|
||||||
// check the post request honours updates with same id
|
|
||||||
res = await request
|
|
||||||
.post(`/api/automations`)
|
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.send(automation)
|
|
||||||
.expect('Content-Type', /json/)
|
|
||||||
.expect(200)
|
|
||||||
|
|
||||||
const automationRes = res.body.automation
|
const automationRes = res.body.automation
|
||||||
|
const message = res.body.message
|
||||||
|
|
||||||
|
// doc attributes
|
||||||
expect(automationRes._id).toEqual(automation._id)
|
expect(automationRes._id).toEqual(automation._id)
|
||||||
expect(automationRes._rev).toBeDefined()
|
expect(automationRes._rev).toBeDefined()
|
||||||
expect(automationRes._rev).not.toEqual(automation._rev)
|
expect(automationRes._rev).not.toEqual(automation._rev)
|
||||||
|
// content updates
|
||||||
|
expect(automationRes.name).toEqual("Updated Name")
|
||||||
|
expect(message).toEqual(`Automation ${automation._id} updated successfully.`)
|
||||||
|
// events
|
||||||
|
expect(events.automation.created).not.toBeCalled()
|
||||||
|
expect(events.automation.stepCreated).not.toBeCalled()
|
||||||
|
expect(events.automation.stepDeleted).not.toBeCalled()
|
||||||
|
expect(events.automation.triggerUpdated).not.toBeCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
it("updates a automations name using POST request", async () => {
|
||||||
|
let automation = newAutomation()
|
||||||
|
await config.createAutomation(automation)
|
||||||
|
automation.name = "Updated Name"
|
||||||
|
jest.clearAllMocks()
|
||||||
|
|
||||||
|
// the POST request will defer to the update
|
||||||
|
// when an id has been supplied.
|
||||||
|
const res = await updateWithPost(automation)
|
||||||
|
|
||||||
|
const automationRes = res.body.automation
|
||||||
|
const message = res.body.message
|
||||||
|
// doc attributes
|
||||||
|
expect(automationRes._id).toEqual(automation._id)
|
||||||
|
expect(automationRes._rev).toBeDefined()
|
||||||
|
expect(automationRes._rev).not.toEqual(automation._rev)
|
||||||
|
// content updates
|
||||||
|
expect(automationRes.name).toEqual("Updated Name")
|
||||||
|
expect(message).toEqual(`Automation ${automation._id} updated successfully.`)
|
||||||
|
// events
|
||||||
|
expect(events.automation.created).not.toBeCalled()
|
||||||
|
expect(events.automation.stepCreated).not.toBeCalled()
|
||||||
|
expect(events.automation.stepDeleted).not.toBeCalled()
|
||||||
|
expect(events.automation.triggerUpdated).not.toBeCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("updates an automation trigger", async () => {
|
||||||
|
let automation = newAutomation()
|
||||||
|
automation = await config.createAutomation(automation)
|
||||||
|
automation.definition.trigger = automationTrigger(TRIGGER_DEFINITIONS.WEBHOOK)
|
||||||
|
jest.clearAllMocks()
|
||||||
|
|
||||||
|
await update(automation)
|
||||||
|
|
||||||
|
// events
|
||||||
|
expect(events.automation.created).not.toBeCalled()
|
||||||
|
expect(events.automation.stepCreated).not.toBeCalled()
|
||||||
|
expect(events.automation.stepDeleted).not.toBeCalled()
|
||||||
|
expect(events.automation.triggerUpdated).toBeCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("adds automation steps", async () => {
|
||||||
|
let automation = newAutomation()
|
||||||
|
automation = await config.createAutomation(automation)
|
||||||
|
automation.definition.steps.push(automationStep())
|
||||||
|
automation.definition.steps.push(automationStep())
|
||||||
|
jest.clearAllMocks()
|
||||||
|
|
||||||
|
// check the post request honours updates with same id
|
||||||
|
await update(automation)
|
||||||
|
|
||||||
|
// events
|
||||||
|
expect(events.automation.stepCreated).toBeCalledTimes(2)
|
||||||
|
expect(events.automation.created).not.toBeCalled()
|
||||||
|
expect(events.automation.stepDeleted).not.toBeCalled()
|
||||||
|
expect(events.automation.triggerUpdated).not.toBeCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
it("removes automation steps", async () => {
|
||||||
|
let automation = newAutomation()
|
||||||
|
automation.definition.steps.push(automationStep())
|
||||||
|
automation = await config.createAutomation(automation)
|
||||||
|
automation.definition.steps = []
|
||||||
|
jest.clearAllMocks()
|
||||||
|
|
||||||
|
// check the post request honours updates with same id
|
||||||
|
await update(automation)
|
||||||
|
|
||||||
|
// events
|
||||||
|
expect(events.automation.stepDeleted).toBeCalledTimes(2)
|
||||||
|
expect(events.automation.stepCreated).not.toBeCalled()
|
||||||
|
expect(events.automation.created).not.toBeCalled()
|
||||||
|
expect(events.automation.triggerUpdated).not.toBeCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("adds and removes automation steps", async () => {
|
||||||
|
let automation = newAutomation()
|
||||||
|
automation = await config.createAutomation(automation)
|
||||||
|
automation.definition.steps = [automationStep(), automationStep()]
|
||||||
|
jest.clearAllMocks()
|
||||||
|
|
||||||
|
// check the post request honours updates with same id
|
||||||
|
await update(automation)
|
||||||
|
|
||||||
|
// events
|
||||||
|
expect(events.automation.stepCreated).toBeCalledTimes(2)
|
||||||
|
expect(events.automation.stepDeleted).toBeCalledTimes(1)
|
||||||
|
expect(events.automation.created).not.toBeCalled()
|
||||||
|
expect(events.automation.triggerUpdated).not.toBeCalled()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -216,7 +328,8 @@ describe("/automations", () => {
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.body.id).toEqual(automation._id)
|
expect(res.body.id).toEqual(automation._id)
|
||||||
|
expect(events.automation.deleted).toBeCalledTimes(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should apply authorization to endpoint", async () => {
|
it("should apply authorization to endpoint", async () => {
|
||||||
|
|
Loading…
Reference in a new issue