ToolJet/frontend/src/_helpers/utils.js

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;
}