diff --git a/packages/server/src/sdk/app/automations/crud.ts b/packages/server/src/sdk/app/automations/crud.ts index 90fe6c16c4..7a2f4c12d0 100644 --- a/packages/server/src/sdk/app/automations/crud.ts +++ b/packages/server/src/sdk/app/automations/crud.ts @@ -11,7 +11,7 @@ import { import { definitions } from "../../../automations/triggerInfo" import automations from "." -interface PersistedAutomation extends Automation { +type PersistedAutomation = Automation & { _id: string _rev: string } @@ -125,6 +125,9 @@ export async function update(automation: Automation) { const db = getDb() const oldAutomation = await db.get(automation._id) + + guardInvalidUpdatesAndThrow(automation, oldAutomation) + automation = cleanAutomationInputs(automation) automation = await checkForWebhooks({ oldAuto: oldAutomation, @@ -251,3 +254,30 @@ async function checkForWebhooks({ oldAuto, newAuto }: any) { } return newAuto } +function guardInvalidUpdatesAndThrow( + automation: Automation, + oldAutomation: Automation +) { + const stepDefinitions = [ + automation.definition.trigger, + ...automation.definition.steps, + ] + const oldStepDefinitions = [ + oldAutomation.definition.trigger, + ...oldAutomation.definition.steps, + ] + for (const step of stepDefinitions) { + const readonlyFields = Object.keys( + step.schema.inputs.properties || {} + ).filter(k => step.schema.inputs.properties[k].readonly) + readonlyFields.forEach(readonlyField => { + const oldStep = oldStepDefinitions.find(i => i.id === step.id) + if (step.inputs[readonlyField] !== oldStep?.inputs[readonlyField]) { + throw new HTTPError( + `Field ${readonlyField} is readonly and it cannot be modified`, + 400 + ) + } + }) + } +} diff --git a/packages/server/src/sdk/app/automations/tests/index.spec.ts b/packages/server/src/sdk/app/automations/tests/index.spec.ts new file mode 100644 index 0000000000..295ab690d1 --- /dev/null +++ b/packages/server/src/sdk/app/automations/tests/index.spec.ts @@ -0,0 +1,54 @@ +import _ from "lodash/fp" +import { Automation } from "@budibase/types" +import automationSdk from "../" +import { structures } from "../../../../api/routes/tests/utilities" +import TestConfiguration from "../../../../tests/utilities/TestConfiguration" + +describe("automation sdk", () => { + const config = new TestConfiguration() + + beforeAll(async () => { + await config.init() + }) + + describe("update", () => { + it("can update input fields", async () => { + await config.doInContext(config.getAppId(), async () => { + const automation: Automation = structures.newAutomation() + const keyToUse = _.sample( + Object.keys(automation.definition.trigger.inputs) + )! + automation.definition.trigger.inputs[keyToUse] = "anyValue" + + const response = await automationSdk.create(automation) + + const update = { ...response } + update.definition.trigger.inputs[keyToUse] = "anyUpdatedValue" + const result = await automationSdk.update(update) + expect(result.definition.trigger.inputs[keyToUse]).toEqual( + "anyUpdatedValue" + ) + }) + }) + + it("cannot update readonly fields", async () => { + await config.doInContext(config.getAppId(), async () => { + const automation: Automation = { ...structures.newAutomation() } + automation.definition.trigger.schema.inputs.properties[ + "readonlyProperty" + ] = { + readonly: true, + } + automation.definition.trigger.inputs["readonlyProperty"] = "anyValue" + + const response = await automationSdk.create(automation) + + const update = { ...response } + update.definition.trigger.inputs["readonlyProperty"] = "anyUpdatedValue" + await expect(automationSdk.update(update)).rejects.toThrow( + "Field readonlyProperty is readonly and it cannot be modified" + ) + }) + }) + }) +})