automation events + tests

This commit is contained in:
Rory Powell 2022-04-06 13:54:57 +01:00
parent ec4e9df4c2
commit 266b34aaa0
5 changed files with 200 additions and 43 deletions

View File

@ -125,9 +125,10 @@ exports.Events = {
AUTOMATION_CREATED: "automation:created",
AUTOMATION_DELETED: "automation:deleted",
AUTOMATION_TESTED: "automation:tested",
AUTOMATION_RUN: "automation:run",
// AUTOMATION_RUN: "automation:run",
AUTOMATION_STEP_CREATED: "automation:step:created",
AUTOMATION_STEP_DELETED: "automation:step:deleted",
AUTOMATION_TRIGGER_UPDATED: "automation:trigger:updated",
// LICENSING
LICENSING_QUOTA_EXCEEDED: "licensing:quota:exceeded",

View File

@ -6,32 +6,33 @@ exports.created = () => {
events.processEvent(Events.AUTOMATION_CREATED, properties)
}
// TODO
exports.deleted = () => {
const properties = {}
events.processEvent(Events.AUTOMATION_DELETED, properties)
}
// TODO
exports.tested = () => {
const properties = {}
events.processEvent(Events.AUTOMATION_TESTED, properties)
}
// TODO
exports.run = () => {
const properties = {}
events.processEvent(Events.AUTOMATION_RUN, properties)
}
// exports.run = () => {
// const properties = {}
// events.processEvent(Events.AUTOMATION_RUN, properties)
// }
// TODO
exports.stepCreated = () => {
const properties = {}
events.processEvent(Events.AUTOMATION_STEP_CREATED, properties)
}
// TODO
exports.stepDeleted = () => {
const properties = {}
events.processEvent(Events.AUTOMATION_STEP_DELETED, properties)
}
exports.triggerUpdated = () => {
const properties = {}
events.processEvent(Events.AUTOMATION_TRIGGER_UPDATED, properties)
}

View File

@ -26,6 +26,15 @@ jest.mock("../../../events", () => {
SSOActivated: 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: {
created: jest.fn(),
updated: jest.fn(),

View File

@ -10,6 +10,7 @@ const { deleteEntityMetadata } = require("../../utilities")
const { MetadataTypes } = require("../../constants")
const { setTestFlag, clearTestFlag } = require("../../utilities/redis")
const { getAppDB } = require("@budibase/backend-core/context")
const { events } = require("@budibase/backend-core")
const ACTION_DEFS = removeDeprecated(actions.ACTION_DEFINITIONS)
const TRIGGER_DEFS = removeDeprecated(triggers.TRIGGER_DEFINITIONS)
@ -70,6 +71,10 @@ exports.create = async function (ctx) {
newAuto: 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
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) {
const db = getAppDB()
let automation = ctx.request.body
@ -98,13 +126,14 @@ exports.update = async function (ctx) {
const oldAutoTrigger =
oldAutomation && oldAutomation.definition.trigger
? oldAutomation.definition.trigger
: {}
: undefined
const newAutoTrigger =
automation && automation.definition.trigger
? automation.definition.trigger
: {}
// trigger has been updated, remove the test inputs
if (oldAutoTrigger.id !== newAutoTrigger.id) {
if (oldAutoTrigger && oldAutoTrigger.id !== newAutoTrigger.id) {
events.automation.triggerUpdated()
await deleteEntityMetadata(
ctx.appId,
MetadataTypes.AUTOMATION_TEST_INPUT,
@ -112,6 +141,8 @@ exports.update = async function (ctx) {
)
}
handleStepEvents(oldAutomation, automation)
ctx.status = 200
ctx.body = {
message: `Automation ${automation._id} updated successfully.`,
@ -148,6 +179,7 @@ exports.destroy = async function (ctx) {
// delete metadata first
await cleanupAutomationMetadata(automationId)
ctx.body = await db.remove(automationId, ctx.params.rev)
events.automation.deleted()
}
exports.getActionList = async function (ctx) {
@ -215,4 +247,5 @@ exports.test = async function (ctx) {
})
await clearTestFlag(automation._id)
ctx.body = response
events.automation.tested()
}

View File

@ -10,6 +10,7 @@ const { mocks } = require("@budibase/backend-core/testUtils")
mocks.date.mock()
const MAX_RETRIES = 4
const { TRIGGER_DEFINITIONS, ACTION_DEFINITIONS } = require("../../../automations")
const { events } = require("@budibase/backend-core")
describe("/automations", () => {
let request = setup.getRequest()
@ -58,8 +59,9 @@ describe("/automations", () => {
})
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()
automation.definition.steps = []
const res = await request
.post(`/api/automations`)
@ -71,6 +73,27 @@ describe("/automations", () => {
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).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 () => {
@ -97,8 +120,8 @@ describe("/automations", () => {
})
})
describe("trigger", () => {
it("trigger the automation successfully", async () => {
describe("test", () => {
it("tests the automation successfully", async () => {
let table = await config.createTable()
let automation = newAutomation()
automation.definition.trigger.inputs.tableId = table._id
@ -113,6 +136,7 @@ describe("/automations", () => {
automation = await config.createAutomation(automation)
await setup.delay(500)
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
// 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
@ -134,53 +158,141 @@ describe("/automations", () => {
})
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`)
.set(config.defaultHeaders())
.send(automation)
.expect('Content-Type', /json/)
.expect(200)
}
expect(res.body.message).toEqual(`Automation ${automation._id} updated successfully.`)
expect(res.body.automation.name).toEqual("Updated Name")
})
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
const updateWithPost = async (automation) => {
return request
.post(`/api/automations`)
.set(config.defaultHeaders())
.send(automation)
.expect('Content-Type', /json/)
.expect(200)
}
automation = res.body.automation
expect(automation._id).toBeDefined()
expect(automation._rev).toBeDefined()
it("updates a automations name", async () => {
let automation = newAutomation()
await config.createAutomation(automation)
automation.name = "Updated Name"
jest.clearAllMocks()
// change the trigger
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 res = await update(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 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(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 () => {