Merge branch 'master' into view-calculation-sql-2
This commit is contained in:
commit
d00513db33
|
@ -17,44 +17,65 @@ describe("Branching automations", () => {
|
||||||
afterAll(setup.afterAll)
|
afterAll(setup.afterAll)
|
||||||
|
|
||||||
it("should run a multiple nested branching automation", async () => {
|
it("should run a multiple nested branching automation", async () => {
|
||||||
|
const firstLogId = "11111111-1111-1111-1111-111111111111"
|
||||||
|
const branch1LogId = "22222222-2222-2222-2222-222222222222"
|
||||||
|
const branch2LogId = "33333333-3333-3333-3333-333333333333"
|
||||||
|
const branch2Id = "44444444-4444-4444-4444-444444444444"
|
||||||
|
|
||||||
const builder = createAutomationBuilder({
|
const builder = createAutomationBuilder({
|
||||||
name: "Test Trigger with Loop and Create Row",
|
name: "Test Trigger with Loop and Create Row",
|
||||||
})
|
})
|
||||||
|
|
||||||
const results = await builder
|
const results = await builder
|
||||||
.appAction({ fields: {} })
|
.appAction({ fields: {} })
|
||||||
.serverLog({ text: "Starting automation" })
|
.serverLog(
|
||||||
|
{ text: "Starting automation" },
|
||||||
|
{ stepName: "FirstLog", stepId: firstLogId }
|
||||||
|
)
|
||||||
.branch({
|
.branch({
|
||||||
topLevelBranch1: {
|
topLevelBranch1: {
|
||||||
steps: stepBuilder =>
|
steps: stepBuilder =>
|
||||||
stepBuilder.serverLog({ text: "Branch 1" }).branch({
|
stepBuilder
|
||||||
branch1: {
|
.serverLog(
|
||||||
steps: stepBuilder =>
|
{ text: "Branch 1" },
|
||||||
stepBuilder.serverLog({ text: "Branch 1.1" }),
|
{ stepId: "66666666-6666-6666-6666-666666666666" }
|
||||||
condition: {
|
)
|
||||||
equal: { "{{steps.1.success}}": true },
|
.branch({
|
||||||
|
branch1: {
|
||||||
|
steps: stepBuilder =>
|
||||||
|
stepBuilder.serverLog(
|
||||||
|
{ text: "Branch 1.1" },
|
||||||
|
{ stepId: branch1LogId }
|
||||||
|
),
|
||||||
|
condition: {
|
||||||
|
equal: { [`{{ steps.${firstLogId}.success }}`]: true },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
branch2: {
|
||||||
branch2: {
|
steps: stepBuilder =>
|
||||||
steps: stepBuilder =>
|
stepBuilder.serverLog(
|
||||||
stepBuilder.serverLog({ text: "Branch 1.2" }),
|
{ text: "Branch 1.2" },
|
||||||
condition: {
|
{ stepId: branch2LogId }
|
||||||
equal: { "{{steps.1.success}}": false },
|
),
|
||||||
|
condition: {
|
||||||
|
equal: { [`{{ steps.${firstLogId}.success }}`]: false },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
}),
|
||||||
}),
|
|
||||||
condition: {
|
condition: {
|
||||||
equal: { "{{steps.1.success}}": true },
|
equal: { [`{{ steps.${firstLogId}.success }}`]: true },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
topLevelBranch2: {
|
topLevelBranch2: {
|
||||||
steps: stepBuilder => stepBuilder.serverLog({ text: "Branch 2" }),
|
steps: stepBuilder =>
|
||||||
|
stepBuilder.serverLog({ text: "Branch 2" }, { stepId: branch2Id }),
|
||||||
condition: {
|
condition: {
|
||||||
equal: { "{{steps.1.success}}": false },
|
equal: { [`{{ steps.${firstLogId}.success }}`]: false },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.run()
|
.run()
|
||||||
|
|
||||||
expect(results.steps[3].outputs.status).toContain("branch1 branch taken")
|
expect(results.steps[3].outputs.status).toContain("branch1 branch taken")
|
||||||
expect(results.steps[4].outputs.message).toContain("Branch 1.1")
|
expect(results.steps[4].outputs.message).toContain("Branch 1.1")
|
||||||
})
|
})
|
||||||
|
|
|
@ -64,18 +64,18 @@ class BaseStepBuilder {
|
||||||
stepId: TStep,
|
stepId: TStep,
|
||||||
stepSchema: Omit<AutomationStep, "id" | "stepId" | "inputs">,
|
stepSchema: Omit<AutomationStep, "id" | "stepId" | "inputs">,
|
||||||
inputs: AutomationStepInputs<TStep>,
|
inputs: AutomationStepInputs<TStep>,
|
||||||
stepName?: string
|
opts?: { stepName?: string; stepId?: string }
|
||||||
): this {
|
): this {
|
||||||
const id = uuidv4()
|
const id = opts?.stepId || uuidv4()
|
||||||
this.steps.push({
|
this.steps.push({
|
||||||
...stepSchema,
|
...stepSchema,
|
||||||
inputs: inputs as any,
|
inputs: inputs as any,
|
||||||
id,
|
id,
|
||||||
stepId,
|
stepId,
|
||||||
name: stepName || stepSchema.name,
|
name: opts?.stepName || stepSchema.name,
|
||||||
})
|
})
|
||||||
if (stepName) {
|
if (opts?.stepName) {
|
||||||
this.stepNames[id] = stepName
|
this.stepNames[id] = opts.stepName
|
||||||
}
|
}
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,6 @@ class BaseStepBuilder {
|
||||||
})
|
})
|
||||||
branchStepInputs.children![key] = stepBuilder.build()
|
branchStepInputs.children![key] = stepBuilder.build()
|
||||||
})
|
})
|
||||||
|
|
||||||
const branchStep: AutomationStep = {
|
const branchStep: AutomationStep = {
|
||||||
...definition,
|
...definition,
|
||||||
id: uuidv4(),
|
id: uuidv4(),
|
||||||
|
@ -106,80 +105,98 @@ class BaseStepBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
// STEPS
|
// STEPS
|
||||||
createRow(inputs: CreateRowStepInputs, opts?: { stepName?: string }): this {
|
createRow(
|
||||||
|
inputs: CreateRowStepInputs,
|
||||||
|
opts?: { stepName?: string; stepId?: string }
|
||||||
|
): this {
|
||||||
return this.step(
|
return this.step(
|
||||||
AutomationActionStepId.CREATE_ROW,
|
AutomationActionStepId.CREATE_ROW,
|
||||||
BUILTIN_ACTION_DEFINITIONS.CREATE_ROW,
|
BUILTIN_ACTION_DEFINITIONS.CREATE_ROW,
|
||||||
inputs,
|
inputs,
|
||||||
opts?.stepName
|
opts
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateRow(inputs: UpdateRowStepInputs, opts?: { stepName?: string }): this {
|
updateRow(
|
||||||
|
inputs: UpdateRowStepInputs,
|
||||||
|
opts?: { stepName?: string; stepId?: string }
|
||||||
|
): this {
|
||||||
return this.step(
|
return this.step(
|
||||||
AutomationActionStepId.UPDATE_ROW,
|
AutomationActionStepId.UPDATE_ROW,
|
||||||
BUILTIN_ACTION_DEFINITIONS.UPDATE_ROW,
|
BUILTIN_ACTION_DEFINITIONS.UPDATE_ROW,
|
||||||
inputs,
|
inputs,
|
||||||
opts?.stepName
|
opts
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteRow(inputs: DeleteRowStepInputs, opts?: { stepName?: string }): this {
|
deleteRow(
|
||||||
|
inputs: DeleteRowStepInputs,
|
||||||
|
opts?: { stepName?: string; stepId?: string }
|
||||||
|
): this {
|
||||||
return this.step(
|
return this.step(
|
||||||
AutomationActionStepId.DELETE_ROW,
|
AutomationActionStepId.DELETE_ROW,
|
||||||
BUILTIN_ACTION_DEFINITIONS.DELETE_ROW,
|
BUILTIN_ACTION_DEFINITIONS.DELETE_ROW,
|
||||||
inputs,
|
inputs,
|
||||||
opts?.stepName
|
opts
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
sendSmtpEmail(
|
sendSmtpEmail(
|
||||||
inputs: SmtpEmailStepInputs,
|
inputs: SmtpEmailStepInputs,
|
||||||
opts?: { stepName?: string }
|
opts?: { stepName?: string; stepId?: string }
|
||||||
): this {
|
): this {
|
||||||
return this.step(
|
return this.step(
|
||||||
AutomationActionStepId.SEND_EMAIL_SMTP,
|
AutomationActionStepId.SEND_EMAIL_SMTP,
|
||||||
BUILTIN_ACTION_DEFINITIONS.SEND_EMAIL_SMTP,
|
BUILTIN_ACTION_DEFINITIONS.SEND_EMAIL_SMTP,
|
||||||
inputs,
|
inputs,
|
||||||
opts?.stepName
|
opts
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
executeQuery(
|
executeQuery(
|
||||||
inputs: ExecuteQueryStepInputs,
|
inputs: ExecuteQueryStepInputs,
|
||||||
opts?: { stepName?: string }
|
opts?: { stepName?: string; stepId?: string }
|
||||||
): this {
|
): this {
|
||||||
return this.step(
|
return this.step(
|
||||||
AutomationActionStepId.EXECUTE_QUERY,
|
AutomationActionStepId.EXECUTE_QUERY,
|
||||||
BUILTIN_ACTION_DEFINITIONS.EXECUTE_QUERY,
|
BUILTIN_ACTION_DEFINITIONS.EXECUTE_QUERY,
|
||||||
inputs,
|
inputs,
|
||||||
opts?.stepName
|
opts
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
queryRows(inputs: QueryRowsStepInputs, opts?: { stepName?: string }): this {
|
queryRows(
|
||||||
|
inputs: QueryRowsStepInputs,
|
||||||
|
opts?: { stepName?: string; stepId?: string }
|
||||||
|
): this {
|
||||||
return this.step(
|
return this.step(
|
||||||
AutomationActionStepId.QUERY_ROWS,
|
AutomationActionStepId.QUERY_ROWS,
|
||||||
BUILTIN_ACTION_DEFINITIONS.QUERY_ROWS,
|
BUILTIN_ACTION_DEFINITIONS.QUERY_ROWS,
|
||||||
inputs,
|
inputs,
|
||||||
opts?.stepName
|
opts
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
loop(inputs: LoopStepInputs, opts?: { stepName?: string }): this {
|
loop(
|
||||||
|
inputs: LoopStepInputs,
|
||||||
|
opts?: { stepName?: string; stepId?: string }
|
||||||
|
): this {
|
||||||
return this.step(
|
return this.step(
|
||||||
AutomationActionStepId.LOOP,
|
AutomationActionStepId.LOOP,
|
||||||
BUILTIN_ACTION_DEFINITIONS.LOOP,
|
BUILTIN_ACTION_DEFINITIONS.LOOP,
|
||||||
inputs,
|
inputs,
|
||||||
opts?.stepName
|
opts
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
serverLog(input: ServerLogStepInputs, opts?: { stepName?: string }): this {
|
serverLog(
|
||||||
|
input: ServerLogStepInputs,
|
||||||
|
opts?: { stepName?: string; stepId?: string }
|
||||||
|
): this {
|
||||||
return this.step(
|
return this.step(
|
||||||
AutomationActionStepId.SERVER_LOG,
|
AutomationActionStepId.SERVER_LOG,
|
||||||
BUILTIN_ACTION_DEFINITIONS.SERVER_LOG,
|
BUILTIN_ACTION_DEFINITIONS.SERVER_LOG,
|
||||||
input,
|
input,
|
||||||
opts?.stepName
|
opts
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,8 @@ export interface TriggerOutput {
|
||||||
|
|
||||||
export interface AutomationContext extends AutomationResults {
|
export interface AutomationContext extends AutomationResults {
|
||||||
steps: any[]
|
steps: any[]
|
||||||
stepsByName?: Record<string, any>
|
stepsById: Record<string, any>
|
||||||
|
stepsByName: Record<string, any>
|
||||||
env?: Record<string, string>
|
env?: Record<string, string>
|
||||||
trigger: any
|
trigger: any
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ class Orchestrator {
|
||||||
private job: Job
|
private job: Job
|
||||||
private loopStepOutputs: LoopStep[]
|
private loopStepOutputs: LoopStep[]
|
||||||
private stopped: boolean
|
private stopped: boolean
|
||||||
private executionOutput: AutomationContext
|
private executionOutput: Omit<AutomationContext, "stepsByName" | "stepsById">
|
||||||
|
|
||||||
constructor(job: AutomationJob) {
|
constructor(job: AutomationJob) {
|
||||||
let automation = job.data.automation
|
let automation = job.data.automation
|
||||||
|
@ -91,6 +91,7 @@ class Orchestrator {
|
||||||
// step zero is never used as the template string is zero indexed for customer facing
|
// step zero is never used as the template string is zero indexed for customer facing
|
||||||
this.context = {
|
this.context = {
|
||||||
steps: [{}],
|
steps: [{}],
|
||||||
|
stepsById: {},
|
||||||
stepsByName: {},
|
stepsByName: {},
|
||||||
trigger: triggerOutput,
|
trigger: triggerOutput,
|
||||||
}
|
}
|
||||||
|
@ -457,8 +458,9 @@ class Orchestrator {
|
||||||
inputs: steps[stepToLoopIndex].inputs,
|
inputs: steps[stepToLoopIndex].inputs,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.context.stepsById[steps[stepToLoopIndex].id] = tempOutput
|
||||||
const stepName = steps[stepToLoopIndex].name || steps[stepToLoopIndex].id
|
const stepName = steps[stepToLoopIndex].name || steps[stepToLoopIndex].id
|
||||||
this.context.stepsByName![stepName] = tempOutput
|
this.context.stepsByName[stepName] = tempOutput
|
||||||
this.context.steps[this.context.steps.length] = tempOutput
|
this.context.steps[this.context.steps.length] = tempOutput
|
||||||
this.context.steps = this.context.steps.filter(
|
this.context.steps = this.context.steps.filter(
|
||||||
item => !item.hasOwnProperty.call(item, "currentItem")
|
item => !item.hasOwnProperty.call(item, "currentItem")
|
||||||
|
@ -517,7 +519,10 @@ class Orchestrator {
|
||||||
Object.entries(filter).forEach(([_, value]) => {
|
Object.entries(filter).forEach(([_, value]) => {
|
||||||
Object.entries(value).forEach(([field, _]) => {
|
Object.entries(value).forEach(([field, _]) => {
|
||||||
const updatedField = field.replace("{{", "{{ literal ")
|
const updatedField = field.replace("{{", "{{ literal ")
|
||||||
const fromContext = processStringSync(updatedField, this.context)
|
const fromContext = processStringSync(
|
||||||
|
updatedField,
|
||||||
|
this.processContext(this.context)
|
||||||
|
)
|
||||||
toFilter[field] = fromContext
|
toFilter[field] = fromContext
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -563,9 +568,9 @@ class Orchestrator {
|
||||||
}
|
}
|
||||||
|
|
||||||
const stepFn = await this.getStepFunctionality(step.stepId)
|
const stepFn = await this.getStepFunctionality(step.stepId)
|
||||||
let inputs = await this.addContextAndProcess(
|
let inputs = await processObject(
|
||||||
originalStepInput,
|
originalStepInput,
|
||||||
this.context
|
this.processContext(this.context)
|
||||||
)
|
)
|
||||||
|
|
||||||
inputs = automationUtils.cleanInputValues(inputs, step.schema.inputs)
|
inputs = automationUtils.cleanInputValues(inputs, step.schema.inputs)
|
||||||
|
@ -594,16 +599,16 @@ class Orchestrator {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private async addContextAndProcess(inputs: any, context: any) {
|
private processContext(context: AutomationContext) {
|
||||||
const processContext = {
|
const processContext = {
|
||||||
...context,
|
...context,
|
||||||
steps: {
|
steps: {
|
||||||
...context.steps,
|
...context.steps,
|
||||||
|
...context.stepsById,
|
||||||
...context.stepsByName,
|
...context.stepsByName,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
return processContext
|
||||||
return processObject(inputs, processContext)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleStepOutput(
|
private handleStepOutput(
|
||||||
|
@ -623,6 +628,7 @@ class Orchestrator {
|
||||||
} else {
|
} else {
|
||||||
this.updateExecutionOutput(step.id, step.stepId, step.inputs, outputs)
|
this.updateExecutionOutput(step.id, step.stepId, step.inputs, outputs)
|
||||||
this.context.steps[this.context.steps.length] = outputs
|
this.context.steps[this.context.steps.length] = outputs
|
||||||
|
this.context.stepsById![step.id] = outputs
|
||||||
const stepName = step.name || step.id
|
const stepName = step.name || step.id
|
||||||
this.context.stepsByName![stepName] = outputs
|
this.context.stepsByName![stepName] = outputs
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue