Merge branch 'master' into fix-yarn-manifest
This commit is contained in:
commit
cff55783e6
|
@ -46,6 +46,7 @@ async function init() {
|
||||||
HTTP_LOGGING: "0",
|
HTTP_LOGGING: "0",
|
||||||
VERSION: "0.0.0+local",
|
VERSION: "0.0.0+local",
|
||||||
PASSWORD_MIN_LENGTH: "1",
|
PASSWORD_MIN_LENGTH: "1",
|
||||||
|
OPENAI_API_KEY: "sk-abcdefghijklmnopqrstuvwxyz1234567890abcd",
|
||||||
}
|
}
|
||||||
|
|
||||||
config = { ...config, ...existingConfig }
|
config = { ...config, ...existingConfig }
|
||||||
|
|
|
@ -1,100 +0,0 @@
|
||||||
jest.mock("../../threads/automation")
|
|
||||||
jest.mock("../../utilities/redis", () => ({
|
|
||||||
init: jest.fn(),
|
|
||||||
checkTestFlag: () => {
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
shutdown: jest.fn(),
|
|
||||||
}))
|
|
||||||
|
|
||||||
jest.spyOn(global.console, "error")
|
|
||||||
|
|
||||||
import "../../environment"
|
|
||||||
import * as automation from "../index"
|
|
||||||
import * as thread from "../../threads/automation"
|
|
||||||
import * as triggers from "../triggers"
|
|
||||||
import { basicAutomation } from "../../tests/utilities/structures"
|
|
||||||
import { wait } from "../../utilities"
|
|
||||||
import { makePartial } from "../../tests/utilities"
|
|
||||||
import { cleanInputValues } from "../automationUtils"
|
|
||||||
import { Automation } from "@budibase/types"
|
|
||||||
import TestConfiguration from "../../tests/utilities/TestConfiguration"
|
|
||||||
|
|
||||||
describe("Run through some parts of the automations system", () => {
|
|
||||||
const config = new TestConfiguration()
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
await automation.init()
|
|
||||||
await config.init()
|
|
||||||
})
|
|
||||||
|
|
||||||
afterAll(async () => {
|
|
||||||
await automation.shutdown()
|
|
||||||
config.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should be able to init in builder", async () => {
|
|
||||||
const automation: Automation = {
|
|
||||||
...basicAutomation(),
|
|
||||||
appId: config.appId!,
|
|
||||||
}
|
|
||||||
const fields: any = { a: 1, appId: config.appId }
|
|
||||||
await triggers.externalTrigger(automation, fields)
|
|
||||||
await wait(100)
|
|
||||||
expect(thread.execute).toHaveBeenCalled()
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should check coercion", async () => {
|
|
||||||
const table = await config.createTable()
|
|
||||||
const automation: any = basicAutomation()
|
|
||||||
automation.definition.trigger.inputs.tableId = table._id
|
|
||||||
automation.definition.trigger.stepId = "APP"
|
|
||||||
automation.definition.trigger.inputs.fields = { a: "number" }
|
|
||||||
const fields: any = {
|
|
||||||
appId: config.getAppId(),
|
|
||||||
fields: {
|
|
||||||
a: "1",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
await triggers.externalTrigger(automation, fields)
|
|
||||||
await wait(100)
|
|
||||||
expect(thread.execute).toHaveBeenCalledWith(
|
|
||||||
makePartial({
|
|
||||||
data: {
|
|
||||||
event: {
|
|
||||||
fields: {
|
|
||||||
a: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
expect.any(Function)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should be able to clean inputs with the utilities", () => {
|
|
||||||
// can't clean without a schema
|
|
||||||
let output = cleanInputValues({ a: "1" })
|
|
||||||
expect(output.a).toBe("1")
|
|
||||||
output = cleanInputValues(
|
|
||||||
{ a: "1", b: "true", c: "false", d: 1, e: "help" },
|
|
||||||
{
|
|
||||||
properties: {
|
|
||||||
a: {
|
|
||||||
type: "number",
|
|
||||||
},
|
|
||||||
b: {
|
|
||||||
type: "boolean",
|
|
||||||
},
|
|
||||||
c: {
|
|
||||||
type: "boolean",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
expect(output.a).toBe(1)
|
|
||||||
expect(output.b).toBe(true)
|
|
||||||
expect(output.c).toBe(false)
|
|
||||||
expect(output.d).toBe(1)
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -119,5 +119,31 @@ describe("automationUtils", () => {
|
||||||
schema,
|
schema,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should be able to clean inputs with the utilities", () => {
|
||||||
|
// can't clean without a schema
|
||||||
|
let output = cleanInputValues({ a: "1" })
|
||||||
|
expect(output.a).toBe("1")
|
||||||
|
output = cleanInputValues(
|
||||||
|
{ a: "1", b: "true", c: "false", d: 1, e: "help" },
|
||||||
|
{
|
||||||
|
properties: {
|
||||||
|
a: {
|
||||||
|
type: "number",
|
||||||
|
},
|
||||||
|
b: {
|
||||||
|
type: "boolean",
|
||||||
|
},
|
||||||
|
c: {
|
||||||
|
type: "boolean",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
expect(output.a).toBe(1)
|
||||||
|
expect(output.b).toBe(true)
|
||||||
|
expect(output.c).toBe(false)
|
||||||
|
expect(output.d).toBe(1)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
|
@ -1,17 +1,13 @@
|
||||||
import * as automation from "../../index"
|
import * as automation from "../../index"
|
||||||
import * as triggers from "../../triggers"
|
import { basicTable } from "../../../tests/utilities/structures"
|
||||||
import { basicTable, loopAutomation } from "../../../tests/utilities/structures"
|
|
||||||
import { context } from "@budibase/backend-core"
|
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
LoopStepType,
|
LoopStepType,
|
||||||
AutomationResults,
|
|
||||||
ServerLogStepOutputs,
|
ServerLogStepOutputs,
|
||||||
CreateRowStepOutputs,
|
CreateRowStepOutputs,
|
||||||
FieldType,
|
FieldType,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import * as loopUtils from "../../loopUtils"
|
import * as loopUtils from "../../loopUtils"
|
||||||
import { LoopInput } from "../../../definitions/automations"
|
|
||||||
import { createAutomationBuilder } from "../utilities/AutomationTestBuilder"
|
import { createAutomationBuilder } from "../utilities/AutomationTestBuilder"
|
||||||
import TestConfiguration from "../../../tests/utilities/TestConfiguration"
|
import TestConfiguration from "../../../tests/utilities/TestConfiguration"
|
||||||
|
|
||||||
|
@ -34,41 +30,46 @@ describe("Attempt to run a basic loop automation", () => {
|
||||||
config.end()
|
config.end()
|
||||||
})
|
})
|
||||||
|
|
||||||
async function runLoop(loopOpts?: LoopInput): Promise<AutomationResults> {
|
|
||||||
const appId = config.getAppId()
|
|
||||||
return await context.doInAppContext(appId, async () => {
|
|
||||||
const params = { fields: { appId } }
|
|
||||||
const result = await triggers.externalTrigger(
|
|
||||||
loopAutomation(table._id!, loopOpts),
|
|
||||||
params,
|
|
||||||
{ getResponses: true }
|
|
||||||
)
|
|
||||||
if ("outputs" in result && !result.outputs.success) {
|
|
||||||
throw new Error("Unable to proceed - failed to return anything.")
|
|
||||||
}
|
|
||||||
return result as AutomationResults
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
it("attempt to run a basic loop", async () => {
|
it("attempt to run a basic loop", async () => {
|
||||||
const resp = await runLoop()
|
const result = await createAutomationBuilder(config)
|
||||||
expect(resp.steps[2].outputs.iterations).toBe(1)
|
.onAppAction()
|
||||||
|
.queryRows({
|
||||||
|
tableId: table._id!,
|
||||||
|
})
|
||||||
|
.loop({
|
||||||
|
option: LoopStepType.ARRAY,
|
||||||
|
binding: "{{ steps.1.rows }}",
|
||||||
|
})
|
||||||
|
.serverLog({ text: "log statement" })
|
||||||
|
.test({ fields: {} })
|
||||||
|
|
||||||
|
expect(result.steps[1].outputs.iterations).toBe(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("test a loop with a string", async () => {
|
it("test a loop with a string", async () => {
|
||||||
const resp = await runLoop({
|
const result = await createAutomationBuilder(config)
|
||||||
option: LoopStepType.STRING,
|
.onAppAction()
|
||||||
binding: "a,b,c",
|
.loop({
|
||||||
})
|
option: LoopStepType.STRING,
|
||||||
expect(resp.steps[2].outputs.iterations).toBe(3)
|
binding: "a,b,c",
|
||||||
|
})
|
||||||
|
.serverLog({ text: "log statement" })
|
||||||
|
.test({ fields: {} })
|
||||||
|
|
||||||
|
expect(result.steps[0].outputs.iterations).toBe(3)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("test a loop with a binding that returns an integer", async () => {
|
it("test a loop with a binding that returns an integer", async () => {
|
||||||
const resp = await runLoop({
|
const result = await createAutomationBuilder(config)
|
||||||
option: LoopStepType.ARRAY,
|
.onAppAction()
|
||||||
binding: "{{ 1 }}",
|
.loop({
|
||||||
})
|
option: LoopStepType.ARRAY,
|
||||||
expect(resp.steps[2].outputs.iterations).toBe(1)
|
binding: "{{ 1 }}",
|
||||||
|
})
|
||||||
|
.serverLog({ text: "log statement" })
|
||||||
|
.test({ fields: {} })
|
||||||
|
|
||||||
|
expect(result.steps[0].outputs.iterations).toBe(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should run an automation with a trigger, loop, and create row step", async () => {
|
it("should run an automation with a trigger, loop, and create row step", async () => {
|
||||||
|
|
|
@ -42,4 +42,33 @@ describe("app action trigger", () => {
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should correct coerce values based on the schema", async () => {
|
||||||
|
const { automation } = await createAutomationBuilder(config)
|
||||||
|
.onAppAction({
|
||||||
|
fields: { text: "string", number: "number", boolean: "boolean" },
|
||||||
|
})
|
||||||
|
.serverLog({
|
||||||
|
text: "{{ fields.text }} {{ fields.number }} {{ fields.boolean }}",
|
||||||
|
})
|
||||||
|
.save()
|
||||||
|
|
||||||
|
await config.api.application.publish()
|
||||||
|
|
||||||
|
const jobs = await captureAutomationResults(automation, async () => {
|
||||||
|
await config.withProdApp(async () => {
|
||||||
|
await config.api.automation.trigger(automation._id!, {
|
||||||
|
fields: { text: "1", number: "2", boolean: "true" },
|
||||||
|
timeout: 1000,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(jobs).toHaveLength(1)
|
||||||
|
expect(jobs[0].data.event.fields).toEqual({
|
||||||
|
text: "1",
|
||||||
|
number: 2,
|
||||||
|
boolean: true,
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -145,13 +145,13 @@ class StepBuilder<
|
||||||
TStep extends AutomationTriggerStepId
|
TStep extends AutomationTriggerStepId
|
||||||
> extends BranchStepBuilder<TStep> {
|
> extends BranchStepBuilder<TStep> {
|
||||||
private readonly config: TestConfiguration
|
private readonly config: TestConfiguration
|
||||||
private readonly trigger: AutomationTrigger
|
private readonly _trigger: AutomationTrigger
|
||||||
private _name: string | undefined = undefined
|
private _name: string | undefined = undefined
|
||||||
|
|
||||||
constructor(config: TestConfiguration, trigger: AutomationTrigger) {
|
constructor(config: TestConfiguration, trigger: AutomationTrigger) {
|
||||||
super()
|
super()
|
||||||
this.config = config
|
this.config = config
|
||||||
this.trigger = trigger
|
this._trigger = trigger
|
||||||
}
|
}
|
||||||
|
|
||||||
name(n: string): this {
|
name(n: string): this {
|
||||||
|
@ -165,7 +165,7 @@ class StepBuilder<
|
||||||
name,
|
name,
|
||||||
definition: {
|
definition: {
|
||||||
steps: this.steps,
|
steps: this.steps,
|
||||||
trigger: this.trigger,
|
trigger: this._trigger,
|
||||||
stepNames: this.stepNames,
|
stepNames: this.stepNames,
|
||||||
},
|
},
|
||||||
type: "automation",
|
type: "automation",
|
||||||
|
@ -182,6 +182,13 @@ class StepBuilder<
|
||||||
const runner = await this.save()
|
const runner = await this.save()
|
||||||
return await runner.test(triggerOutput)
|
return await runner.test(triggerOutput)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async trigger(
|
||||||
|
request: TriggerAutomationRequest
|
||||||
|
): Promise<TriggerAutomationResponse> {
|
||||||
|
const runner = await this.save()
|
||||||
|
return await runner.trigger(request)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AutomationRunner<TStep extends AutomationTriggerStepId> {
|
class AutomationRunner<TStep extends AutomationTriggerStepId> {
|
||||||
|
|
|
@ -15,6 +15,7 @@ export interface AutomationDataEvent {
|
||||||
oldRow?: Row
|
oldRow?: Row
|
||||||
user?: UserBindings
|
user?: UserBindings
|
||||||
timestamp?: number
|
timestamp?: number
|
||||||
|
fields?: Record<string, any>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AutomationData {
|
export interface AutomationData {
|
||||||
|
|
Loading…
Reference in New Issue