Adding some basic test cases for the metadata API, testing that automation tests do store history.
This commit is contained in:
parent
da9d78d1fb
commit
84cdec5907
|
@ -3,9 +3,10 @@ const actions = require("../../automations/actions")
|
||||||
const logic = require("../../automations/logic")
|
const logic = require("../../automations/logic")
|
||||||
const triggers = require("../../automations/triggers")
|
const triggers = require("../../automations/triggers")
|
||||||
const { getAutomationParams, generateAutomationID } = require("../../db/utils")
|
const { getAutomationParams, generateAutomationID } = require("../../db/utils")
|
||||||
const { saveEntityMetadata } = require("../../utilities")
|
const {
|
||||||
const { MetadataTypes } = require("../../constants")
|
checkForWebhooks,
|
||||||
const { checkForWebhooks } = require("../../automations/utils")
|
updateTestHistory,
|
||||||
|
} = require("../../automations/utils")
|
||||||
|
|
||||||
/*************************
|
/*************************
|
||||||
* *
|
* *
|
||||||
|
@ -171,11 +172,9 @@ exports.test = async function (ctx) {
|
||||||
{ getResponses: true }
|
{ getResponses: true }
|
||||||
)
|
)
|
||||||
// save a test history run
|
// save a test history run
|
||||||
await saveEntityMetadata(
|
await updateTestHistory(ctx.appId, automation, {
|
||||||
ctx.appId,
|
...ctx.request.body,
|
||||||
MetadataTypes.AUTOMATION_TEST_HISTORY,
|
occurredAt: new Date().toISOString(),
|
||||||
automation._id,
|
})
|
||||||
ctx.request.body
|
|
||||||
)
|
|
||||||
ctx.body = response
|
ctx.body = response
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,12 @@ exports.saveMetadata = async ctx => {
|
||||||
if (type === MetadataTypes.AUTOMATION_TEST_HISTORY) {
|
if (type === MetadataTypes.AUTOMATION_TEST_HISTORY) {
|
||||||
ctx.throw(400, "Cannot save automation history type")
|
ctx.throw(400, "Cannot save automation history type")
|
||||||
}
|
}
|
||||||
await saveEntityMetadata(ctx.appId, type, entityId, ctx.request.body)
|
ctx.body = await saveEntityMetadata(
|
||||||
|
ctx.appId,
|
||||||
|
type,
|
||||||
|
entityId,
|
||||||
|
ctx.request.body
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.deleteMetadata = async ctx => {
|
exports.deleteMetadata = async ctx => {
|
||||||
|
@ -34,7 +39,7 @@ exports.deleteMetadata = async ctx => {
|
||||||
await db.remove(id, rev)
|
await db.remove(id, rev)
|
||||||
}
|
}
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
message: "Metadata deleted successfully.",
|
message: "Metadata deleted successfully",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,5 +47,13 @@ exports.getMetadata = async ctx => {
|
||||||
const { type, entityId } = ctx.params
|
const { type, entityId } = ctx.params
|
||||||
const db = new CouchDB(ctx.appId)
|
const db = new CouchDB(ctx.appId)
|
||||||
const id = generateMetadataID(type, entityId)
|
const id = generateMetadataID(type, entityId)
|
||||||
ctx.body = await db.get(id)
|
try {
|
||||||
|
ctx.body = await db.get(id)
|
||||||
|
} catch (err) {
|
||||||
|
if (err.status === 404) {
|
||||||
|
ctx.body = {}
|
||||||
|
} else {
|
||||||
|
ctx.throw(err.status, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ const datasourceRoutes = require("./datasource")
|
||||||
const queryRoutes = require("./query")
|
const queryRoutes = require("./query")
|
||||||
const hostingRoutes = require("./hosting")
|
const hostingRoutes = require("./hosting")
|
||||||
const backupRoutes = require("./backup")
|
const backupRoutes = require("./backup")
|
||||||
|
const metadataRoutes = require("./metadata")
|
||||||
const devRoutes = require("./dev")
|
const devRoutes = require("./dev")
|
||||||
|
|
||||||
exports.mainRoutes = [
|
exports.mainRoutes = [
|
||||||
|
@ -46,6 +47,7 @@ exports.mainRoutes = [
|
||||||
queryRoutes,
|
queryRoutes,
|
||||||
hostingRoutes,
|
hostingRoutes,
|
||||||
backupRoutes,
|
backupRoutes,
|
||||||
|
metadataRoutes,
|
||||||
devRoutes,
|
devRoutes,
|
||||||
// these need to be handled last as they still use /api/:tableId
|
// these need to be handled last as they still use /api/:tableId
|
||||||
// this could be breaking as koa may recognise other routes as this
|
// this could be breaking as koa may recognise other routes as this
|
||||||
|
|
|
@ -4,27 +4,33 @@ const {
|
||||||
middleware: appInfoMiddleware,
|
middleware: appInfoMiddleware,
|
||||||
AppType,
|
AppType,
|
||||||
} = require("../../middleware/appInfo")
|
} = require("../../middleware/appInfo")
|
||||||
|
const authorized = require("../../middleware/authorized")
|
||||||
|
const { BUILDER } = require("@budibase/auth/permissions")
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
router
|
router
|
||||||
.post(
|
.post(
|
||||||
"/api/metadata/:type/:entityId",
|
"/api/metadata/:type/:entityId",
|
||||||
|
authorized(BUILDER),
|
||||||
appInfoMiddleware({ appType: AppType.DEV }),
|
appInfoMiddleware({ appType: AppType.DEV }),
|
||||||
controller.saveMetadata
|
controller.saveMetadata
|
||||||
)
|
)
|
||||||
.delete(
|
.delete(
|
||||||
"/api/metadata/:type/:entityId",
|
"/api/metadata/:type/:entityId",
|
||||||
|
authorized(BUILDER),
|
||||||
appInfoMiddleware({ appType: AppType.DEV }),
|
appInfoMiddleware({ appType: AppType.DEV }),
|
||||||
controller.deleteMetadata
|
controller.deleteMetadata
|
||||||
)
|
)
|
||||||
.get(
|
.get(
|
||||||
"/api/metadata/type",
|
"/api/metadata/type",
|
||||||
|
authorized(BUILDER),
|
||||||
appInfoMiddleware({ appType: AppType.DEV }),
|
appInfoMiddleware({ appType: AppType.DEV }),
|
||||||
controller.getTypes
|
controller.getTypes
|
||||||
)
|
)
|
||||||
.get(
|
.get(
|
||||||
"/api/metadata/:type/:entityId",
|
"/api/metadata/:type/:entityId",
|
||||||
|
authorized(BUILDER),
|
||||||
appInfoMiddleware({ appType: AppType.DEV }),
|
appInfoMiddleware({ appType: AppType.DEV }),
|
||||||
controller.getMetadata
|
controller.getMetadata
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,6 +2,7 @@ const {
|
||||||
checkBuilderEndpoint,
|
checkBuilderEndpoint,
|
||||||
getAllTableRows,
|
getAllTableRows,
|
||||||
clearAllAutomations,
|
clearAllAutomations,
|
||||||
|
triggerAutomation,
|
||||||
} = require("./utilities/TestFunctions")
|
} = require("./utilities/TestFunctions")
|
||||||
const setup = require("./utilities")
|
const setup = require("./utilities")
|
||||||
const { basicAutomation } = setup.structures
|
const { basicAutomation } = setup.structures
|
||||||
|
@ -23,15 +24,6 @@ describe("/automations", () => {
|
||||||
await config.init()
|
await config.init()
|
||||||
})
|
})
|
||||||
|
|
||||||
const triggerWorkflow = async automation => {
|
|
||||||
return await request
|
|
||||||
.post(`/api/automations/${automation._id}/trigger`)
|
|
||||||
.send({ name: "Test", description: "TEST" })
|
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.expect('Content-Type', /json/)
|
|
||||||
.expect(200)
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("get definitions", () => {
|
describe("get definitions", () => {
|
||||||
it("returns a list of definitions for actions", async () => {
|
it("returns a list of definitions for actions", async () => {
|
||||||
const res = await request
|
const res = await request
|
||||||
|
@ -168,7 +160,7 @@ describe("/automations", () => {
|
||||||
automation.definition.steps[0].inputs.row.tableId = table._id
|
automation.definition.steps[0].inputs.row.tableId = table._id
|
||||||
automation = await config.createAutomation(automation)
|
automation = await config.createAutomation(automation)
|
||||||
await setup.delay(500)
|
await setup.delay(500)
|
||||||
const res = await triggerWorkflow(automation)
|
const res = await triggerAutomation(config, automation)
|
||||||
// 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
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
const { testAutomation } = require("./utilities/TestFunctions")
|
||||||
|
const setup = require("./utilities")
|
||||||
|
const { MetadataTypes } = require("../../../constants")
|
||||||
|
|
||||||
|
describe("/metadata", () => {
|
||||||
|
let request = setup.getRequest()
|
||||||
|
let config = setup.getConfig()
|
||||||
|
let automation
|
||||||
|
|
||||||
|
afterAll(setup.afterAll)
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await config.init()
|
||||||
|
automation = await config.createAutomation()
|
||||||
|
})
|
||||||
|
|
||||||
|
async function createMetadata(data, type = MetadataTypes.AUTOMATION_TEST_INPUT) {
|
||||||
|
const res = await request
|
||||||
|
.post(`/api/metadata/${type}/${automation._id}`)
|
||||||
|
.send(data)
|
||||||
|
.set(config.defaultHeaders())
|
||||||
|
.expect("Content-Type", /json/)
|
||||||
|
.expect(200)
|
||||||
|
expect(res.body._rev).toBeDefined()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getMetadata(type) {
|
||||||
|
const res = await request
|
||||||
|
.get(`/api/metadata/${type}/${automation._id}`)
|
||||||
|
.set(config.defaultHeaders())
|
||||||
|
.expect("Content-Type", /json/)
|
||||||
|
.expect(200)
|
||||||
|
return res.body
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("save", () => {
|
||||||
|
it("should be able to save some metadata", async () => {
|
||||||
|
await createMetadata({ test: "a" })
|
||||||
|
const testInput = await getMetadata(MetadataTypes.AUTOMATION_TEST_INPUT)
|
||||||
|
expect(testInput.test).toBe("a")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should save history metadata on automation run", async () => {
|
||||||
|
// this should have created some history
|
||||||
|
await testAutomation(config, automation)
|
||||||
|
const metadata = await getMetadata(MetadataTypes.AUTOMATION_TEST_HISTORY)
|
||||||
|
expect(metadata).toBeDefined()
|
||||||
|
expect(metadata.history.length).toBe(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("destroy", () => {
|
||||||
|
it("should be able to delete some test inputs", async () => {
|
||||||
|
const res = await request
|
||||||
|
.delete(`/api/metadata/${MetadataTypes.AUTOMATION_TEST_INPUT}/${automation._id}`)
|
||||||
|
.set(config.defaultHeaders())
|
||||||
|
.expect("Content-Type", /json/)
|
||||||
|
.expect(200)
|
||||||
|
expect(res.body.message).toBeDefined()
|
||||||
|
const metadata = await getMetadata(MetadataTypes.AUTOMATION_TEST_INPUT)
|
||||||
|
expect(metadata.test).toBeUndefined()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -101,3 +101,21 @@ exports.checkPermissionsEndpoint = async ({
|
||||||
exports.getDB = config => {
|
exports.getDB = config => {
|
||||||
return new CouchDB(config.getAppId())
|
return new CouchDB(config.getAppId())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.triggerAutomation = async (config, automation) => {
|
||||||
|
return await config.request
|
||||||
|
.post(`/api/automations/${automation._id}/trigger`)
|
||||||
|
.send({ name: "Test", description: "TEST" })
|
||||||
|
.set(config.defaultHeaders())
|
||||||
|
.expect("Content-Type", /json/)
|
||||||
|
.expect(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.testAutomation = async (config, automation) => {
|
||||||
|
return await config.request
|
||||||
|
.post(`/api/automations/${automation._id}/test`)
|
||||||
|
.send({ name: "Test", description: "TEST" })
|
||||||
|
.set(config.defaultHeaders())
|
||||||
|
.expect("Content-Type", /json/)
|
||||||
|
.expect(200)
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@ const webhooks = require("../api/controllers/webhook")
|
||||||
const CouchDB = require("../db")
|
const CouchDB = require("../db")
|
||||||
const { queue } = require("./bullboard")
|
const { queue } = require("./bullboard")
|
||||||
const newid = require("../db/newid")
|
const newid = require("../db/newid")
|
||||||
|
const { updateEntityMetadata } = require("../utilities")
|
||||||
|
const { MetadataTypes } = require("../constants")
|
||||||
|
|
||||||
const WH_STEP_ID = definitions.WEBHOOK.stepId
|
const WH_STEP_ID = definitions.WEBHOOK.stepId
|
||||||
const CRON_STEP_ID = definitions.CRON.stepId
|
const CRON_STEP_ID = definitions.CRON.stepId
|
||||||
|
@ -63,6 +65,24 @@ exports.processEvent = async job => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.updateTestHistory = async (appId, automation, history) => {
|
||||||
|
return updateEntityMetadata(
|
||||||
|
appId,
|
||||||
|
MetadataTypes.AUTOMATION_TEST_HISTORY,
|
||||||
|
automation._id,
|
||||||
|
metadata => {
|
||||||
|
if (metadata && Array.isArray(metadata.history)) {
|
||||||
|
metadata.history.push(history)
|
||||||
|
} else {
|
||||||
|
metadata = {
|
||||||
|
history: [history],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return metadata
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// end the repetition and the job itself
|
// end the repetition and the job itself
|
||||||
exports.disableAllCrons = async appId => {
|
exports.disableAllCrons = async appId => {
|
||||||
const promises = []
|
const promises = []
|
||||||
|
|
|
@ -58,16 +58,19 @@ exports.attachmentsRelativeURL = attachmentKey => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.saveEntityMetadata = async (appId, type, entityId, metadata) => {
|
exports.updateEntityMetadata = async (appId, type, entityId, updateFn) => {
|
||||||
const db = new CouchDB(appId)
|
const db = new CouchDB(appId)
|
||||||
const id = generateMetadataID(type, entityId)
|
const id = generateMetadataID(type, entityId)
|
||||||
// read it to see if it exists, we'll overwrite it no matter what
|
// read it to see if it exists, we'll overwrite it no matter what
|
||||||
let rev
|
let rev,
|
||||||
|
metadata = {}
|
||||||
try {
|
try {
|
||||||
const oldMetadata = await db.get(id)
|
const oldMetadata = await db.get(id)
|
||||||
rev = oldMetadata._rev
|
rev = oldMetadata._rev
|
||||||
|
metadata = updateFn(oldMetadata)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
rev = null
|
rev = null
|
||||||
|
metadata = updateFn({})
|
||||||
}
|
}
|
||||||
metadata._id = id
|
metadata._id = id
|
||||||
if (rev) {
|
if (rev) {
|
||||||
|
@ -80,3 +83,9 @@ exports.saveEntityMetadata = async (appId, type, entityId, metadata) => {
|
||||||
_rev: response.rev,
|
_rev: response.rev,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.saveEntityMetadata = async (appId, type, entityId, metadata) => {
|
||||||
|
return exports.updateEntityMetadata(appId, type, entityId, () => {
|
||||||
|
return metadata
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue