const { getTable } = require("../api/controllers/table/utils") const { findHBSBlocks, decodeJSBinding, isJSBinding, encodeJSBinding, } = require("@budibase/string-templates") /** * 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. * * @param {object} inputs An object of inputs, please note this will not recurse down into any objects within, it simply * cleanses the top level inputs, however it can be used by recursively calling it deeper into the object structures if * the schema is known. * @param {object} schema The defined schema of the inputs, in the form of JSON schema. The schema definition of an * 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. * @returns {object} The inputs object which has had all the various types supported by this function converted to their * primitive types. */ exports.cleanInputValues = (inputs, schema) => { 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 } } } return inputs } /** * Given a row input like a save or update row we need to clean the inputs against a schema that is not part of * the automation but is instead part of the Table/Table. This function will get the table schema and use it to instead * perform the cleanInputValues function on the input row. * * @param {string} tableId The ID of the Table/Table which the schema is to be retrieved for. * @param {object} row The input row structure which requires clean-up after having been through template statements. * @returns {Promise} The cleaned up rows object, will should now have all the required primitive types. */ exports.cleanUpRow = async (tableId, row) => { let table = await getTable(tableId) return exports.cleanInputValues(row, { properties: table.schema }) } exports.getError = err => { if (err == null) { return "No error provided." } if ( typeof err === "object" && (err.toString == null || err.toString() === "[object Object]") ) { return JSON.stringify(err) } return typeof err !== "string" ? err.toString() : err } exports.substituteLoopStep = (hbsString, substitute) => { let blocks = [] let checkForJS = isJSBinding(hbsString) if (checkForJS) { hbsString = decodeJSBinding(hbsString) blocks.push(hbsString) } else { blocks = findHBSBlocks(hbsString) } for (let block of blocks) { block = block.replace(/loop/, substitute) if (checkForJS) { hbsString = encodeJSBinding(block) } else { hbsString = block } } return hbsString } exports.stringSplit = value => { if (value == null) { return [] } if (value.split("\n").length > 1) { value = value.split("\n") } else { value = value.split(",") } return value }