Merge branches 'master' and 'budi-7664-sqs-self-host-ui-for-detecting-lack-of-sqs-support-2' of github.com:budibase/budibase into budi-7664-sqs-self-host-ui-for-detecting-lack-of-sqs-support-2
This commit is contained in:
commit
080cfc8dcf
|
@ -45,6 +45,16 @@
|
||||||
"no-prototype-builtins": "off",
|
"no-prototype-builtins": "off",
|
||||||
"local-rules/no-budibase-imports": "error"
|
"local-rules/no-budibase-imports": "error"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"packages/builder/**/*",
|
||||||
|
"packages/client/**/*",
|
||||||
|
"packages/frontend-core/**/*"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"no-console": ["error", { "allow": ["warn", "error", "debug"] } ]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "2.16.0",
|
"version": "2.16.2",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*",
|
"packages/*",
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
"test:watch": "jest --watchAll"
|
"test:watch": "jest --watchAll"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/nano": "10.1.4",
|
"@budibase/nano": "10.1.5",
|
||||||
"@budibase/pouchdb-replication-stream": "1.2.10",
|
"@budibase/pouchdb-replication-stream": "1.2.10",
|
||||||
"@budibase/shared-core": "0.0.0",
|
"@budibase/shared-core": "0.0.0",
|
||||||
"@budibase/types": "0.0.0",
|
"@budibase/types": "0.0.0",
|
||||||
|
|
|
@ -23,7 +23,7 @@ const getCloudfrontSignParams = () => {
|
||||||
return {
|
return {
|
||||||
keypairId: env.CLOUDFRONT_PUBLIC_KEY_ID!,
|
keypairId: env.CLOUDFRONT_PUBLIC_KEY_ID!,
|
||||||
privateKeyString: getPrivateKey(),
|
privateKeyString: getPrivateKey(),
|
||||||
expireTime: new Date().getTime() + 1000 * 60 * 60, // 1 hour
|
expireTime: new Date().getTime() + 1000 * 60 * 60 * 24, // 1 day
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -364,7 +364,6 @@ const getContextBindings = (asset, componentId) => {
|
||||||
* Generates a set of bindings for a given component context
|
* Generates a set of bindings for a given component context
|
||||||
*/
|
*/
|
||||||
const generateComponentContextBindings = (asset, componentContext) => {
|
const generateComponentContextBindings = (asset, componentContext) => {
|
||||||
console.log("Hello ")
|
|
||||||
const { component, definition, contexts } = componentContext
|
const { component, definition, contexts } = componentContext
|
||||||
if (!component || !definition || !contexts?.length) {
|
if (!component || !definition || !contexts?.length) {
|
||||||
return []
|
return []
|
||||||
|
|
|
@ -21,7 +21,7 @@ export const createBuilderWebsocket = appId => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
socket.on("connect_error", err => {
|
socket.on("connect_error", err => {
|
||||||
console.log("Failed to connect to builder websocket:", err.message)
|
console.error("Failed to connect to builder websocket:", err.message)
|
||||||
})
|
})
|
||||||
socket.on("disconnect", () => {
|
socket.on("disconnect", () => {
|
||||||
userStore.actions.reset()
|
userStore.actions.reset()
|
||||||
|
|
|
@ -373,6 +373,7 @@
|
||||||
confirmText="Save"
|
confirmText="Save"
|
||||||
onConfirm={saveRelationship}
|
onConfirm={saveRelationship}
|
||||||
disabled={!valid}
|
disabled={!valid}
|
||||||
|
size="L"
|
||||||
>
|
>
|
||||||
<div class="headings">
|
<div class="headings">
|
||||||
<Detail>Tables</Detail>
|
<Detail>Tables</Detail>
|
||||||
|
|
|
@ -312,7 +312,7 @@ export const insertBinding = (view, from, to, text, mode) => {
|
||||||
} else if (mode.name == "handlebars") {
|
} else if (mode.name == "handlebars") {
|
||||||
parsedInsert = hbInsert(view.state.doc?.toString(), from, to, text)
|
parsedInsert = hbInsert(view.state.doc?.toString(), from, to, text)
|
||||||
} else {
|
} else {
|
||||||
console.log("Unsupported")
|
console.warn("Unsupported")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="relationship-container">
|
<div class="relationship-container">
|
||||||
<div class="relationship-part">
|
<div class="relationship-type">
|
||||||
<Select
|
<Select
|
||||||
disabled={linkEditDisabled}
|
disabled={linkEditDisabled}
|
||||||
bind:value={relationshipPart1}
|
bind:value={relationshipPart1}
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="relationship-container">
|
<div class="relationship-container">
|
||||||
<div class="relationship-part">
|
<div class="relationship-type">
|
||||||
<Select
|
<Select
|
||||||
disabled={linkEditDisabled}
|
disabled={linkEditDisabled}
|
||||||
bind:value={relationshipPart2}
|
bind:value={relationshipPart2}
|
||||||
|
@ -79,6 +79,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.relationship-part {
|
.relationship-part {
|
||||||
flex-basis: 60%;
|
flex-basis: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.relationship-type {
|
||||||
|
flex-basis: 30%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -47,9 +47,10 @@
|
||||||
})
|
})
|
||||||
$: filteredHelpers = helpers?.filter(helper => {
|
$: filteredHelpers = helpers?.filter(helper => {
|
||||||
return (
|
return (
|
||||||
!search ||
|
(!search ||
|
||||||
helper.label.match(searchRgx) ||
|
helper.label.match(searchRgx) ||
|
||||||
helper.description.match(searchRgx)
|
helper.description.match(searchRgx)) &&
|
||||||
|
(mode.name !== "javascript" || helper.allowsJs)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,7 @@
|
||||||
}))
|
}))
|
||||||
navigateStep(target)
|
navigateStep(target)
|
||||||
} else {
|
} else {
|
||||||
console.log("Could not retrieve step")
|
console.warn("Could not retrieve step")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (typeof tourStep.onComplete === "function") {
|
if (typeof tourStep.onComplete === "function") {
|
||||||
|
|
|
@ -3,11 +3,11 @@ import { get } from "svelte/store"
|
||||||
|
|
||||||
const registerNode = async (node, tourStepKey) => {
|
const registerNode = async (node, tourStepKey) => {
|
||||||
if (!node) {
|
if (!node) {
|
||||||
console.log("Tour Handler - an anchor node is required")
|
console.warn("Tour Handler - an anchor node is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!get(store).tourKey) {
|
if (!get(store).tourKey) {
|
||||||
console.log("Tour Handler - No active tour ", tourStepKey, node)
|
console.error("Tour Handler - No active tour ", tourStepKey, node)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ const endUserOnboarding = async ({ skipped = false } = {}) => {
|
||||||
onboarding: false,
|
onboarding: false,
|
||||||
}))
|
}))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("Onboarding failed", e)
|
console.error("Onboarding failed", e)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { getManifest } from "@budibase/string-templates"
|
import { getManifest, helpersToRemoveForJs } from "@budibase/string-templates"
|
||||||
|
|
||||||
export function handlebarsCompletions() {
|
export function handlebarsCompletions() {
|
||||||
const manifest = getManifest()
|
const manifest = getManifest()
|
||||||
|
@ -11,6 +11,9 @@ export function handlebarsCompletions() {
|
||||||
label: helperName,
|
label: helperName,
|
||||||
displayText: helperName,
|
displayText: helperName,
|
||||||
description: helperConfig.description,
|
description: helperConfig.description,
|
||||||
|
allowsJs:
|
||||||
|
!helperConfig.requiresBlock &&
|
||||||
|
!helpersToRemoveForJs.includes(helperName),
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ export const syncURLToState = options => {
|
||||||
let cachedPage = get(routify.page)
|
let cachedPage = get(routify.page)
|
||||||
let previousParamsHash = null
|
let previousParamsHash = null
|
||||||
let debug = false
|
let debug = false
|
||||||
const log = (...params) => debug && console.log(`[${urlParam}]`, ...params)
|
const log = (...params) => debug && console.debug(`[${urlParam}]`, ...params)
|
||||||
|
|
||||||
// Navigate to a certain URL
|
// Navigate to a certain URL
|
||||||
const gotoUrl = (url, params) => {
|
const gotoUrl = (url, params) => {
|
||||||
|
|
|
@ -107,7 +107,7 @@
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!prodAppId) {
|
if (!prodAppId) {
|
||||||
console.log("Application id required")
|
console.error("Application id required")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await usersFetch.update({
|
await usersFetch.update({
|
||||||
|
|
|
@ -66,7 +66,7 @@
|
||||||
try {
|
try {
|
||||||
await store.actions.screens.updateSetting(get(selectedScreen), key, value)
|
await store.actions.screens.updateSetting(get(selectedScreen), key, value)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.error(error)
|
||||||
notifications.error("Error saving screen settings")
|
notifications.error("Error saving screen settings")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,7 +71,7 @@
|
||||||
$goto(`./${screenId}`)
|
$goto(`./${screenId}`)
|
||||||
store.actions.screens.select(screenId)
|
store.actions.screens.select(screenId)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.error(error)
|
||||||
notifications.error("Error creating screens")
|
notifications.error("Error creating screens")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
async function login() {
|
async function login() {
|
||||||
form.validate()
|
form.validate()
|
||||||
if (Object.keys(errors).length > 0) {
|
if (Object.keys(errors).length > 0) {
|
||||||
console.log("errors", errors)
|
console.error("errors", errors)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -307,7 +307,7 @@
|
||||||
// Reset view
|
// Reset view
|
||||||
resetView()
|
resetView()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("There was a problem with the map", e)
|
console.error("There was a problem with the map", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@
|
||||||
resolve({ initialised: true })
|
resolve({ initialised: true })
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.log("There was a problem scanning the image", err)
|
console.error("There was a problem scanning the image", err)
|
||||||
resolve({ initialised: false })
|
resolve({ initialised: false })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -14,7 +14,7 @@ const createOrgStore = () => {
|
||||||
const settingsConfigDoc = await API.getTenantConfig(tenantId)
|
const settingsConfigDoc = await API.getTenantConfig(tenantId)
|
||||||
set({ logoUrl: settingsConfigDoc.config.logoUrl })
|
set({ logoUrl: settingsConfigDoc.config.logoUrl })
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("Could not init org ", e)
|
console.error("Could not init org ", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ export const createGridWebsocket = context => {
|
||||||
connectToDatasource(get(datasource))
|
connectToDatasource(get(datasource))
|
||||||
})
|
})
|
||||||
socket.on("connect_error", err => {
|
socket.on("connect_error", err => {
|
||||||
console.log("Failed to connect to grid websocket:", err.message)
|
console.error("Failed to connect to grid websocket:", err.message)
|
||||||
})
|
})
|
||||||
|
|
||||||
// User events
|
// User events
|
||||||
|
|
|
@ -5,10 +5,10 @@ if [[ -n $CI ]]
|
||||||
then
|
then
|
||||||
# Running in ci, where resources are limited
|
# Running in ci, where resources are limited
|
||||||
export NODE_OPTIONS="--max-old-space-size=4096"
|
export NODE_OPTIONS="--max-old-space-size=4096"
|
||||||
echo "jest --coverage --maxWorkers=2 --forceExit --workerIdleMemoryLimit=2000MB --bail"
|
echo "jest --coverage --maxWorkers=2 --forceExit --workerIdleMemoryLimit=2000MB --bail $@"
|
||||||
jest --coverage --maxWorkers=2 --forceExit --workerIdleMemoryLimit=2000MB --bail
|
jest --coverage --maxWorkers=2 --forceExit --workerIdleMemoryLimit=2000MB --bail $@
|
||||||
else
|
else
|
||||||
# --maxWorkers performs better in development
|
# --maxWorkers performs better in development
|
||||||
echo "jest --coverage --maxWorkers=2 --forceExit"
|
echo "jest --coverage --maxWorkers=2 --forceExit $@"
|
||||||
jest --coverage --maxWorkers=2 --forceExit
|
jest --coverage --maxWorkers=2 --forceExit $@
|
||||||
fi
|
fi
|
|
@ -5,7 +5,7 @@ import {
|
||||||
} from "@budibase/string-templates"
|
} from "@budibase/string-templates"
|
||||||
import sdk from "../sdk"
|
import sdk from "../sdk"
|
||||||
import { Row } from "@budibase/types"
|
import { Row } from "@budibase/types"
|
||||||
import { LoopStep, LoopStepType, LoopInput } from "../definitions/automations"
|
import { LoopInput, LoopStep, LoopStepType } from "../definitions/automations"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When values are input to the system generally they will be of type string as this is required for template strings.
|
* When values are input to the system generally they will be of type string as this is required for template strings.
|
||||||
|
@ -128,23 +128,28 @@ export function substituteLoopStep(hbsString: string, substitute: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function stringSplit(value: string | string[]) {
|
export function stringSplit(value: string | string[]) {
|
||||||
if (value == null || Array.isArray(value)) {
|
if (value == null) {
|
||||||
return value || []
|
return []
|
||||||
}
|
|
||||||
if (value.split("\n").length > 1) {
|
|
||||||
value = value.split("\n")
|
|
||||||
} else {
|
|
||||||
value = value.split(",")
|
|
||||||
}
|
}
|
||||||
|
if (Array.isArray(value)) {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
if (typeof value !== "string") {
|
||||||
|
throw new Error(`Unable to split value of type ${typeof value}: ${value}`)
|
||||||
|
}
|
||||||
|
const splitOnNewLine = value.split("\n")
|
||||||
|
if (splitOnNewLine.length > 1) {
|
||||||
|
return splitOnNewLine
|
||||||
|
}
|
||||||
|
return value.split(",")
|
||||||
|
}
|
||||||
|
|
||||||
export function typecastForLooping(loopStep: LoopStep, input: LoopInput) {
|
export function typecastForLooping(input: LoopInput) {
|
||||||
if (!input || !input.binding) {
|
if (!input || !input.binding) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
switch (loopStep.inputs.option) {
|
switch (input.option) {
|
||||||
case LoopStepType.ARRAY:
|
case LoopStepType.ARRAY:
|
||||||
if (typeof input.binding === "string") {
|
if (typeof input.binding === "string") {
|
||||||
return JSON.parse(input.binding)
|
return JSON.parse(input.binding)
|
||||||
|
|
|
@ -3,11 +3,13 @@ import * as triggers from "../triggers"
|
||||||
import { loopAutomation } from "../../tests/utilities/structures"
|
import { loopAutomation } from "../../tests/utilities/structures"
|
||||||
import { context } from "@budibase/backend-core"
|
import { context } from "@budibase/backend-core"
|
||||||
import * as setup from "./utilities"
|
import * as setup from "./utilities"
|
||||||
|
import { Row, Table } from "@budibase/types"
|
||||||
|
import { LoopInput, LoopStepType } from "../../definitions/automations"
|
||||||
|
|
||||||
describe("Attempt to run a basic loop automation", () => {
|
describe("Attempt to run a basic loop automation", () => {
|
||||||
let config = setup.getConfig(),
|
let config = setup.getConfig(),
|
||||||
table: any,
|
table: Table,
|
||||||
row: any
|
row: Row
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await automation.init()
|
await automation.init()
|
||||||
|
@ -18,12 +20,12 @@ describe("Attempt to run a basic loop automation", () => {
|
||||||
|
|
||||||
afterAll(setup.afterAll)
|
afterAll(setup.afterAll)
|
||||||
|
|
||||||
async function runLoop(loopOpts?: any) {
|
async function runLoop(loopOpts?: LoopInput) {
|
||||||
const appId = config.getAppId()
|
const appId = config.getAppId()
|
||||||
return await context.doInAppContext(appId, async () => {
|
return await context.doInAppContext(appId, async () => {
|
||||||
const params = { fields: { appId } }
|
const params = { fields: { appId } }
|
||||||
return await triggers.externalTrigger(
|
return await triggers.externalTrigger(
|
||||||
loopAutomation(table._id, loopOpts),
|
loopAutomation(table._id!, loopOpts),
|
||||||
params,
|
params,
|
||||||
{ getResponses: true }
|
{ getResponses: true }
|
||||||
)
|
)
|
||||||
|
@ -37,9 +39,17 @@ describe("Attempt to run a basic loop automation", () => {
|
||||||
|
|
||||||
it("test a loop with a string", async () => {
|
it("test a loop with a string", async () => {
|
||||||
const resp = await runLoop({
|
const resp = await runLoop({
|
||||||
type: "String",
|
option: LoopStepType.STRING,
|
||||||
binding: "a,b,c",
|
binding: "a,b,c",
|
||||||
})
|
})
|
||||||
expect(resp.steps[2].outputs.iterations).toBe(3)
|
expect(resp.steps[2].outputs.iterations).toBe(3)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("test a loop with a binding that returns an integer", async () => {
|
||||||
|
const resp = await runLoop({
|
||||||
|
option: LoopStepType.ARRAY,
|
||||||
|
binding: "{{ 1 }}",
|
||||||
|
})
|
||||||
|
expect(resp.steps[2].outputs.iterations).toBe(1)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -9,7 +9,7 @@ import * as utils from "./utils"
|
||||||
import env from "../environment"
|
import env from "../environment"
|
||||||
import { context, db as dbCore } from "@budibase/backend-core"
|
import { context, db as dbCore } from "@budibase/backend-core"
|
||||||
import { Automation, Row, AutomationData, AutomationJob } from "@budibase/types"
|
import { Automation, Row, AutomationData, AutomationJob } from "@budibase/types"
|
||||||
import { executeSynchronously } from "../threads/automation"
|
import { executeInThread } from "../threads/automation"
|
||||||
|
|
||||||
export const TRIGGER_DEFINITIONS = definitions
|
export const TRIGGER_DEFINITIONS = definitions
|
||||||
const JOB_OPTS = {
|
const JOB_OPTS = {
|
||||||
|
@ -117,8 +117,7 @@ export async function externalTrigger(
|
||||||
appId: context.getAppId(),
|
appId: context.getAppId(),
|
||||||
automation,
|
automation,
|
||||||
}
|
}
|
||||||
const job = { data } as AutomationJob
|
return executeInThread({ data } as AutomationJob)
|
||||||
return executeSynchronously(job)
|
|
||||||
} else {
|
} else {
|
||||||
return automationQueue.add(data, JOB_OPTS)
|
return automationQueue.add(data, JOB_OPTS)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
const automationUtils = require("../automationUtils")
|
import { LoopStep, LoopStepType } from "../../definitions/automations"
|
||||||
|
import {
|
||||||
|
typecastForLooping,
|
||||||
|
cleanInputValues,
|
||||||
|
substituteLoopStep,
|
||||||
|
} from "../automationUtils"
|
||||||
|
|
||||||
describe("automationUtils", () => {
|
describe("automationUtils", () => {
|
||||||
describe("substituteLoopStep", () => {
|
describe("substituteLoopStep", () => {
|
||||||
it("should allow multiple loop binding substitutes", () => {
|
it("should allow multiple loop binding substitutes", () => {
|
||||||
expect(
|
expect(
|
||||||
automationUtils.substituteLoopStep(
|
substituteLoopStep(
|
||||||
`{{ loop.currentItem._id }} {{ loop.currentItem._id }} {{ loop.currentItem._id }}`,
|
`{{ loop.currentItem._id }} {{ loop.currentItem._id }} {{ loop.currentItem._id }}`,
|
||||||
"step.2"
|
"step.2"
|
||||||
)
|
)
|
||||||
|
@ -15,7 +20,7 @@ describe("automationUtils", () => {
|
||||||
|
|
||||||
it("should handle not subsituting outside of curly braces", () => {
|
it("should handle not subsituting outside of curly braces", () => {
|
||||||
expect(
|
expect(
|
||||||
automationUtils.substituteLoopStep(
|
substituteLoopStep(
|
||||||
`loop {{ loop.currentItem._id }}loop loop{{ loop.currentItem._id }}loop`,
|
`loop {{ loop.currentItem._id }}loop loop{{ loop.currentItem._id }}loop`,
|
||||||
"step.2"
|
"step.2"
|
||||||
)
|
)
|
||||||
|
@ -28,37 +33,20 @@ describe("automationUtils", () => {
|
||||||
describe("typeCastForLooping", () => {
|
describe("typeCastForLooping", () => {
|
||||||
it("should parse to correct type", () => {
|
it("should parse to correct type", () => {
|
||||||
expect(
|
expect(
|
||||||
automationUtils.typecastForLooping(
|
typecastForLooping({ option: LoopStepType.ARRAY, binding: [1, 2, 3] })
|
||||||
{ inputs: { option: "Array" } },
|
|
||||||
{ binding: [1, 2, 3] }
|
|
||||||
)
|
|
||||||
).toEqual([1, 2, 3])
|
).toEqual([1, 2, 3])
|
||||||
expect(
|
expect(
|
||||||
automationUtils.typecastForLooping(
|
typecastForLooping({ option: LoopStepType.ARRAY, binding: "[1,2,3]" })
|
||||||
{ inputs: { option: "Array" } },
|
|
||||||
{ binding: "[1, 2, 3]" }
|
|
||||||
)
|
|
||||||
).toEqual([1, 2, 3])
|
).toEqual([1, 2, 3])
|
||||||
expect(
|
expect(
|
||||||
automationUtils.typecastForLooping(
|
typecastForLooping({ option: LoopStepType.STRING, binding: [1, 2, 3] })
|
||||||
{ inputs: { option: "String" } },
|
|
||||||
{ binding: [1, 2, 3] }
|
|
||||||
)
|
|
||||||
).toEqual("1,2,3")
|
).toEqual("1,2,3")
|
||||||
})
|
})
|
||||||
it("should handle null values", () => {
|
it("should handle null values", () => {
|
||||||
// expect it to handle where the binding is null
|
// expect it to handle where the binding is null
|
||||||
expect(
|
expect(typecastForLooping({ option: LoopStepType.ARRAY })).toEqual(null)
|
||||||
automationUtils.typecastForLooping(
|
|
||||||
{ inputs: { option: "Array" } },
|
|
||||||
{ binding: null }
|
|
||||||
)
|
|
||||||
).toEqual(null)
|
|
||||||
expect(() =>
|
expect(() =>
|
||||||
automationUtils.typecastForLooping(
|
typecastForLooping({ option: LoopStepType.ARRAY, binding: "test" })
|
||||||
{ inputs: { option: "Array" } },
|
|
||||||
{ binding: "test" }
|
|
||||||
)
|
|
||||||
).toThrow()
|
).toThrow()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -80,7 +68,7 @@ describe("automationUtils", () => {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
expect(
|
expect(
|
||||||
automationUtils.cleanInputValues(
|
cleanInputValues(
|
||||||
{
|
{
|
||||||
row: {
|
row: {
|
||||||
relationship: `[{"_id": "ro_ta_users_us_3"}]`,
|
relationship: `[{"_id": "ro_ta_users_us_3"}]`,
|
||||||
|
@ -113,7 +101,7 @@ describe("automationUtils", () => {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
expect(
|
expect(
|
||||||
automationUtils.cleanInputValues(
|
cleanInputValues(
|
||||||
{
|
{
|
||||||
row: {
|
row: {
|
||||||
relationship: `ro_ta_users_us_3`,
|
relationship: `ro_ta_users_us_3`,
|
||||||
|
|
|
@ -6,14 +6,14 @@ export enum LoopStepType {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LoopStep extends AutomationStep {
|
export interface LoopStep extends AutomationStep {
|
||||||
inputs: {
|
inputs: LoopInput
|
||||||
option: LoopStepType
|
|
||||||
[key: string]: any
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LoopInput {
|
export interface LoopInput {
|
||||||
binding: string[] | string
|
option: LoopStepType
|
||||||
|
binding?: string[] | string | number[]
|
||||||
|
iterations?: string
|
||||||
|
failure?: any
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TriggerOutput {
|
export interface TriggerOutput {
|
||||||
|
|
|
@ -23,6 +23,7 @@ import {
|
||||||
TableSourceType,
|
TableSourceType,
|
||||||
Query,
|
Query,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
|
import { LoopInput, LoopStepType } from "../../definitions/automations"
|
||||||
|
|
||||||
const { BUILTIN_ROLE_IDS } = roles
|
const { BUILTIN_ROLE_IDS } = roles
|
||||||
|
|
||||||
|
@ -204,10 +205,13 @@ export function serverLogAutomation(appId?: string): Automation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loopAutomation(tableId: string, loopOpts?: any): Automation {
|
export function loopAutomation(
|
||||||
|
tableId: string,
|
||||||
|
loopOpts?: LoopInput
|
||||||
|
): Automation {
|
||||||
if (!loopOpts) {
|
if (!loopOpts) {
|
||||||
loopOpts = {
|
loopOpts = {
|
||||||
option: "Array",
|
option: LoopStepType.ARRAY,
|
||||||
binding: "{{ steps.1.rows }}",
|
binding: "{{ steps.1.rows }}",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,22 +43,19 @@ const CRON_STEP_ID = triggerDefs.CRON.stepId
|
||||||
const STOPPED_STATUS = { success: true, status: AutomationStatus.STOPPED }
|
const STOPPED_STATUS = { success: true, status: AutomationStatus.STOPPED }
|
||||||
|
|
||||||
function getLoopIterations(loopStep: LoopStep) {
|
function getLoopIterations(loopStep: LoopStep) {
|
||||||
let binding = loopStep.inputs.binding
|
const binding = loopStep.inputs.binding
|
||||||
if (!binding) {
|
if (!binding) {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
const isString = typeof binding === "string"
|
|
||||||
try {
|
try {
|
||||||
if (isString) {
|
const json = typeof binding === "string" ? JSON.parse(binding) : binding
|
||||||
binding = JSON.parse(binding)
|
if (Array.isArray(json)) {
|
||||||
|
return json.length
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// ignore error - wasn't able to parse
|
// ignore error - wasn't able to parse
|
||||||
}
|
}
|
||||||
if (Array.isArray(binding)) {
|
if (typeof binding === "string") {
|
||||||
return binding.length
|
|
||||||
}
|
|
||||||
if (isString) {
|
|
||||||
return automationUtils.stringSplit(binding).length
|
return automationUtils.stringSplit(binding).length
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
|
@ -256,7 +253,7 @@ class Orchestrator {
|
||||||
this._context.env = await sdkUtils.getEnvironmentVariables()
|
this._context.env = await sdkUtils.getEnvironmentVariables()
|
||||||
let automation = this._automation
|
let automation = this._automation
|
||||||
let stopped = false
|
let stopped = false
|
||||||
let loopStep: AutomationStep | undefined = undefined
|
let loopStep: LoopStep | undefined = undefined
|
||||||
|
|
||||||
let stepCount = 0
|
let stepCount = 0
|
||||||
let loopStepNumber: any = undefined
|
let loopStepNumber: any = undefined
|
||||||
|
@ -311,7 +308,7 @@ class Orchestrator {
|
||||||
|
|
||||||
stepCount++
|
stepCount++
|
||||||
if (step.stepId === LOOP_STEP_ID) {
|
if (step.stepId === LOOP_STEP_ID) {
|
||||||
loopStep = step
|
loopStep = step as LoopStep
|
||||||
loopStepNumber = stepCount
|
loopStepNumber = stepCount
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -331,7 +328,6 @@ class Orchestrator {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
loopStep.inputs.binding = automationUtils.typecastForLooping(
|
loopStep.inputs.binding = automationUtils.typecastForLooping(
|
||||||
loopStep as LoopStep,
|
|
||||||
loopStep.inputs as LoopInput
|
loopStep.inputs as LoopInput
|
||||||
)
|
)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -348,7 +344,7 @@ class Orchestrator {
|
||||||
loopStep = undefined
|
loopStep = undefined
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
let item = []
|
let item: any[] = []
|
||||||
if (
|
if (
|
||||||
typeof loopStep.inputs.binding === "string" &&
|
typeof loopStep.inputs.binding === "string" &&
|
||||||
loopStep.inputs.option === "String"
|
loopStep.inputs.option === "String"
|
||||||
|
@ -399,7 +395,8 @@ class Orchestrator {
|
||||||
|
|
||||||
if (
|
if (
|
||||||
index === env.AUTOMATION_MAX_ITERATIONS ||
|
index === env.AUTOMATION_MAX_ITERATIONS ||
|
||||||
index === parseInt(loopStep.inputs.iterations)
|
(loopStep.inputs.iterations &&
|
||||||
|
index === parseInt(loopStep.inputs.iterations))
|
||||||
) {
|
) {
|
||||||
this.updateContextAndOutput(
|
this.updateContextAndOutput(
|
||||||
loopStepNumber,
|
loopStepNumber,
|
||||||
|
@ -615,7 +612,7 @@ export function execute(job: Job<AutomationData>, callback: WorkerCallback) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function executeSynchronously(job: Job) {
|
export async function executeInThread(job: Job<AutomationData>) {
|
||||||
const appId = job.data.event.appId
|
const appId = job.data.event.appId
|
||||||
if (!appId) {
|
if (!appId) {
|
||||||
throw new Error("Unable to execute, event doesn't contain app ID.")
|
throw new Error("Unable to execute, event doesn't contain app ID.")
|
||||||
|
@ -627,10 +624,10 @@ export function executeSynchronously(job: Job) {
|
||||||
}, job.data.event.timeout || 12000)
|
}, job.data.event.timeout || 12000)
|
||||||
})
|
})
|
||||||
|
|
||||||
return context.doInAppContext(appId, async () => {
|
return await context.doInAppContext(appId, async () => {
|
||||||
const envVars = await sdkUtils.getEnvironmentVariables()
|
const envVars = await sdkUtils.getEnvironmentVariables()
|
||||||
// put into automation thread for whole context
|
// put into automation thread for whole context
|
||||||
return context.doInEnvironmentContext(envVars, async () => {
|
return await context.doInEnvironmentContext(envVars, async () => {
|
||||||
const automationOrchestrator = new Orchestrator(job)
|
const automationOrchestrator = new Orchestrator(job)
|
||||||
return await Promise.race([
|
return await Promise.race([
|
||||||
automationOrchestrator.execute(),
|
automationOrchestrator.execute(),
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -25,7 +25,7 @@
|
||||||
"manifest": "node ./scripts/gen-collection-info.js"
|
"manifest": "node ./scripts/gen-collection-info.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/handlebars-helpers": "^0.13.0",
|
"@budibase/handlebars-helpers": "^0.13.1",
|
||||||
"dayjs": "^1.10.8",
|
"dayjs": "^1.10.8",
|
||||||
"handlebars": "^4.7.6",
|
"handlebars": "^4.7.6",
|
||||||
"lodash.clonedeep": "^4.5.0",
|
"lodash.clonedeep": "^4.5.0",
|
||||||
|
|
|
@ -10,8 +10,8 @@ const marked = require("marked")
|
||||||
* https://github.com/budibase/handlebars-helpers
|
* https://github.com/budibase/handlebars-helpers
|
||||||
*/
|
*/
|
||||||
const { join } = require("path")
|
const { join } = require("path")
|
||||||
|
const path = require("path")
|
||||||
|
|
||||||
const DIRECTORY = join(__dirname, "..", "..", "..")
|
|
||||||
const COLLECTIONS = [
|
const COLLECTIONS = [
|
||||||
"math",
|
"math",
|
||||||
"array",
|
"array",
|
||||||
|
@ -115,6 +115,8 @@ function getCommentInfo(file, func) {
|
||||||
docs.example = docs.example.replace("product", "multiply")
|
docs.example = docs.example.replace("product", "multiply")
|
||||||
}
|
}
|
||||||
docs.description = blocks[0].trim()
|
docs.description = blocks[0].trim()
|
||||||
|
docs.acceptsBlock = docs.tags.some(el => el.title === "block")
|
||||||
|
docs.acceptsInline = docs.tags.some(el => el.title === "inline")
|
||||||
return docs
|
return docs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +129,7 @@ function run() {
|
||||||
const foundNames = []
|
const foundNames = []
|
||||||
for (let collection of COLLECTIONS) {
|
for (let collection of COLLECTIONS) {
|
||||||
const collectionFile = fs.readFileSync(
|
const collectionFile = fs.readFileSync(
|
||||||
`${DIRECTORY}/node_modules/${HELPER_LIBRARY}/lib/${collection}.js`,
|
`${path.dirname(require.resolve(HELPER_LIBRARY))}/lib/${collection}.js`,
|
||||||
"utf8"
|
"utf8"
|
||||||
)
|
)
|
||||||
const collectionInfo = {}
|
const collectionInfo = {}
|
||||||
|
@ -159,6 +161,7 @@ function run() {
|
||||||
numArgs: args.length,
|
numArgs: args.length,
|
||||||
example: jsDocInfo.example || undefined,
|
example: jsDocInfo.example || undefined,
|
||||||
description: jsDocInfo.description,
|
description: jsDocInfo.description,
|
||||||
|
requiresBlock: jsDocInfo.acceptsBlock && !jsDocInfo.acceptsInline,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
outputJSON[collection] = collectionInfo
|
outputJSON[collection] = collectionInfo
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const { getHelperList } = require("../helpers")
|
const { getJsHelperList } = require("../helpers")
|
||||||
|
|
||||||
function getLayers(fullBlock) {
|
function getLayers(fullBlock) {
|
||||||
let layers = []
|
let layers = []
|
||||||
|
@ -109,7 +109,7 @@ module.exports.convertHBSBlock = (block, blockNumber) => {
|
||||||
const layers = getLayers(block)
|
const layers = getLayers(block)
|
||||||
|
|
||||||
let value = null
|
let value = null
|
||||||
const list = getHelperList()
|
const list = getJsHelperList()
|
||||||
for (let layer of layers) {
|
for (let layer of layers) {
|
||||||
const parts = splitBySpace(layer)
|
const parts = splitBySpace(layer)
|
||||||
if (value || parts.length > 1 || list[parts[0]]) {
|
if (value || parts.length > 1 || list[parts[0]]) {
|
||||||
|
|
|
@ -115,7 +115,7 @@ module.exports.duration = (str, pattern, format) => {
|
||||||
setLocale(config.str, config.pattern)
|
setLocale(config.str, config.pattern)
|
||||||
|
|
||||||
const duration = dayjs.duration(config.str, config.pattern)
|
const duration = dayjs.duration(config.str, config.pattern)
|
||||||
if (!isOptions(format)) {
|
if (format && !isOptions(format)) {
|
||||||
return duration.format(format)
|
return duration.format(format)
|
||||||
} else {
|
} else {
|
||||||
return duration.humanize()
|
return duration.humanize()
|
||||||
|
|
|
@ -7,7 +7,7 @@ const {
|
||||||
HelperFunctionBuiltin,
|
HelperFunctionBuiltin,
|
||||||
LITERAL_MARKER,
|
LITERAL_MARKER,
|
||||||
} = require("./constants")
|
} = require("./constants")
|
||||||
const { getHelperList } = require("./list")
|
const { getJsHelperList } = require("./list")
|
||||||
|
|
||||||
const HTML_SWAPS = {
|
const HTML_SWAPS = {
|
||||||
"<": "<",
|
"<": "<",
|
||||||
|
@ -97,4 +97,4 @@ module.exports.unregisterAll = handlebars => {
|
||||||
externalHandlebars.unregisterAll(handlebars)
|
externalHandlebars.unregisterAll(handlebars)
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.getHelperList = getHelperList
|
module.exports.getJsHelperList = getJsHelperList
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const { atob } = require("../utilities")
|
const { atob } = require("../utilities")
|
||||||
const cloneDeep = require("lodash.clonedeep")
|
const cloneDeep = require("lodash.clonedeep")
|
||||||
const { LITERAL_MARKER } = require("../helpers/constants")
|
const { LITERAL_MARKER } = require("../helpers/constants")
|
||||||
const { getHelperList } = require("./list")
|
const { getJsHelperList } = require("./list")
|
||||||
|
|
||||||
// The method of executing JS scripts depends on the bundle being built.
|
// The method of executing JS scripts depends on the bundle being built.
|
||||||
// This setter is used in the entrypoint (either index.cjs or index.mjs).
|
// This setter is used in the entrypoint (either index.cjs or index.mjs).
|
||||||
|
@ -49,7 +49,7 @@ module.exports.processJS = (handlebars, context) => {
|
||||||
// app context.
|
// app context.
|
||||||
const sandboxContext = {
|
const sandboxContext = {
|
||||||
$: path => getContextValue(path, cloneDeep(context)),
|
$: path => getContextValue(path, cloneDeep(context)),
|
||||||
helpers: getHelperList(),
|
helpers: getJsHelperList(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a sandbox with our context and run the JS
|
// Create a sandbox with our context and run the JS
|
||||||
|
|
|
@ -3,7 +3,10 @@ const helperList = require("@budibase/handlebars-helpers")
|
||||||
|
|
||||||
let helpers = undefined
|
let helpers = undefined
|
||||||
|
|
||||||
module.exports.getHelperList = () => {
|
const helpersToRemoveForJs = ["sortBy"]
|
||||||
|
module.exports.helpersToRemoveForJs = helpersToRemoveForJs
|
||||||
|
|
||||||
|
module.exports.getJsHelperList = () => {
|
||||||
if (helpers) {
|
if (helpers) {
|
||||||
return helpers
|
return helpers
|
||||||
}
|
}
|
||||||
|
@ -15,12 +18,17 @@ module.exports.getHelperList = () => {
|
||||||
}
|
}
|
||||||
for (let collection of constructed) {
|
for (let collection of constructed) {
|
||||||
for (let [key, func] of Object.entries(collection)) {
|
for (let [key, func] of Object.entries(collection)) {
|
||||||
helpers[key] = func
|
// Handlebars injects the hbs options to the helpers by default. We are adding an empty {} as a last parameter to simulate it
|
||||||
|
helpers[key] = (...props) => func(...props, {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (let key of Object.keys(externalHandlebars.addedHelpers)) {
|
for (let key of Object.keys(externalHandlebars.addedHelpers)) {
|
||||||
helpers[key] = externalHandlebars.addedHelpers[key]
|
helpers[key] = externalHandlebars.addedHelpers[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const toRemove of helpersToRemoveForJs) {
|
||||||
|
delete helpers[toRemove]
|
||||||
|
}
|
||||||
Object.freeze(helpers)
|
Object.freeze(helpers)
|
||||||
return helpers
|
return helpers
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ module.exports.findHBSBlocks = templates.findHBSBlocks
|
||||||
module.exports.convertToJS = templates.convertToJS
|
module.exports.convertToJS = templates.convertToJS
|
||||||
module.exports.setJSRunner = templates.setJSRunner
|
module.exports.setJSRunner = templates.setJSRunner
|
||||||
module.exports.FIND_ANY_HBS_REGEX = templates.FIND_ANY_HBS_REGEX
|
module.exports.FIND_ANY_HBS_REGEX = templates.FIND_ANY_HBS_REGEX
|
||||||
|
module.exports.helpersToRemoveForJs = templates.helpersToRemoveForJs
|
||||||
|
|
||||||
if (!process.env.NO_JS) {
|
if (!process.env.NO_JS) {
|
||||||
const { VM } = require("vm2")
|
const { VM } = require("vm2")
|
||||||
|
|
|
@ -10,6 +10,7 @@ const {
|
||||||
} = require("./utilities")
|
} = require("./utilities")
|
||||||
const { convertHBSBlock } = require("./conversion")
|
const { convertHBSBlock } = require("./conversion")
|
||||||
const javascript = require("./helpers/javascript")
|
const javascript = require("./helpers/javascript")
|
||||||
|
const { helpersToRemoveForJs } = require("./helpers/list")
|
||||||
|
|
||||||
const hbsInstance = handlebars.create()
|
const hbsInstance = handlebars.create()
|
||||||
registerAll(hbsInstance)
|
registerAll(hbsInstance)
|
||||||
|
@ -394,3 +395,4 @@ module.exports.convertToJS = hbs => {
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.FIND_ANY_HBS_REGEX = FIND_ANY_HBS_REGEX
|
module.exports.FIND_ANY_HBS_REGEX = FIND_ANY_HBS_REGEX
|
||||||
|
module.exports.helpersToRemoveForJs = helpersToRemoveForJs
|
||||||
|
|
|
@ -21,6 +21,7 @@ export const findHBSBlocks = templates.findHBSBlocks
|
||||||
export const convertToJS = templates.convertToJS
|
export const convertToJS = templates.convertToJS
|
||||||
export const setJSRunner = templates.setJSRunner
|
export const setJSRunner = templates.setJSRunner
|
||||||
export const FIND_ANY_HBS_REGEX = templates.FIND_ANY_HBS_REGEX
|
export const FIND_ANY_HBS_REGEX = templates.FIND_ANY_HBS_REGEX
|
||||||
|
export const helpersToRemoveForJs = templates.helpersToRemoveForJs
|
||||||
|
|
||||||
if (process && !process.env.NO_JS) {
|
if (process && !process.env.NO_JS) {
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -16,21 +16,55 @@ jest.mock("@budibase/handlebars-helpers/lib/uuid", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const fs = require("fs")
|
const fs = require("fs")
|
||||||
const { processString } = require("../src/index.cjs")
|
const {
|
||||||
|
processString,
|
||||||
|
convertToJS,
|
||||||
|
processStringSync,
|
||||||
|
encodeJSBinding,
|
||||||
|
} = require("../src/index.cjs")
|
||||||
|
|
||||||
const tk = require("timekeeper")
|
const tk = require("timekeeper")
|
||||||
|
const { getJsHelperList } = require("../src/helpers")
|
||||||
|
|
||||||
tk.freeze("2021-01-21T12:00:00")
|
tk.freeze("2021-01-21T12:00:00")
|
||||||
|
|
||||||
|
const processJS = (js, context) => {
|
||||||
|
return processStringSync(encodeJSBinding(js), context)
|
||||||
|
}
|
||||||
|
|
||||||
const manifest = JSON.parse(
|
const manifest = JSON.parse(
|
||||||
fs.readFileSync(require.resolve("../manifest.json"), "utf8")
|
fs.readFileSync(require.resolve("../manifest.json"), "utf8")
|
||||||
)
|
)
|
||||||
|
|
||||||
const collections = Object.keys(manifest)
|
const collections = Object.keys(manifest)
|
||||||
const examples = collections.reduce((acc, collection) => {
|
const examples = collections.reduce((acc, collection) => {
|
||||||
const functions = Object.keys(manifest[collection]).filter(
|
const functions = Object.entries(manifest[collection])
|
||||||
fnc => manifest[collection][fnc].example
|
.filter(([_, details]) => details.example)
|
||||||
)
|
.map(([name, details]) => {
|
||||||
if (functions.length) {
|
const example = details.example
|
||||||
|
let [hbs, js] = example.split("->").map(x => x.trim())
|
||||||
|
if (!js) {
|
||||||
|
// The function has no return value
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim 's
|
||||||
|
js = js.replace(/^\'|\'$/g, "")
|
||||||
|
if ((parsedExpected = tryParseJson(js))) {
|
||||||
|
if (Array.isArray(parsedExpected)) {
|
||||||
|
if (typeof parsedExpected[0] === "object") {
|
||||||
|
js = JSON.stringify(parsedExpected)
|
||||||
|
} else {
|
||||||
|
js = parsedExpected.join(",")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const requiresHbsBody = details.requiresBlock
|
||||||
|
return [name, { hbs, js, requiresHbsBody }]
|
||||||
|
})
|
||||||
|
.filter(x => !!x)
|
||||||
|
|
||||||
|
if (Object.keys(functions).length) {
|
||||||
acc[collection] = functions
|
acc[collection] = functions
|
||||||
}
|
}
|
||||||
return acc
|
return acc
|
||||||
|
@ -55,11 +89,7 @@ function tryParseJson(str) {
|
||||||
describe("manifest", () => {
|
describe("manifest", () => {
|
||||||
describe("examples are valid", () => {
|
describe("examples are valid", () => {
|
||||||
describe.each(Object.keys(examples))("%s", collection => {
|
describe.each(Object.keys(examples))("%s", collection => {
|
||||||
it.each(examples[collection])("%s", async func => {
|
it.each(examples[collection])("%s", async (_, { hbs, js }) => {
|
||||||
const example = manifest[collection][func].example
|
|
||||||
|
|
||||||
let [hbs, js] = example.split("->").map(x => x.trim())
|
|
||||||
|
|
||||||
const context = {
|
const context = {
|
||||||
double: i => i * 2,
|
double: i => i * 2,
|
||||||
isString: x => typeof x === "string",
|
isString: x => typeof x === "string",
|
||||||
|
@ -71,23 +101,40 @@ describe("manifest", () => {
|
||||||
context[`array${i}`] = JSON.parse(arrayString.replace(/\'/g, '"'))
|
context[`array${i}`] = JSON.parse(arrayString.replace(/\'/g, '"'))
|
||||||
})
|
})
|
||||||
|
|
||||||
if (js === undefined) {
|
let result = await processString(hbs, context)
|
||||||
// The function has no return value
|
result = result.replace(/ /g, " ")
|
||||||
return
|
expect(result).toEqual(js)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("can be parsed and run as js", () => {
|
||||||
|
const jsHelpers = getJsHelperList()
|
||||||
|
const jsExamples = Object.keys(examples).reduce((acc, v) => {
|
||||||
|
acc[v] = examples[v].filter(([key]) => jsHelpers[key])
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
describe.each(Object.keys(jsExamples))("%s", collection => {
|
||||||
|
it.each(
|
||||||
|
jsExamples[collection].filter(
|
||||||
|
([_, { requiresHbsBody }]) => !requiresHbsBody
|
||||||
|
)
|
||||||
|
)("%s", async (_, { hbs, js }) => {
|
||||||
|
const context = {
|
||||||
|
double: i => i * 2,
|
||||||
|
isString: x => typeof x === "string",
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = await processString(hbs, context)
|
const arrays = hbs.match(/\[[^/\]]+\]/)
|
||||||
// Trim 's
|
arrays?.forEach((arrayString, i) => {
|
||||||
js = js.replace(/^\'|\'$/g, "")
|
hbs = hbs.replace(new RegExp(escapeRegExp(arrayString)), `array${i}`)
|
||||||
if ((parsedExpected = tryParseJson(js))) {
|
context[`array${i}`] = JSON.parse(arrayString.replace(/\'/g, '"'))
|
||||||
if (Array.isArray(parsedExpected)) {
|
})
|
||||||
if (typeof parsedExpected[0] === "object") {
|
|
||||||
js = JSON.stringify(parsedExpected)
|
let convertedJs = convertToJS(hbs)
|
||||||
} else {
|
|
||||||
js = parsedExpected.join(",")
|
let result = processJS(convertedJs, context)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result = result.replace(/ /g, " ")
|
result = result.replace(/ /g, " ")
|
||||||
expect(result).toEqual(js)
|
expect(result).toEqual(js)
|
||||||
})
|
})
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
},
|
},
|
||||||
"jest": {},
|
"jest": {},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@budibase/nano": "10.1.4",
|
"@budibase/nano": "10.1.5",
|
||||||
"@types/koa": "2.13.4",
|
"@types/koa": "2.13.4",
|
||||||
"@types/pouchdb": "6.4.0",
|
"@types/pouchdb": "6.4.0",
|
||||||
"@types/redlock": "4.0.3",
|
"@types/redlock": "4.0.3",
|
||||||
|
|
16
yarn.lock
16
yarn.lock
|
@ -2031,10 +2031,10 @@
|
||||||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||||
|
|
||||||
"@budibase/handlebars-helpers@^0.13.0":
|
"@budibase/handlebars-helpers@^0.13.1":
|
||||||
version "0.13.0"
|
version "0.13.1"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/handlebars-helpers/-/handlebars-helpers-0.13.0.tgz#224333d14e3900b7dacf48286af1e624a9fd62ea"
|
resolved "https://registry.yarnpkg.com/@budibase/handlebars-helpers/-/handlebars-helpers-0.13.1.tgz#d02e73c0df8305cd675e70dc37f8427eb0842080"
|
||||||
integrity sha512-g8+sFrMNxsIDnK+MmdUICTVGr6ReUFtnPp9hJX0VZwz1pN3Ynolpk/Qbu6rEWAvoU1sEqY1mXr9uo/+kEfeGbQ==
|
integrity sha512-v4RbXhr3igvK3i2pj5cNltu/4NMxdPIzcUt/o0RoInhesNH1VSLRdweSFr6/Y34fsCR5jHZ6vltdcz2RgrTKgw==
|
||||||
dependencies:
|
dependencies:
|
||||||
get-object "^0.2.0"
|
get-object "^0.2.0"
|
||||||
get-value "^3.0.1"
|
get-value "^3.0.1"
|
||||||
|
@ -2050,10 +2050,10 @@
|
||||||
to-gfm-code-block "^0.1.1"
|
to-gfm-code-block "^0.1.1"
|
||||||
uuid "^9.0.1"
|
uuid "^9.0.1"
|
||||||
|
|
||||||
"@budibase/nano@10.1.4":
|
"@budibase/nano@10.1.5":
|
||||||
version "10.1.4"
|
version "10.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/nano/-/nano-10.1.4.tgz#5c2670d0b4c12d736ddd6581c57d47c0aa45efad"
|
resolved "https://registry.yarnpkg.com/@budibase/nano/-/nano-10.1.5.tgz#eeaded7bfc707ecabf8fde604425b865a90c06ec"
|
||||||
integrity sha512-J+IVaAljGideDvJss/AUxXA1599HEIUJo5c0LLlmc1KMA3GZWZjyX+w2fxAw3qF7hqFvX+qAStQgdcD3+/GPMA==
|
integrity sha512-q1eKIsYKo+iK17zsJYd3VBl+5ufQMPpHYLec0wVsid8wnJVrTQk7RNpBlBUn/EDgXM7t8XNNHlERqHu+CxJu8Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/tough-cookie" "^4.0.2"
|
"@types/tough-cookie" "^4.0.2"
|
||||||
axios "^1.1.3"
|
axios "^1.1.3"
|
||||||
|
|
Loading…
Reference in New Issue