mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-05 22:38:48 +00:00
347 lines
9.6 KiB
JavaScript
347 lines
9.6 KiB
JavaScript
/* eslint-disable no-useless-escape */
|
|
import moment from 'moment';
|
|
import _ from 'lodash';
|
|
import axios from 'axios';
|
|
|
|
export function findProp(obj, prop, defval) {
|
|
if (typeof defval === 'undefined') defval = null;
|
|
prop = prop.split('.');
|
|
console.log('prop', prop);
|
|
console.log('obj', obj);
|
|
for (var i = 0; i < prop.length; i++) {
|
|
if (prop[i].endsWith(']')) {
|
|
const actual_prop = prop[i].split('[')[0];
|
|
const index = prop[i].split('[')[1].split(']')[0];
|
|
if (obj[actual_prop]) {
|
|
obj = obj[actual_prop][index];
|
|
} else {
|
|
obj = undefined;
|
|
}
|
|
} else if (obj !== undefined) {
|
|
if (typeof obj[prop[i]] === 'undefined') return defval;
|
|
obj = obj[prop[i]];
|
|
}
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
export const pluralize = (count, noun, suffix = 's') => `${count} ${noun}${count !== 1 ? suffix : ''}`;
|
|
|
|
export function resolve(data, state) {
|
|
if (data.startsWith('{{queries.') || data.startsWith('{{globals.') || data.startsWith('{{components.')) {
|
|
let prop = data.replace('{{', '').replace('}}', '');
|
|
return findProp(state, prop, '');
|
|
}
|
|
}
|
|
|
|
export function resolveReferences(object, state, defaultValue, customObjects = {}, withError = false) {
|
|
const reservedKeyword = ['app']; //Keywords that slows down the app
|
|
object = _.clone(object);
|
|
const objectType = typeof object;
|
|
let error;
|
|
switch (objectType) {
|
|
case 'string': {
|
|
if (object.startsWith('{{') && object.endsWith('}}')) {
|
|
const code = object.replace('{{', '').replace('}}', '');
|
|
let result = '';
|
|
|
|
if (reservedKeyword.includes(code)) {
|
|
error = `${code} is a reserved keyword`;
|
|
return [{}, error];
|
|
}
|
|
|
|
try {
|
|
const evalFunction = Function(
|
|
[
|
|
'variables',
|
|
'components',
|
|
'queries',
|
|
'globals',
|
|
'moment',
|
|
'_',
|
|
...Object.keys(customObjects),
|
|
reservedKeyword,
|
|
],
|
|
`return ${code}`
|
|
);
|
|
result = evalFunction(
|
|
state.variables,
|
|
state.components,
|
|
state.queries,
|
|
state.globals,
|
|
moment,
|
|
_,
|
|
...Object.values(customObjects),
|
|
null
|
|
);
|
|
} catch (err) {
|
|
error = err;
|
|
console.log('eval_error', err);
|
|
}
|
|
if (withError) return [result, error];
|
|
return result;
|
|
}
|
|
|
|
const dynamicVariables = getDynamicVariables(object);
|
|
|
|
if (dynamicVariables) {
|
|
if (dynamicVariables.length === 1 && dynamicVariables[0] === object) {
|
|
object = resolveReferences(dynamicVariables[0], state, null, customObjects);
|
|
} else {
|
|
for (const dynamicVariable of dynamicVariables) {
|
|
const value = resolveReferences(dynamicVariable, state, null, customObjects);
|
|
if (typeof value !== 'function') {
|
|
object = object.replace(dynamicVariable, value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (withError) return [object, error];
|
|
return object;
|
|
}
|
|
|
|
case 'object': {
|
|
if (Array.isArray(object)) {
|
|
console.log(`[Resolver] Resolving as array ${typeof object}`);
|
|
|
|
const new_array = [];
|
|
|
|
object.forEach((element, index) => {
|
|
const resolved_object = resolveReferences(element, state);
|
|
new_array[index] = resolved_object;
|
|
});
|
|
|
|
if (withError) return [new_array, error];
|
|
return new_array;
|
|
} else if (!_.isEmpty(object)) {
|
|
console.log(`[Resolver] Resolving as object ${typeof object}, state: ${state}`);
|
|
Object.keys(object).forEach((key) => {
|
|
const resolved_object = resolveReferences(object[key], state);
|
|
object[key] = resolved_object;
|
|
});
|
|
if (withError) return [object, error];
|
|
return object;
|
|
}
|
|
}
|
|
// eslint-disable-next-line no-fallthrough
|
|
default: {
|
|
if (withError) return [object, error];
|
|
return object;
|
|
}
|
|
}
|
|
}
|
|
|
|
export function getDynamicVariables(text) {
|
|
const matchedParams = text.match(/\{\{(.*?)\}\}/g);
|
|
return matchedParams;
|
|
}
|
|
|
|
export function computeComponentName(componentType, currentComponents) {
|
|
const currentComponentsForKind = Object.values(currentComponents).filter(
|
|
(component) => component.component.component === componentType
|
|
);
|
|
let found = false;
|
|
let componentName = '';
|
|
let currentNumber = currentComponentsForKind.length + 1;
|
|
|
|
while (!found) {
|
|
componentName = `${componentType.toLowerCase()}${currentNumber}`;
|
|
if (
|
|
Object.values(currentComponents).find((component) => component.component.name === componentName) === undefined
|
|
) {
|
|
found = true;
|
|
}
|
|
currentNumber = currentNumber + 1;
|
|
}
|
|
|
|
return componentName;
|
|
}
|
|
|
|
export function computeActionName(actions) {
|
|
const values = actions ? actions.value : [];
|
|
|
|
let currentNumber = values.length;
|
|
let found = false;
|
|
let actionName = '';
|
|
|
|
while (!found) {
|
|
actionName = `Action${currentNumber}`;
|
|
if (values.find((action) => action.name === actionName) === undefined) {
|
|
found = true;
|
|
}
|
|
currentNumber += 1;
|
|
}
|
|
|
|
return actionName;
|
|
}
|
|
|
|
export function validateQueryName(name) {
|
|
const nameRegex = new RegExp('^[A-Za-z0-9_-]*$');
|
|
return nameRegex.test(name);
|
|
}
|
|
|
|
export const convertToKebabCase = (string) =>
|
|
string
|
|
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
.replace(/\s+/g, '-')
|
|
.toLowerCase();
|
|
|
|
export const serializeNestedObjectToQueryParams = function (obj, prefix) {
|
|
var str = [],
|
|
p;
|
|
for (p in obj) {
|
|
if (Object.prototype.hasOwnProperty.call(obj, p)) {
|
|
var k = prefix ? prefix + '[' + p + ']' : p,
|
|
v = obj[p];
|
|
str.push(
|
|
// eslint-disable-next-line no-undef
|
|
v !== null && typeof v === 'object' ? serialize(v, k) : encodeURIComponent(k) + '=' + encodeURIComponent(v)
|
|
);
|
|
}
|
|
}
|
|
return str.join('&');
|
|
};
|
|
|
|
export function resolveWidgetFieldValue(prop, state, _default = [], customResolveObjects = {}) {
|
|
const widgetFieldValue = prop;
|
|
|
|
try {
|
|
return resolveReferences(widgetFieldValue, state, _default, customResolveObjects);
|
|
} catch (err) {
|
|
console.log(err);
|
|
}
|
|
|
|
return widgetFieldValue;
|
|
}
|
|
|
|
export function validateWidget({ validationObject, widgetValue, currentState, customResolveObjects }) {
|
|
let isValid = true;
|
|
let validationError = null;
|
|
|
|
const regex = validationObject?.regex?.value;
|
|
const minLength = validationObject?.minLength?.value;
|
|
const maxLength = validationObject?.maxLength?.value;
|
|
const customRule = validationObject?.customRule?.value;
|
|
|
|
const validationRegex = resolveWidgetFieldValue(regex, currentState, '', customResolveObjects);
|
|
const re = new RegExp(validationRegex, 'g');
|
|
|
|
if (!re.test(widgetValue)) {
|
|
return {
|
|
isValid: false,
|
|
validationError: 'The input should match pattern',
|
|
};
|
|
}
|
|
|
|
const resolvedMinLength = resolveWidgetFieldValue(minLength, currentState, 0, customResolveObjects);
|
|
if ((widgetValue || '').length < parseInt(resolvedMinLength)) {
|
|
return {
|
|
isValid: false,
|
|
validationError: `Minimum ${resolvedMinLength} characters is needed`,
|
|
};
|
|
}
|
|
|
|
const resolvedMaxLength = resolveWidgetFieldValue(maxLength, currentState, undefined, customResolveObjects);
|
|
if (resolvedMaxLength !== undefined) {
|
|
if ((widgetValue || '').length > parseInt(resolvedMaxLength)) {
|
|
return {
|
|
isValid: false,
|
|
validationError: `Maximum ${resolvedMaxLength} characters is allowed`,
|
|
};
|
|
}
|
|
}
|
|
|
|
const resolvedCustomRule = resolveWidgetFieldValue(customRule, currentState, false, customResolveObjects);
|
|
if (typeof resolvedCustomRule === 'string') {
|
|
return { isValid: false, validationError: resolvedCustomRule };
|
|
}
|
|
|
|
return {
|
|
isValid,
|
|
validationError,
|
|
};
|
|
}
|
|
|
|
export function validateEmail(email) {
|
|
const emailRegex =
|
|
/^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
|
|
return emailRegex.test(email);
|
|
}
|
|
|
|
export async function executeMultilineJS(currentState, code) {
|
|
let result = {},
|
|
error = null;
|
|
|
|
try {
|
|
const AsyncFunction = new Function(`return Object.getPrototypeOf(async function(){}).constructor`)();
|
|
var evalFn = new AsyncFunction('moment', '_', 'components', 'queries', 'globals', 'axios', 'variables', code);
|
|
result = {
|
|
status: 'ok',
|
|
data: await evalFn(
|
|
moment,
|
|
_,
|
|
currentState.components,
|
|
currentState.queries,
|
|
currentState.globals,
|
|
axios,
|
|
currentState.variables
|
|
),
|
|
};
|
|
} catch (err) {
|
|
console.log('JS execution failed: ', err);
|
|
error = err.stack.split('\n')[0];
|
|
result = { status: 'failed', data: { message: error, description: error } };
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
export function toQuery(params, delimiter = '&') {
|
|
const keys = Object.keys(params);
|
|
|
|
return keys.reduce((str, key, index) => {
|
|
let query = `${str}${key}=${params[key]}`;
|
|
|
|
if (index < keys.length - 1) {
|
|
query += delimiter;
|
|
}
|
|
|
|
return query;
|
|
}, '');
|
|
}
|
|
|
|
export const isJson = (str) => {
|
|
try {
|
|
JSON.parse(str);
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
export function buildURLWithQuery(url, query = {}) {
|
|
return `${url}?${toQuery(query)}`;
|
|
}
|
|
|
|
export const handleCircularStructureToJSON = () => {
|
|
const seen = new WeakSet();
|
|
|
|
return (key, value) => {
|
|
if (typeof value === 'object' && value !== null) {
|
|
if (seen.has(value)) {
|
|
return 'Object';
|
|
}
|
|
seen.add(value);
|
|
}
|
|
return value;
|
|
};
|
|
};
|
|
|
|
export function hasCircularDependency(obj) {
|
|
try {
|
|
JSON.stringify(obj);
|
|
} catch (e) {
|
|
return String(e).includes('Converting circular structure to JSON');
|
|
}
|
|
return false;
|
|
}
|