From 3eeb7c27b8b1c466be3371446f5cb066b107dd4f Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 10 Sep 2021 14:37:34 +0100 Subject: [PATCH] Introducing the concept of flagging an automation as 'in test' which means it can run with triggers and everything as it normally would in development. --- packages/auth/src/redis/utils.js | 1 + .../server/src/api/controllers/automation.js | 3 +++ .../src/api/routes/tests/automation.spec.js | 7 +++---- .../routes/tests/utilities/TestFunctions.js | 16 ++++++---------- packages/server/src/automations/triggers.js | 13 ++++++++----- packages/server/src/utilities/redis.js | 19 ++++++++++++++++++- 6 files changed, 39 insertions(+), 20 deletions(-) diff --git a/packages/auth/src/redis/utils.js b/packages/auth/src/redis/utils.js index 09b4905298..c6702166ff 100644 --- a/packages/auth/src/redis/utils.js +++ b/packages/auth/src/redis/utils.js @@ -13,6 +13,7 @@ exports.Databases = { DEBOUNCE: "debounce", SESSIONS: "session", USER_CACHE: "users", + FLAGS: "flags", } exports.SEPARATOR = SEPARATOR diff --git a/packages/server/src/api/controllers/automation.js b/packages/server/src/api/controllers/automation.js index 36a7b4970e..b8f6cc7351 100644 --- a/packages/server/src/api/controllers/automation.js +++ b/packages/server/src/api/controllers/automation.js @@ -7,6 +7,7 @@ const { checkForWebhooks, updateTestHistory, } = require("../../automations/utils") +const { setTestFlag, clearTestFlag } = require("../../utilities/redis") /************************* * * @@ -163,6 +164,7 @@ exports.test = async function (ctx) { const appId = ctx.appId const db = new CouchDB(appId) let automation = await db.get(ctx.params.id) + await setTestFlag(automation._id) const response = await triggers.externalTrigger( automation, { @@ -176,5 +178,6 @@ exports.test = async function (ctx) { ...ctx.request.body, occurredAt: new Date().toISOString(), }) + await clearTestFlag(automation._id) ctx.body = response } diff --git a/packages/server/src/api/routes/tests/automation.spec.js b/packages/server/src/api/routes/tests/automation.spec.js index 28a3718f57..1c920848a7 100644 --- a/packages/server/src/api/routes/tests/automation.spec.js +++ b/packages/server/src/api/routes/tests/automation.spec.js @@ -2,7 +2,7 @@ const { checkBuilderEndpoint, getAllTableRows, clearAllAutomations, - triggerAutomation, + testAutomation, } = require("./utilities/TestFunctions") const setup = require("./utilities") const { basicAutomation } = setup.structures @@ -160,14 +160,13 @@ describe("/automations", () => { automation.definition.steps[0].inputs.row.tableId = table._id automation = await config.createAutomation(automation) await setup.delay(500) - const res = await triggerAutomation(config, automation) + const res = await testAutomation(config, automation) // 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 // TODO: update when workflow logs are a thing for (let tries = 0; tries < MAX_RETRIES; tries++) { - expect(res.body.message).toEqual(`Automation ${automation._id} has been triggered.`) - expect(res.body.automation.name).toEqual(automation.name) + expect(res.body).toBeDefined() await setup.delay(500) let elements = await getAllTableRows(config) // don't test it unless there are values to test diff --git a/packages/server/src/api/routes/tests/utilities/TestFunctions.js b/packages/server/src/api/routes/tests/utilities/TestFunctions.js index ed5094679c..87190c3f76 100644 --- a/packages/server/src/api/routes/tests/utilities/TestFunctions.js +++ b/packages/server/src/api/routes/tests/utilities/TestFunctions.js @@ -102,19 +102,15 @@ exports.getDB = config => { 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" }) + .send({ + row: { + name: "Test", + description: "TEST", + }, + }) .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) diff --git a/packages/server/src/automations/triggers.js b/packages/server/src/automations/triggers.js index b32747d319..db4b8304dd 100644 --- a/packages/server/src/automations/triggers.js +++ b/packages/server/src/automations/triggers.js @@ -7,6 +7,7 @@ const { isDevAppID } = require("../db/utils") // need this to call directly, so we can get a response const { processEvent } = require("./utils") const { queue } = require("./bullboard") +const { checkTestFlag } = require("../utilities/redis") const TRIGGER_DEFINITIONS = definitions @@ -14,11 +15,7 @@ async function queueRelevantRowAutomations(event, eventType) { if (event.appId == null) { throw `No appId specified for ${eventType} - check event emitters.` } - // don't queue events which are for dev apps, only way to test automations is - // running tests on them - if (isDevAppID(event.appId)) { - return - } + const db = new CouchDB(event.appId) let automations = await db.allDocs( getAutomationParams(null, { include_docs: true }) @@ -33,6 +30,12 @@ async function queueRelevantRowAutomations(event, eventType) { }) for (let automation of automations) { + // don't queue events which are for dev apps, only way to test automations is + // running tests on them + // in production the test flag will never be checked due to lazy evaluation (first always false) + if (isDevAppID(event.appId) && !(await checkTestFlag(automation._id))) { + return + } let automationDef = automation.definition let automationTrigger = automationDef ? automationDef.trigger : {} if ( diff --git a/packages/server/src/utilities/redis.js b/packages/server/src/utilities/redis.js index 6f1cf46606..151feabdbb 100644 --- a/packages/server/src/utilities/redis.js +++ b/packages/server/src/utilities/redis.js @@ -2,20 +2,24 @@ const { Client, utils } = require("@budibase/auth/redis") const { getGlobalIDFromUserMetadataID } = require("../db/utils") const APP_DEV_LOCK_SECONDS = 600 -let devAppClient, debounceClient +const AUTOMATION_TEST_FLAG_SECONDS = 60 +let devAppClient, debounceClient, flagClient // we init this as we want to keep the connection open all the time // reduces the performance hit exports.init = async () => { devAppClient = new Client(utils.Databases.DEV_LOCKS) debounceClient = new Client(utils.Databases.DEBOUNCE) + flagClient = new Client(utils.Databases.FLAGS) await devAppClient.init() await debounceClient.init() + await flagClient.init() } exports.shutdown = async () => { if (devAppClient) await devAppClient.finish() if (debounceClient) await debounceClient.finish() + if (flagClient) await flagClient.finish() } exports.doesUserHaveLock = async (devAppId, user) => { @@ -67,3 +71,16 @@ exports.checkDebounce = async id => { exports.setDebounce = async (id, seconds) => { await debounceClient.store(id, "debouncing", seconds) } + +exports.setTestFlag = async id => { + await flagClient.store(id, { testing: true }, AUTOMATION_TEST_FLAG_SECONDS) +} + +exports.checkTestFlag = async id => { + const flag = await flagClient.get(id) + return !!(flag && flag.testing) +} + +exports.clearTestFlag = async id => { + await devAppClient.delete(id) +}