2022-11-25 20:57:07 +01:00
|
|
|
import {
|
2022-06-23 17:09:35 +02:00
|
|
|
decodeJSBinding,
|
|
|
|
isJSBinding,
|
|
|
|
encodeJSBinding,
|
2022-11-25 20:57:07 +01:00
|
|
|
} from "@budibase/string-templates"
|
|
|
|
import sdk from "../sdk"
|
|
|
|
import { Row } from "@budibase/types"
|
2024-01-30 11:00:44 +01:00
|
|
|
import { LoopStep, LoopStepType } from "../definitions/automations"
|
2020-09-23 14:34:11 +02:00
|
|
|
|
2020-09-23 17:16:24 +02:00
|
|
|
/**
|
2021-01-19 18:29:38 +01:00
|
|
|
* When values are input to the system generally they will be of type string as this is required for template strings.
|
|
|
|
* This can generate some odd scenarios as the Schema of the automation requires a number but the builder might supply
|
|
|
|
* a string with template syntax to get the number from the rest of the context. To support this the server has to
|
|
|
|
* make sure that the post template statement can be cast into the correct type, this function does this for numbers
|
|
|
|
* and booleans.
|
2020-09-23 17:16:24 +02:00
|
|
|
*
|
2023-10-17 17:46:32 +02:00
|
|
|
* @param inputs An object of inputs, please note this will not recurse down into any objects within, it simply
|
2020-09-23 17:16:24 +02:00
|
|
|
* cleanses the top level inputs, however it can be used by recursively calling it deeper into the object structures if
|
|
|
|
* the schema is known.
|
2023-10-17 17:46:32 +02:00
|
|
|
* @param schema The defined schema of the inputs, in the form of JSON schema. The schema definition of an
|
2020-09-23 17:16:24 +02:00
|
|
|
* automation is the likely use case of this, however validate.js syntax can be converted closely enough to use this by
|
|
|
|
* wrapping the schema properties in a top level "properties" object.
|
2023-10-17 17:46:32 +02:00
|
|
|
* @returns The inputs object which has had all the various types supported by this function converted to their
|
2020-09-23 17:16:24 +02:00
|
|
|
* primitive types.
|
|
|
|
*/
|
2023-03-27 20:38:49 +02:00
|
|
|
export function cleanInputValues(inputs: Record<string, any>, schema?: any) {
|
2020-09-23 14:34:11 +02:00
|
|
|
if (schema == null) {
|
|
|
|
return inputs
|
|
|
|
}
|
|
|
|
for (let inputKey of Object.keys(inputs)) {
|
|
|
|
let input = inputs[inputKey]
|
|
|
|
if (typeof input !== "string") {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
let propSchema = schema.properties[inputKey]
|
|
|
|
if (!propSchema) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if (propSchema.type === "boolean") {
|
|
|
|
let lcInput = input.toLowerCase()
|
|
|
|
if (lcInput === "true") {
|
|
|
|
inputs[inputKey] = true
|
|
|
|
}
|
|
|
|
if (lcInput === "false") {
|
|
|
|
inputs[inputKey] = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (propSchema.type === "number") {
|
|
|
|
let floatInput = parseFloat(input)
|
|
|
|
if (!isNaN(floatInput)) {
|
|
|
|
inputs[inputKey] = floatInput
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-03-08 14:39:18 +01:00
|
|
|
//Check if input field for Update Row should be a relationship and cast to array
|
2023-01-31 11:11:56 +01:00
|
|
|
for (let key in inputs.row) {
|
|
|
|
if (
|
|
|
|
inputs.schema?.[key]?.type === "link" &&
|
|
|
|
inputs.row[key] &&
|
|
|
|
typeof inputs.row[key] === "string"
|
|
|
|
) {
|
2023-03-08 14:39:18 +01:00
|
|
|
try {
|
|
|
|
inputs.row[key] = JSON.parse(inputs.row[key])
|
|
|
|
} catch (e) {
|
|
|
|
//Link is not an array or object, so continue
|
|
|
|
}
|
2023-01-31 11:11:56 +01:00
|
|
|
}
|
|
|
|
}
|
2020-09-23 14:34:11 +02:00
|
|
|
return inputs
|
|
|
|
}
|
|
|
|
|
2020-09-23 17:16:24 +02:00
|
|
|
/**
|
2020-10-09 20:10:28 +02:00
|
|
|
* Given a row input like a save or update row we need to clean the inputs against a schema that is not part of
|
2020-10-09 19:49:23 +02:00
|
|
|
* the automation but is instead part of the Table/Table. This function will get the table schema and use it to instead
|
2020-10-09 20:10:28 +02:00
|
|
|
* perform the cleanInputValues function on the input row.
|
2020-09-23 17:16:24 +02:00
|
|
|
*
|
2023-10-17 17:46:32 +02:00
|
|
|
* @param tableId The ID of the Table/Table which the schema is to be retrieved for.
|
|
|
|
* @param row The input row structure which requires clean-up after having been through template statements.
|
|
|
|
* @returns The cleaned up rows object, will should now have all the required primitive types.
|
2020-09-23 17:16:24 +02:00
|
|
|
*/
|
2022-11-25 20:57:07 +01:00
|
|
|
export async function cleanUpRow(tableId: string, row: Row) {
|
2022-10-12 18:02:23 +02:00
|
|
|
let table = await sdk.tables.getTable(tableId)
|
2022-11-25 20:57:07 +01:00
|
|
|
return cleanInputValues(row, { properties: table.schema })
|
2020-09-23 14:34:11 +02:00
|
|
|
}
|
|
|
|
|
2022-11-25 20:57:07 +01:00
|
|
|
export function getError(err: any) {
|
2021-11-12 12:21:06 +01:00
|
|
|
if (err == null) {
|
|
|
|
return "No error provided."
|
|
|
|
}
|
|
|
|
if (
|
|
|
|
typeof err === "object" &&
|
|
|
|
(err.toString == null || err.toString() === "[object Object]")
|
|
|
|
) {
|
|
|
|
return JSON.stringify(err)
|
|
|
|
}
|
2021-10-19 18:00:54 +02:00
|
|
|
return typeof err !== "string" ? err.toString() : err
|
2020-09-23 14:34:11 +02:00
|
|
|
}
|
2022-04-18 10:22:23 +02:00
|
|
|
|
2022-11-25 20:57:07 +01:00
|
|
|
export function substituteLoopStep(hbsString: string, substitute: string) {
|
2022-06-23 17:09:35 +02:00
|
|
|
let checkForJS = isJSBinding(hbsString)
|
2022-10-25 16:19:07 +02:00
|
|
|
let substitutedHbsString = ""
|
|
|
|
let open = checkForJS ? `$("` : "{{"
|
|
|
|
let closed = checkForJS ? `")` : "}}"
|
2022-06-23 17:09:35 +02:00
|
|
|
if (checkForJS) {
|
2022-11-25 20:57:07 +01:00
|
|
|
hbsString = decodeJSBinding(hbsString) as string
|
2022-06-23 17:09:35 +02:00
|
|
|
}
|
2022-10-25 16:19:07 +02:00
|
|
|
let pointer = 0,
|
|
|
|
openPointer = 0,
|
|
|
|
closedPointer = 0
|
2023-03-10 15:40:26 +01:00
|
|
|
while (pointer < hbsString?.length) {
|
2022-10-25 16:19:07 +02:00
|
|
|
openPointer = hbsString.indexOf(open, pointer)
|
|
|
|
closedPointer = hbsString.indexOf(closed, pointer) + 2
|
|
|
|
if (openPointer < 0 || closedPointer < 0) {
|
|
|
|
substitutedHbsString += hbsString.substring(pointer)
|
|
|
|
break
|
2022-06-23 17:09:35 +02:00
|
|
|
}
|
2022-10-25 16:19:07 +02:00
|
|
|
let before = hbsString.substring(pointer, openPointer)
|
|
|
|
let block = hbsString
|
|
|
|
.substring(openPointer, closedPointer)
|
|
|
|
.replace(/loop/, substitute)
|
|
|
|
substitutedHbsString += before + block
|
|
|
|
pointer = closedPointer
|
2022-04-18 10:22:23 +02:00
|
|
|
}
|
2022-10-25 16:19:07 +02:00
|
|
|
if (checkForJS) {
|
|
|
|
substitutedHbsString = encodeJSBinding(substitutedHbsString)
|
|
|
|
}
|
|
|
|
return substitutedHbsString
|
2022-04-18 10:22:23 +02:00
|
|
|
}
|
2022-05-04 12:55:26 +02:00
|
|
|
|
2022-11-25 20:57:07 +01:00
|
|
|
export function stringSplit(value: string | string[]) {
|
|
|
|
if (value == null || Array.isArray(value)) {
|
|
|
|
return value || []
|
2022-05-04 12:55:26 +02:00
|
|
|
}
|
|
|
|
if (value.split("\n").length > 1) {
|
|
|
|
value = value.split("\n")
|
|
|
|
} else {
|
|
|
|
value = value.split(",")
|
|
|
|
}
|
|
|
|
return value
|
|
|
|
}
|
2022-12-12 23:02:32 +01:00
|
|
|
|
2024-01-30 11:00:44 +01:00
|
|
|
export function typecastForLooping(loopStep: LoopStep) {
|
|
|
|
const input = loopStep.inputs
|
2022-12-12 23:02:32 +01:00
|
|
|
if (!input || !input.binding) {
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
switch (loopStep.inputs.option) {
|
|
|
|
case LoopStepType.ARRAY:
|
|
|
|
if (typeof input.binding === "string") {
|
|
|
|
return JSON.parse(input.binding)
|
|
|
|
}
|
|
|
|
break
|
|
|
|
case LoopStepType.STRING:
|
|
|
|
if (Array.isArray(input.binding)) {
|
|
|
|
return input.binding.join(",")
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
} catch (err) {
|
|
|
|
throw new Error("Unable to cast to correct type")
|
|
|
|
}
|
|
|
|
return input.binding
|
|
|
|
}
|