Skip to content

Commit

Permalink
wip: custom validation implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Skaiir committed Apr 10, 2024
1 parent 022ee05 commit 938a6be
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 30 deletions.
63 changes: 37 additions & 26 deletions packages/form-js-viewer/src/core/Validator.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { isNil, get, set } from 'min-dash';
import { countDecimals } from '../render/components/util/numberFieldUtil';
import Big from 'big.js';
import { clone } from '../util';
import { wrapObjectKeysWithUnderscores } from '../util/simple';

const EMAIL_PATTERN = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
const PHONE_PATTERN = /(\+|00)(297|93|244|1264|358|355|376|971|54|374|1684|1268|61|43|994|257|32|229|226|880|359|973|1242|387|590|375|501|1441|591|55|1246|673|975|267|236|1|61|41|56|86|225|237|243|242|682|57|269|238|506|53|5999|61|1345|357|420|49|253|1767|45|1809|1829|1849|213|593|20|291|212|34|372|251|358|679|500|33|298|691|241|44|995|44|233|350|224|590|220|245|240|30|1473|299|502|594|1671|592|852|504|385|509|36|62|44|91|246|353|98|964|354|972|39|1876|44|962|81|76|77|254|996|855|686|1869|82|383|965|856|961|231|218|1758|423|94|266|370|352|371|853|590|212|377|373|261|960|52|692|389|223|356|95|382|976|1670|258|222|1664|596|230|265|60|262|264|687|227|672|234|505|683|31|47|977|674|64|968|92|507|64|51|63|680|675|48|1787|1939|850|351|595|970|689|974|262|40|7|250|966|249|221|65|500|4779|677|232|503|378|252|508|381|211|239|597|421|386|46|268|1721|248|963|1649|235|228|66|992|690|993|670|676|1868|216|90|688|886|255|256|380|598|1|998|3906698|379|1784|58|1284|1340|84|678|681|685|967|27|260|263)(9[976]\d|8[987530]\d|6[987]\d|5[90]\d|42\d|3[875]\d|2[98654321]\d|9[8543210]|8[6421]|6[6543210]|5[87654321]|4[987654310]|3[9643210]|2[70]|7|1)\d{4,20}$/;

const VALIDATE_FEEL_PROPERTIES = [
const PRECOMPUTE_FEEL_PROPERTIES = [
'custom',
'min',
'max',
'minLength',
Expand Down Expand Up @@ -75,7 +78,7 @@ export class Validator {
return errors;
}

const evaluatedValidation = evaluateFEELValues(
const evaluatedValidation = precomputeFEELValues(
validate,
this._expressionLanguage,
this._conditionChecker,
Expand Down Expand Up @@ -144,6 +147,20 @@ export class Validator {
];
}

if ('custom' in evaluatedValidation && value && evaluatedValidation.custom.length) {
const { custom } = evaluatedValidation;
custom.forEach(customValidation => {
const { condition, message } = customValidation;

if (condition && !evaluateExpression(condition, this._expressionLanguage, this._conditionChecker, this._form, { value })) {
errors = [
...errors,
message
];
}
});
}

return errors;
}
}
Expand All @@ -153,35 +170,29 @@ Validator.$inject = [ 'expressionLanguage', 'conditionChecker', 'form' ];

// helpers //////////

/**
* Helper function to evaluate optional FEEL validation values.
*/
function evaluateFEELValues(validate, expressionLanguage, conditionChecker, form) {

const evaluatedValidate = { ...validate };

VALIDATE_FEEL_PROPERTIES.forEach(property => {
const path = property.split('.');
function precomputeFEELValues(_validate, expressionLanguage, conditionChecker, form) {

const value = get(evaluatedValidate, path);
const validate = clone(_validate);

// mirroring FEEL evaluation of our hooks
if (!expressionLanguage || !expressionLanguage.isExpression(value)) {
return value;
PRECOMPUTE_FEEL_PROPERTIES.forEach(propertyPath => {
const value = get(validate, propertyPath.split('.'));
const evaluatedValue = evaluateExpression(value, expressionLanguage, conditionChecker, form);
if (evaluatedValue) {
set(validate, propertyPath.split('.'), evaluatedValue);
}
});

const { initialData, data } = form._getState();

const newData = conditionChecker ? conditionChecker.applyConditions(data, data) : data;
const filteredData = { ...initialData, ...newData };
return validate;
}

const evaluatedValue = expressionLanguage.evaluate(value, filteredData);
function evaluateExpression(expression, expressionLanguage, conditionChecker, form, additionalContext = {}) {
if (!expressionLanguage || !expressionLanguage.isExpression(expression)) {
return expression;
}

// replace validate property with evaluated value
if (evaluatedValue) {
set(evaluatedValidate, path, evaluatedValue);
}
});
const { initialData, data } = form._getState();
const filteredData = conditionChecker ? conditionChecker.applyConditions(data, data) : data;
const expressionContext = { ...additionalContext, ...initialData, ...filteredData, ...wrapObjectKeysWithUnderscores(additionalContext) };

return evaluatedValidate;
return expressionLanguage.evaluate(expression, expressionContext);
}
6 changes: 2 additions & 4 deletions packages/form-js-viewer/src/util/simple.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export function buildExpressionContext(context) {
return {
...specialContextKeys,
...data,
..._wrapObjectKeysWithUnderscores(specialContextKeys)
...wrapObjectKeysWithUnderscores(specialContextKeys)
};
}

Expand All @@ -77,9 +77,7 @@ export function runRecursively(formField, fn) {
fn(formField);
}

// helpers //////////////////////

function _wrapObjectKeysWithUnderscores(obj) {
export function wrapObjectKeysWithUnderscores(obj) {
const newObj = {};
for (const [ key, value ] of Object.entries(obj)) {
newObj[`_${key}_`] = value;
Expand Down

0 comments on commit 938a6be

Please sign in to comment.