add validators and tests for automation branching
This commit is contained in:
parent
56641e06c3
commit
839292b84d
|
@ -25,6 +25,8 @@ let {
|
|||
collectAutomation,
|
||||
filterAutomation,
|
||||
updateRowAutomationWithFilters,
|
||||
branchAutomationIncorrectPosition,
|
||||
branchAutomation,
|
||||
} = setup.structures
|
||||
|
||||
describe("/automations", () => {
|
||||
|
@ -121,6 +123,78 @@ describe("/automations", () => {
|
|||
expect(events.automation.stepCreated).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it("Should ensure you can't have a branch as not a last step", async () => {
|
||||
const automation = branchAutomationIncorrectPosition()
|
||||
const res = await request
|
||||
.post(`/api/automations`)
|
||||
.set(config.defaultHeaders())
|
||||
.send(automation)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(400)
|
||||
|
||||
expect(res.body.message).toContain("must contain at least 1 items")
|
||||
})
|
||||
|
||||
it("Should check validation on an automation that has a branch step with no children", async () => {
|
||||
const automation = branchAutomationIncorrectPosition()
|
||||
automation.definition.steps[0].inputs.branches = [
|
||||
{ name: "test", condition: { equal: { "steps.1.success": "true" } } },
|
||||
]
|
||||
automation.definition.steps[0].inputs.children = {}
|
||||
|
||||
const res = await request
|
||||
.post(`/api/automations`)
|
||||
.set(config.defaultHeaders())
|
||||
.send(automation)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(400)
|
||||
|
||||
expect(res.body.message).toContain(
|
||||
"Branch steps are only allowed as the last step"
|
||||
)
|
||||
})
|
||||
|
||||
it("Should check validation on a branch step with empty conditions", async () => {
|
||||
const automation = branchAutomation()
|
||||
|
||||
automation.definition.steps[1].inputs.branches = [
|
||||
{ name: "test", condition: {} },
|
||||
]
|
||||
automation.definition.steps[1].inputs.children = {}
|
||||
|
||||
const res = await request
|
||||
.post(`/api/automations`)
|
||||
.set(config.defaultHeaders())
|
||||
.send(automation)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(400)
|
||||
|
||||
expect(res.body.message).toContain("must have at least 1 key")
|
||||
})
|
||||
|
||||
it("Should check validation on an branch that has a condition that is not valid", async () => {
|
||||
const automation = branchAutomation()
|
||||
|
||||
automation.definition.steps[1].inputs.branches = [
|
||||
{
|
||||
name: "test",
|
||||
condition: {
|
||||
INCORRECT: { "steps.1.success": true },
|
||||
},
|
||||
},
|
||||
]
|
||||
automation.definition.steps[1].inputs.children = {}
|
||||
|
||||
const res = await request
|
||||
.post(`/api/automations`)
|
||||
.set(config.defaultHeaders())
|
||||
.send(automation)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(400)
|
||||
|
||||
expect(res.body.message).toContain('INCORRECT" is not allowed')
|
||||
})
|
||||
|
||||
it("should apply authorization to endpoint", async () => {
|
||||
const automation = newAutomation()
|
||||
await checkBuilderEndpoint({
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import { auth, permissions } from "@budibase/backend-core"
|
||||
import { DataSourceOperation } from "../../../constants"
|
||||
import {
|
||||
AutomationActionStepId,
|
||||
AutomationStep,
|
||||
AutomationStepType,
|
||||
EmptyFilterOption,
|
||||
SearchFilters,
|
||||
Table,
|
||||
|
@ -88,7 +91,7 @@ export function datasourceValidator() {
|
|||
)
|
||||
}
|
||||
|
||||
function filterObject() {
|
||||
function filterObject(unknown = true) {
|
||||
const conditionalFilteringObject = () =>
|
||||
Joi.object({
|
||||
conditions: Joi.array().items(Joi.link("#schema")).required(),
|
||||
|
@ -115,7 +118,7 @@ function filterObject() {
|
|||
fuzzyOr: Joi.forbidden(),
|
||||
documentType: Joi.forbidden(),
|
||||
}
|
||||
return Joi.object(filtersValidators).unknown(true).id("schema")
|
||||
return Joi.object(filtersValidators).unknown(unknown).id("schema")
|
||||
}
|
||||
|
||||
export function internalSearchValidator() {
|
||||
|
@ -259,6 +262,11 @@ export function screenValidator() {
|
|||
}
|
||||
|
||||
function generateStepSchema(allowStepTypes: string[]) {
|
||||
const branchSchema = Joi.object({
|
||||
name: Joi.string().required(),
|
||||
condition: filterObject(false).required().min(1),
|
||||
})
|
||||
|
||||
return Joi.object({
|
||||
stepId: Joi.string().required(),
|
||||
id: Joi.string().required(),
|
||||
|
@ -267,6 +275,17 @@ function generateStepSchema(allowStepTypes: string[]) {
|
|||
tagline: Joi.string().required(),
|
||||
icon: Joi.string().required(),
|
||||
params: Joi.object(),
|
||||
inputs: Joi.when("stepId", {
|
||||
is: AutomationActionStepId.BRANCH,
|
||||
then: Joi.object({
|
||||
branches: Joi.array().items(branchSchema).min(1).required(),
|
||||
children: Joi.object()
|
||||
.pattern(Joi.string(), Joi.array().items(Joi.link("#step")))
|
||||
.required(),
|
||||
}).required(),
|
||||
otherwise: Joi.object(),
|
||||
}),
|
||||
|
||||
args: Joi.object(),
|
||||
type: Joi.string()
|
||||
.required()
|
||||
|
@ -274,6 +293,17 @@ function generateStepSchema(allowStepTypes: string[]) {
|
|||
}).unknown(true)
|
||||
}
|
||||
|
||||
const validateStepsArray = (
|
||||
steps: AutomationStep[],
|
||||
helpers: Joi.CustomHelpers
|
||||
) => {
|
||||
for (let i = 0; i < steps.length - 1; i++) {
|
||||
if (steps[i].stepId === AutomationActionStepId.BRANCH) {
|
||||
return helpers.error("branchStepPosition")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function automationValidator(existing = false) {
|
||||
return auth.joiValidator.body(
|
||||
Joi.object({
|
||||
|
@ -284,9 +314,20 @@ export function automationValidator(existing = false) {
|
|||
definition: Joi.object({
|
||||
steps: Joi.array()
|
||||
.required()
|
||||
.items(generateStepSchema(["ACTION", "LOGIC"])),
|
||||
trigger: generateStepSchema(["TRIGGER"]).allow(null),
|
||||
.items(
|
||||
generateStepSchema([
|
||||
AutomationStepType.ACTION,
|
||||
AutomationStepType.LOGIC,
|
||||
])
|
||||
)
|
||||
.custom(validateStepsArray)
|
||||
.messages({
|
||||
branchStepPosition:
|
||||
"Branch steps are only allowed as the last step",
|
||||
}),
|
||||
trigger: generateStepSchema([AutomationStepType.TRIGGER]).allow(null),
|
||||
})
|
||||
|
||||
.required()
|
||||
.unknown(true),
|
||||
}).unknown(true)
|
||||
|
|
|
@ -292,6 +292,132 @@ export function serverLogAutomation(appId?: string): Automation {
|
|||
}
|
||||
}
|
||||
|
||||
export function branchAutomationIncorrectPosition(appId?: string): Automation {
|
||||
return {
|
||||
name: "My Automation",
|
||||
screenId: "kasdkfldsafkl",
|
||||
live: true,
|
||||
uiTree: {},
|
||||
definition: {
|
||||
trigger: {
|
||||
stepId: AutomationTriggerStepId.APP,
|
||||
name: "test",
|
||||
tagline: "test",
|
||||
icon: "test",
|
||||
description: "test",
|
||||
type: AutomationStepType.TRIGGER,
|
||||
id: "test",
|
||||
inputs: { fields: {} },
|
||||
schema: {
|
||||
inputs: {
|
||||
properties: {},
|
||||
},
|
||||
outputs: {
|
||||
properties: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
steps: [
|
||||
{
|
||||
stepId: AutomationActionStepId.BRANCH,
|
||||
name: "Branch",
|
||||
tagline: "Console log a value in the backend",
|
||||
icon: "Monitoring",
|
||||
description: "Logs the given text to the server (using console.log)",
|
||||
inputs: {
|
||||
branches: [],
|
||||
},
|
||||
schema: { inputs: { properties: {} }, outputs: { properties: {} } },
|
||||
id: "y8lkZbeSe",
|
||||
type: AutomationStepType.LOGIC,
|
||||
},
|
||||
{
|
||||
stepId: AutomationActionStepId.SERVER_LOG,
|
||||
name: "Backend log",
|
||||
tagline: "Console log a value in the backend",
|
||||
icon: "Monitoring",
|
||||
description: "Logs the given text to the server (using console.log)",
|
||||
internal: true,
|
||||
features: {
|
||||
LOOPING: true,
|
||||
},
|
||||
inputs: {
|
||||
text: "log statement",
|
||||
},
|
||||
schema: BUILTIN_ACTION_DEFINITIONS.SERVER_LOG.schema,
|
||||
id: "y8lkZbeSe",
|
||||
type: AutomationStepType.ACTION,
|
||||
},
|
||||
],
|
||||
},
|
||||
type: "automation",
|
||||
appId: appId!,
|
||||
}
|
||||
}
|
||||
|
||||
export function branchAutomation(appId?: string): Automation {
|
||||
return {
|
||||
name: "My Automation",
|
||||
screenId: "kasdkfldsafkl",
|
||||
live: true,
|
||||
uiTree: {},
|
||||
definition: {
|
||||
trigger: {
|
||||
stepId: AutomationTriggerStepId.APP,
|
||||
name: "test",
|
||||
tagline: "test",
|
||||
icon: "test",
|
||||
description: "test",
|
||||
type: AutomationStepType.TRIGGER,
|
||||
id: "test",
|
||||
inputs: { fields: {} },
|
||||
schema: {
|
||||
inputs: {
|
||||
properties: {},
|
||||
},
|
||||
outputs: {
|
||||
properties: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
steps: [
|
||||
{
|
||||
stepId: AutomationActionStepId.SERVER_LOG,
|
||||
name: "Backend log",
|
||||
tagline: "Console log a value in the backend",
|
||||
icon: "Monitoring",
|
||||
description: "Logs the given text to the server (using console.log)",
|
||||
internal: true,
|
||||
features: {
|
||||
LOOPING: true,
|
||||
},
|
||||
inputs: {
|
||||
text: "log statement",
|
||||
},
|
||||
schema: BUILTIN_ACTION_DEFINITIONS.SERVER_LOG.schema,
|
||||
id: "y8lkZbeSe",
|
||||
type: AutomationStepType.ACTION,
|
||||
},
|
||||
{
|
||||
stepId: AutomationActionStepId.BRANCH,
|
||||
name: "Branch",
|
||||
tagline: "Console log a value in the backend",
|
||||
icon: "Monitoring",
|
||||
description: "Logs the given text to the server (using console.log)",
|
||||
inputs: {
|
||||
branches: [],
|
||||
},
|
||||
schema: { inputs: { properties: {} }, outputs: { properties: {} } },
|
||||
id: "y8lkZbeSe",
|
||||
type: AutomationStepType.LOGIC,
|
||||
},
|
||||
],
|
||||
},
|
||||
type: "automation",
|
||||
appId: appId!,
|
||||
}
|
||||
}
|
||||
|
||||
export function loopAutomation(
|
||||
tableId: string,
|
||||
loopOpts?: LoopInput
|
||||
|
|
Loading…
Reference in New Issue