low-code-service
Version:
to handle workflow execution, handlebars execution and condition execution for low code service
512 lines (425 loc) • 15.2 kB
text/typescript
import * as HandleBars from 'handlebars';
const { parseBoolean } = require('../condition-executor/condition-handler');
const momentTimezone = require('moment-timezone');
const lodash = require('lodash');
function getEwayBillValues(ewayBill) {
const invoiceDate = lodash.get(ewayBill, 'invoice_date') || lodash.get(ewayBill, 'invoiceDate') || '';
const invoiceNumber = lodash.get(ewayBill, 'invoice_number') || lodash.get(ewayBill, 'invoiceNumber') || '';
const invoiceValue = lodash.get(ewayBill, 'invoiceValue') || lodash.get(ewayBill, 'invoice_amount') || lodash.get(ewayBill, 'invoice_value') || '';
const ewbNumber = lodash.get(ewayBill, 'ewb_number') || lodash.get(ewayBill, 'ewbNumber') || '';
const expiryDate = lodash.get(ewayBill, 'expiry_date') || lodash.get(ewayBill, 'validTill') || '';
const ewbCreationDate = lodash.get(ewayBill, 'creationTime') || lodash.get(ewayBill, 'creation_time') || '';
return {
invoiceDate,
invoiceNumber,
invoiceValue,
ewbNumber,
expiryDate,
ewbCreationDate,
};
}
// registering all the helpers for Handlerbar
(async function() {
// ifEquals helper for comparing two strings
HandleBars.registerHelper('ifEquals', (a, b) => {
return (a ? a.toUpperCase() : '') === b.toUpperCase();
});
// exists helper for checking value is some truthy value and not falsy
HandleBars.registerHelper('exists', (val) => {
return !(val === undefined || val === null);
});
// toUpperCase helper for converting string to uppper case
HandleBars.registerHelper('toUpperCase', (val) => {
return val.toUpperCase();
});
// toLowerCase helper for converting string to lower case
HandleBars.registerHelper('toLowerCase', (val) => {
return val.toLowerCase();
});
// "not" helper use with if
HandleBars.registerHelper('not', function(val) {
return !val;
});
HandleBars.registerHelper('lookupByIndex', function(array, index) {
return array[index];
});
HandleBars.registerHelper('multiply', function(value, multiplier) {
if (!value || !multiplier) {
return 0;
}
return (Number(value) * Number(multiplier)) || 0;
});
HandleBars.registerHelper('dateConvertor', function(value, fromFormat, fromTimezone, toFormat, toTimezone) {
let moment;
if (value) {
moment = momentTimezone(value, fromFormat, fromTimezone);
} else {
moment = momentTimezone();
}
moment = moment.tz(toTimezone);
if (toFormat === 'timestamp') {
return moment.valueOf();
}
return moment.format(toFormat);
});
HandleBars.registerHelper('base64Decoder', function(value) {
try {
const decoded = Buffer.from(value, 'base64').toString('utf-8');
return decoded;
} catch (err) {
return '';
}
});
HandleBars.registerHelper('base64Encoder', function(value) {
try {
const encoded = Buffer.from(value, 'utf-8').toString('base64');
return encoded;
} catch (err) {
return '';
}
});
HandleBars.registerHelper('getBasicAuth', function(username, password) {
try {
const buff = new Buffer(username + ':' + password);
const base64data = buff.toString('base64');
return base64data;
} catch (err) {
return null;
}
});
HandleBars.registerHelper('concat', function() {
return Array.prototype.slice.call(arguments, 0, -1).join('');
});
HandleBars.registerHelper('toNumber', function(value) {
return Number(value);
});
HandleBars.registerHelper('toString', function(value) {
return String(value);
});
HandleBars.registerHelper('toBoolean', function(value) {
return parseBoolean(value);
});
HandleBars.registerHelper('getEwayBillValues', function(ewayBill) {
return getEwayBillValues(ewayBill);
});
HandleBars.registerHelper('arrayLength', function(array) {
// Check if the array is not null or undefined
if (Array.isArray(array)) {
const length = array.length;
// Return falsy if the length is 0, otherwise return the length
return length === 0 ? '' : length;
}
// Return falsy if the input is not an array
return '';
});
HandleBars.registerHelper('sumKey', function(array, key) {
// Check if the array is valid and contains items
if (Array.isArray(array) && array.length > 0) {
// Sum the values of the specified key
return array.reduce((total, item) => {
// Ensure the key exists and is a number
const value = parseFloat(item[key]) || 0;
return total + value;
}, 0);
}
// Return a falsy value if the array is empty or invalid
return '';
});
HandleBars.registerHelper('getInvoiceValues', function(ewayBillList, consignmentExtraDetails) {
let invoiceNumber = '';
let invoiceDate = '';
let ewbNumber = '';
let invoiceAmount: any = '';
let expiryDate = '';
let ewbCreationDate = '';
if (Array.isArray(ewayBillList) && ewayBillList.length) {
const ewayBillValues = getEwayBillValues(ewayBillList[0]);
invoiceNumber = ewayBillValues.invoiceNumber;
invoiceDate = ewayBillValues.invoiceDate;
ewbNumber = ewayBillValues.ewbNumber;
invoiceAmount = ewayBillValues.invoiceValue;
expiryDate = ewayBillValues.expiryDate;
ewbCreationDate = ewayBillValues.ewbCreationDate;
}
if (consignmentExtraDetails.eway_bill) {
const ewayBillValues = getEwayBillValues(consignmentExtraDetails.eway_bill);
if (!invoiceNumber) invoiceNumber = ewayBillValues.invoiceNumber;
if (!invoiceDate) invoiceDate = ewayBillValues.invoiceDate;
if (!ewbNumber) ewbNumber = ewayBillValues.ewbNumber;
if (!expiryDate) expiryDate = ewayBillValues.expiryDate;
if (!ewbCreationDate) ewbCreationDate = ewayBillValues.ewbCreationDate;
}
if (!invoiceNumber) {
invoiceNumber = lodash.get(consignmentExtraDetails, 'invoice_number', '');
}
if (!invoiceDate) {
invoiceDate = lodash.get(consignmentExtraDetails, 'invoice_date', '');
}
if (!invoiceAmount) {
invoiceAmount = lodash.get(consignmentExtraDetails, 'invoice_amount', '');
}
return {
invoiceNumber,
invoiceDate,
ewbNumber,
invoiceAmount,
expiryDate,
ewbCreationDate,
};
});
HandleBars.registerHelper('sumProperty', function(piecesDetail, property) {
if (!piecesDetail || !Array.isArray(piecesDetail)) return 0;
return piecesDetail.reduce((sum, item) => {
if (!item) return sum;
const value = item[property];
if (value === undefined || value === null) return sum;
const numValue = parseFloat(value);
return sum + (isNaN(numValue) ? 0 : numValue);
}, 0);
});
HandleBars.registerHelper('getRegexMatch', function (str, pattern, groupIndex = 0, flags = '') {
if (typeof str !== 'string') {
return '';
}
if (str === '') {
return '';
}
let index: number;
if (typeof groupIndex === 'string') {
index = parseInt(groupIndex, 10);
} else if (typeof groupIndex === 'number') {
index = groupIndex;
} else {
return '';
}
if (isNaN(index)) {
return '';
}
try {
const patternStr = String(pattern);
const validFlags = flags.replace(/[^gimsuyd]/g, '');
const regex = new RegExp(patternStr, validFlags);
const match = str.match(regex);
if (match && match[index] !== undefined) {
return match[index];
} else {
return '';
}
} catch (error) {
return '';
}
});
HandleBars.registerHelper('arrayProperty', function (array, options) {
const {
operation,
property,
value,
order = 'asc',
dataType,
checkType = 'any'
} = options.hash;
if (!Array.isArray(array)) {
return operation === 'exists' ? false : [];
}
function getNestedValue(obj, path) {
return path.split('.').reduce((acc, key) => acc?.[key], obj);
}
function isValidValue(val, type) {
if (val == null) return false;
switch ((type || '').toLowerCase()) {
case 'string':
return typeof val === 'string' && val.trim() !== '';
case 'number':
return typeof val === 'number' && !isNaN(val);
case 'boolean':
return typeof val === 'boolean';
case 'array':
return Array.isArray(val) && val.length > 0;
case 'object':
return typeof val === 'object' && !Array.isArray(val) && Object.keys(val).length > 0;
case 'any':
default:
if (Array.isArray(val)) return val.length > 0;
if (typeof val === 'object') return Object.keys(val).length > 0;
return Boolean(val);
}
}
switch (operation) {
case 'exists':
if (checkType === 'every') {
return array.every(item => isValidValue(getNestedValue(item, property), dataType));
} else {
return array.some(item => isValidValue(getNestedValue(item, property), dataType));
}
case 'map':
return array.map(item => getNestedValue(item, property));
case 'filter':
return array.filter(item => getNestedValue(item, property) == value);
case 'find':
return array.find(item => getNestedValue(item, property) == value);
case 'sort':
return array.slice().sort((a, b) => {
const aVal = getNestedValue(a, property);
const bVal = getNestedValue(b, property);
if (aVal == null && bVal == null) return 0;
if (aVal == null) return 1;
if (bVal == null) return -1;
if (order === 'desc') {
return aVal < bVal ? 1 : aVal > bVal ? -1 : 0;
}
return aVal > bVal ? 1 : aVal < bVal ? -1 : 0;
});
case 'unique':
const seen = new Set();
return array.filter(item => {
const val = getNestedValue(item, property);
if (seen.has(val)) return false;
seen.add(val);
return true;
});
case 'sum':
return array.reduce((acc, item) => {
const val = getNestedValue(item, property);
const num = parseFloat(val);
return acc + (isNaN(num) ? 0 : num);
}, 0);
default:
return array;
}
});
HandleBars.registerHelper('trimToLength', function (str, maxLength) {
// Check if the string is shorter than or equal to the maxLength
if (typeof str !== 'string') {
return ''; // Return empty if input is not a string
}
if (str.length <= maxLength) {
return str; // If string is shorter or equal to maxLength, return the original string
}
// If string is longer than maxLength, trim it
return str.slice(0, maxLength);
});
// abs - returns the absolute value of a number
HandleBars.registerHelper('abs', function(value) {
const num = parseFloat(value);
return isNaN(num) ? 0 : Math.abs(num);
});
// int - converts value to integer
HandleBars.registerHelper('int', function(value) {
const num = parseInt(value, 10);
return isNaN(num) ? 0 : num;
});
// round - rounds a number to specified decimal places (default: 0)
HandleBars.registerHelper('round', function(value, decimalPlaces) {
const num = parseFloat(value);
if (isNaN(num)) return 0;
const places = parseInt(decimalPlaces, 10);
if (isNaN(places)) {
return Math.round(num); // Default to 0 decimal places
}
const multiplier = Math.pow(10, places);
return Math.round(num * multiplier) / multiplier;
});
// max - returns the maximum value from arguments or array, preserving original type
HandleBars.registerHelper('max', function() {
const args = Array.prototype.slice.call(arguments, 0, -1); // Remove options object
let values: any[] = [];
// If first argument is an array, use that
if (Array.isArray(args[0])) {
values = args[0];
} else {
// Otherwise, treat all arguments as values
values = args;
}
// Filter out null/undefined values and convert to numbers for comparison
const validValues = values.filter(val => val != null);
if (validValues.length === 0) return 0;
const numericValues = validValues.map(val => parseFloat(val)).filter(num => !isNaN(num));
if (numericValues.length === 0) return 0;
const maxNumeric = Math.max(...numericValues);
// Find the original value that corresponds to the maximum numeric value
const maxOriginal = validValues.find(val => parseFloat(val) === maxNumeric);
// Return the original value to preserve its type
return maxOriginal;
});
// min - returns the minimum value from arguments or array, preserving original type
HandleBars.registerHelper('min', function() {
const args = Array.prototype.slice.call(arguments, 0, -1); // Remove options object
let values: any[] = [];
// If first argument is an array, use that
if (Array.isArray(args[0])) {
values = args[0];
} else {
// Otherwise, treat all arguments as values
values = args;
}
// Filter out null/undefined values and convert to numbers for comparison
const validValues = values.filter(val => val != null);
if (validValues.length === 0) return 0;
const numericValues = validValues.map(val => parseFloat(val)).filter(num => !isNaN(num));
if (numericValues.length === 0) return 0;
const minNumeric = Math.min(...numericValues);
// Find the original value that corresponds to the minimum numeric value
const minOriginal = validValues.find(val => parseFloat(val) === minNumeric);
// Return the original value to preserve its type
return minOriginal;
});
// float - converts value to floating point number
HandleBars.registerHelper('float', function(value) {
const num = parseFloat(value);
return isNaN(num) ? 0.0 : num;
});
// {{addMinutesToCurrentTime "timestamp" 15 "Europe/Rome"}}
// addMinutesToCurrentTime - adds specified minutes to current time and returns in requested format with optional timezone
HandleBars.registerHelper('addMinutesToCurrentTime', function(timeFormat, minutesToAdd, timezone) {
const minutes = parseInt(minutesToAdd, 10) || 0;
let currentTime = momentTimezone();
// If timezone is provided, convert to that timezone
if (timezone) {
currentTime = currentTime.tz(timezone);
}
const newTime = currentTime.add(minutes, 'minutes');
if (timeFormat === 'timestamp') {
return newTime.valueOf();
}
return newTime.format(timeFormat);
});
// searchOne - searches for the first item in an array that matches the given criteria
HandleBars.registerHelper('searchOne', function(array, value) {
if (!Array.isArray(array)) {
return null;
}
return array.find(item => item === value) || null;
});
// all - checks if all items in an array have truthy values (for a property or the item itself)
HandleBars.registerHelper('all', function(array, property, options) {
if (!Array.isArray(array)) {
return false;
}
if (array.length === 0) {
return true; // vacuous truth - all items in empty array satisfy any condition
}
function getNestedValue(obj, path) {
return path.split('.').reduce((acc, key) => acc?.[key], obj);
}
function isTruthy(value) {
if(value){
return true;
}
return false;
}
return array.every(item => {
if (!item) return false;
const itemValue = property ? getNestedValue(item, property) : item;
return isTruthy(itemValue);
});
});
})();
export const getTemplateData = (template: string, data: any) => {
const compiledTemplate = HandleBars.compile(template);
return compiledTemplate(data);
};
export const cleanHandleBarTemplate = (dataToClean, defaultBody) => {
dataToClean = dataToClean.replace('\n', '');
dataToClean = dataToClean.replace('\t', '');
return dataToClean || defaultBody;
};