1
0
Fork 0
mirror of synced 2024-08-15 01:51:33 +12:00

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_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",

View file

@ -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)
}

View file

@ -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(),

View file

@ -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()
} }

View file

@ -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 () => {