low-code-service
Version:
to handle workflow execution, handlebars execution and condition execution for low code service
252 lines (226 loc) • 5.38 kB
text/typescript
import { get, set, cloneDeep } from 'lodash';
import sift from 'sift';
export const parseBoolean = function(string) {
switch (String(string).toLowerCase()) {
case 'true':
case 'yes':
case 'y':
case '1':
return true;
case 'false':
case 'no':
case 'n':
case '0':
return false;
default:
return undefined;
}
};
export const sanitizeDataForFilters = (dataForFilters, rawConditionData) => {
if (!(Array.isArray(rawConditionData) && rawConditionData.length)) {
return dataForFilters;
}
try {
const toRet = cloneDeep(dataForFilters);
for (const conditionObj of rawConditionData) {
if (!conditionObj.type) {
continue;
}
if (conditionObj.type === 'number') {
const sanitizedData = Number(get(toRet, conditionObj.field, 0));
set(toRet, conditionObj.field, sanitizedData);
}
if (conditionObj.type === 'boolean') {
const sanitizedData = parseBoolean(get(toRet, conditionObj.field, false));
set(toRet, conditionObj.field, sanitizedData);
}
}
return toRet;
} catch (err) {
// returning unsanitized data
return dataForFilters;
}
};
export const validateDataUsingFilters = (filters, dataForFilters) => {
for (const filter of filters) {
const { whenCondition } = filter;
let isWhenSatisfied = true;
if (whenCondition) {
const sanitizedData = sanitizeDataForFilters(dataForFilters, whenCondition);
const shifted: any = converterForSIFT(filter);
const whenSiftFunc = sift(shifted.when);
try {
isWhenSatisfied = whenSiftFunc(sanitizedData);
} catch (err) {
console.log(err);
return false;
}
if (isWhenSatisfied) {
return filter;
}
}
}
return false;
};
function converterForSIFT(obj) {
const cloneObj = cloneDeep(obj);
//structuredClone(obj); //cloneDeep
const { whenOperator, whenCondition } = cloneObj;
const filter = {};
if (whenCondition && Array.isArray(whenCondition) && whenCondition.length) {
filter['when'] = transform(whenOperator, whenCondition);
}
const result = filter;
return result;
}
export const RELATIONAL_OPERATORS = {
greater_than: {
pretty_name: 'Greater Than',
query: '$gt',
value_required: true,
sql_query: '>',
},
greater_than_or_equal: {
pretty_name: 'Greater than or equal',
query: '$gte',
value_required: true,
sql_query: '>=',
},
less_than: {
pretty_name: 'Less than',
query: '$lt',
value_required: true,
sql_query: '<',
},
less_than_or_equal: {
pretty_name: 'Less than or equal',
query: '$lte',
value_required: true,
sql_query: '<=',
},
equal_to: {
pretty_name: 'Equal to',
query: '$eq',
value_required: true,
sql_query: '=',
},
not_equal_to: {
pretty_name: 'Not Equal to',
query: '$ne',
value_required: true,
sql_query: '!=',
},
should_be_true: {
pretty_name: 'Should be TRUE',
query: '$eq',
value_required: false,
default_value: true,
sql_query: 'IS TRUE',
},
should_be_false: {
pretty_name: 'Should be FALSE',
query: '$eq',
value_required: false,
default_value: false,
sql_query: 'IS FALSE',
},
is_number: {
pretty_name: 'Is number',
query: '$type',
value_required: false,
default_value: 'number',
},
is_text: {
pretty_name: 'Is text',
query: '$type',
value_required: false,
default_value: 'string',
},
in: {
pretty_name: 'In Array/List',
query: '$in',
value_required: true,
render_type: 'TAG',
default_value: [],
sql_query: 'IN',
},
nin: {
pretty_name: 'Not In Array/List',
query: '$nin',
value_required: true,
render_type: 'TAG',
default_value: [],
sql_query: 'NOT IN',
},
is_required: {
pretty_name: 'Is required',
query: '$nin',
value_required: false,
default_value: [null, undefined, '', 0],
},
regex: {
pretty_name: 'Regex',
query: '$regex',
value_required: true,
default_value: null,
sql_query: 'LIKE',
},
should_exist: {
pretty_name: 'Should exist',
query: '$exists',
value_required: false,
default_value: true,
sql_query: 'IS NOT NULL',
},
should_not_exist: {
pretty_name: 'Should not exist',
query: '$exists',
value_required: false,
default_value: false,
sql_query: 'IS NULL',
},
};
export const LOGICAL_OPERATORS = {
AND: '$and',
OR: '$or',
};
function transform(operator, condition) {
let result: any = null;
const converter = (conditionObj) => {
const { field, relation, value } = conditionObj;
const relation_obj = RELATIONAL_OPERATORS[relation];
let relation_str: Object = {};
if (relation_obj.value_required) {
relation_str = {
[relation_obj.query]: value,
};
} else {
relation_str = {
[relation_obj.query]: relation_obj.default_value,
};
}
return {
[field]: relation_str,
};
};
if (!operator) {
//single value
const validationObj = condition[0];
result = converter(validationObj);
} else {
//multiple value, operator (whatOperator) is required
const requiredOperator = operator;
const logicalOperatorCondition: Object = {
[requiredOperator]: [],
};
for (const validationObj of condition) {
const convertedResult = validationObj.is_already_processed ? validationObj : converter(validationObj);
if (convertedResult.is_already_processed) {
delete convertedResult.is_already_processed;
}
logicalOperatorCondition[requiredOperator].push(convertedResult);
}
result = logicalOperatorCondition;
}
return result;
}