@remoteoss/json-schema-form 0.6.4-beta.0 → 0.6.5-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/index.cjs +474 -373
- package/dist/index.js +474 -373
- package/dist/standalone.js +1388 -1287
- package/package.json +1 -1
- package/src/tests/checkIfConditionMatches.test.js +6 -6
- package/src/tests/jsonLogic.fixtures.js +405 -1
- package/src/tests/jsonLogic.test.js +201 -42
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
/*!
|
|
3
3
|
Copyright (c) 2023 Remote Technology, Inc.
|
|
4
|
-
NPM Package: @remoteoss/json-schema-form@0.6.
|
|
5
|
-
Generated:
|
|
4
|
+
NPM Package: @remoteoss/json-schema-form@0.6.5-beta.0
|
|
5
|
+
Generated: Mon, 18 Sep 2023 10:09:01 GMT
|
|
6
6
|
|
|
7
7
|
MIT License
|
|
8
8
|
|
|
@@ -67,8 +67,8 @@ function hasProperty(object2, propertyName) {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
// src/checkIfConditionMatches.js
|
|
70
|
-
function
|
|
71
|
-
return Object.keys(node.if.properties).every((name) => {
|
|
70
|
+
function checkIfConditionMatchesProperties(node, formValues, formFields, logic) {
|
|
71
|
+
return Object.keys(node.if.properties ?? {}).every((name) => {
|
|
72
72
|
const currentProperty = node.if.properties[name];
|
|
73
73
|
const value = formValues[name];
|
|
74
74
|
const hasEmptyValue = typeof value === "undefined" || // NOTE: This is a "Remote API" dependency, as empty fields are sent as "null".
|
|
@@ -91,10 +91,11 @@ function checkIfConditionMatches(node, formValues, formFields) {
|
|
|
91
91
|
return currentProperty.enum.includes(value);
|
|
92
92
|
}
|
|
93
93
|
if (currentProperty.properties) {
|
|
94
|
-
return
|
|
94
|
+
return checkIfConditionMatchesProperties(
|
|
95
95
|
{ if: currentProperty },
|
|
96
96
|
formValues[name],
|
|
97
|
-
getField(name, formFields).fields
|
|
97
|
+
getField(name, formFields).fields,
|
|
98
|
+
logic
|
|
98
99
|
);
|
|
99
100
|
}
|
|
100
101
|
const field = getField(name, formFields);
|
|
@@ -110,6 +111,23 @@ function checkIfConditionMatches(node, formValues, formFields) {
|
|
|
110
111
|
);
|
|
111
112
|
});
|
|
112
113
|
}
|
|
114
|
+
function checkIfMatchesValidationsAndComputedValues(node, formValues, logic, parentID) {
|
|
115
|
+
const validationsMatch = Object.entries(node.if.validations ?? {}).every(([name, property]) => {
|
|
116
|
+
const currentValue = logic.getScope(parentID).applyValidationRuleInCondition(name, formValues);
|
|
117
|
+
if (Object.hasOwn(property, "const") && currentValue === property.const)
|
|
118
|
+
return true;
|
|
119
|
+
return false;
|
|
120
|
+
});
|
|
121
|
+
const computedValuesMatch = Object.entries(node.if.computedValues ?? {}).every(
|
|
122
|
+
([name, property]) => {
|
|
123
|
+
const currentValue = logic.getScope(parentID).applyComputedValueRuleInCondition(name, formValues);
|
|
124
|
+
if (Object.hasOwn(property, "const") && currentValue === property.const)
|
|
125
|
+
return true;
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
);
|
|
129
|
+
return computedValuesMatch && validationsMatch;
|
|
130
|
+
}
|
|
113
131
|
|
|
114
132
|
// src/internals/helpers.js
|
|
115
133
|
import merge from "lodash/fp/merge";
|
|
@@ -370,261 +388,14 @@ function _composeFieldCustomClosure(defaultComposeFn) {
|
|
|
370
388
|
};
|
|
371
389
|
}
|
|
372
390
|
|
|
391
|
+
// src/jsonLogic.js
|
|
392
|
+
import jsonLogic from "json-logic-js";
|
|
393
|
+
|
|
373
394
|
// src/yupSchema.js
|
|
374
395
|
import flow from "lodash/flow";
|
|
375
396
|
import noop from "lodash/noop";
|
|
376
397
|
import { randexp } from "randexp";
|
|
377
398
|
import { string, number, boolean, object, array } from "yup";
|
|
378
|
-
|
|
379
|
-
// src/jsonLogic.js
|
|
380
|
-
import jsonLogic from "json-logic-js";
|
|
381
|
-
function createValidationChecker(schema) {
|
|
382
|
-
const scopes = /* @__PURE__ */ new Map();
|
|
383
|
-
function createScopes(jsonSchema, key = "root") {
|
|
384
|
-
const sampleEmptyObject = buildSampleEmptyObject(schema);
|
|
385
|
-
scopes.set(key, createValidationsScope(jsonSchema));
|
|
386
|
-
Object.entries(jsonSchema?.properties ?? {}).filter(([, property]) => property.type === "object" || property.type === "array").forEach(([key2, property]) => {
|
|
387
|
-
if (property.type === "array") {
|
|
388
|
-
createScopes(property.items, `${key2}[]`);
|
|
389
|
-
} else {
|
|
390
|
-
createScopes(property, key2);
|
|
391
|
-
}
|
|
392
|
-
});
|
|
393
|
-
validateInlineRules(jsonSchema, sampleEmptyObject);
|
|
394
|
-
}
|
|
395
|
-
createScopes(schema);
|
|
396
|
-
return {
|
|
397
|
-
scopes,
|
|
398
|
-
getScope(name = "root") {
|
|
399
|
-
return scopes.get(name);
|
|
400
|
-
}
|
|
401
|
-
};
|
|
402
|
-
}
|
|
403
|
-
function createValidationsScope(schema) {
|
|
404
|
-
const validationMap = /* @__PURE__ */ new Map();
|
|
405
|
-
const computedValuesMap = /* @__PURE__ */ new Map();
|
|
406
|
-
const logic = schema?.["x-jsf-logic"] ?? {
|
|
407
|
-
validations: {},
|
|
408
|
-
computedValues: {}
|
|
409
|
-
};
|
|
410
|
-
const validations = Object.entries(logic.validations ?? {});
|
|
411
|
-
const computedValues = Object.entries(logic.computedValues ?? {});
|
|
412
|
-
const sampleEmptyObject = buildSampleEmptyObject(schema);
|
|
413
|
-
validations.forEach(([id, validation]) => {
|
|
414
|
-
if (!validation.rule) {
|
|
415
|
-
throw Error(`[json-schema-form] json-logic error: Validation "${id}" has missing rule.`);
|
|
416
|
-
}
|
|
417
|
-
checkRuleIntegrity(validation.rule, id, sampleEmptyObject);
|
|
418
|
-
validationMap.set(id, validation);
|
|
419
|
-
});
|
|
420
|
-
computedValues.forEach(([id, computedValue]) => {
|
|
421
|
-
if (!computedValue.rule) {
|
|
422
|
-
throw Error(`[json-schema-form] json-logic error: Computed value "${id}" has missing rule.`);
|
|
423
|
-
}
|
|
424
|
-
checkRuleIntegrity(computedValue.rule, id, sampleEmptyObject);
|
|
425
|
-
computedValuesMap.set(id, computedValue);
|
|
426
|
-
});
|
|
427
|
-
function validate(rule, values) {
|
|
428
|
-
return jsonLogic.apply(rule, replaceUndefinedValuesWithNulls(values));
|
|
429
|
-
}
|
|
430
|
-
return {
|
|
431
|
-
validationMap,
|
|
432
|
-
computedValuesMap,
|
|
433
|
-
validate,
|
|
434
|
-
applyValidationRuleInCondition(id, values) {
|
|
435
|
-
const validation = validationMap.get(id);
|
|
436
|
-
return validate(validation.rule, values);
|
|
437
|
-
},
|
|
438
|
-
applyComputedValueInField(id, values, fieldName) {
|
|
439
|
-
const validation = computedValuesMap.get(id);
|
|
440
|
-
if (validation === void 0) {
|
|
441
|
-
throw Error(
|
|
442
|
-
`[json-schema-form] json-logic error: Computed value "${id}" doesn't exist in field "${fieldName}".`
|
|
443
|
-
);
|
|
444
|
-
}
|
|
445
|
-
return validate(validation.rule, values);
|
|
446
|
-
},
|
|
447
|
-
applyComputedValueRuleInCondition(id, values) {
|
|
448
|
-
const validation = computedValuesMap.get(id);
|
|
449
|
-
return validate(validation.rule, values);
|
|
450
|
-
}
|
|
451
|
-
};
|
|
452
|
-
}
|
|
453
|
-
function replaceUndefinedValuesWithNulls(values = {}) {
|
|
454
|
-
return Object.entries(values).reduce((prev, [key, value]) => {
|
|
455
|
-
return { ...prev, [key]: value === void 0 ? null : value };
|
|
456
|
-
}, {});
|
|
457
|
-
}
|
|
458
|
-
function yupSchemaWithCustomJSONLogic({ field, logic, config, id }) {
|
|
459
|
-
const { parentID = "root" } = config;
|
|
460
|
-
const validation = logic.getScope(parentID).validationMap.get(id);
|
|
461
|
-
if (validation === void 0) {
|
|
462
|
-
throw Error(
|
|
463
|
-
`[json-schema-form] json-logic error: "${field.name}" required validation "${id}" doesn't exist.`
|
|
464
|
-
);
|
|
465
|
-
}
|
|
466
|
-
return (yupSchema) => yupSchema.test(
|
|
467
|
-
`${field.name}-validation-${id}`,
|
|
468
|
-
validation?.errorMessage ?? "This field is invalid.",
|
|
469
|
-
(value, { parent }) => {
|
|
470
|
-
if (value === void 0 && !field.required)
|
|
471
|
-
return true;
|
|
472
|
-
return jsonLogic.apply(validation.rule, parent);
|
|
473
|
-
}
|
|
474
|
-
);
|
|
475
|
-
}
|
|
476
|
-
var HANDLEBARS_REGEX = /\{\{([^{}]+)\}\}/g;
|
|
477
|
-
function replaceHandlebarsTemplates({
|
|
478
|
-
value: toReplace,
|
|
479
|
-
logic,
|
|
480
|
-
formValues,
|
|
481
|
-
parentID,
|
|
482
|
-
name: fieldName
|
|
483
|
-
}) {
|
|
484
|
-
if (typeof toReplace === "string") {
|
|
485
|
-
return toReplace.replace(HANDLEBARS_REGEX, (match, key) => {
|
|
486
|
-
return logic.getScope(parentID).applyComputedValueInField(key.trim(), formValues, fieldName);
|
|
487
|
-
});
|
|
488
|
-
} else if (typeof toReplace === "object") {
|
|
489
|
-
const { value, ...rules } = toReplace;
|
|
490
|
-
if (Object.keys(rules).length > 1 && !value) {
|
|
491
|
-
throw Error("Cannot define multiple rules without a template string with key `value`.");
|
|
492
|
-
}
|
|
493
|
-
const computedTemplateValue = Object.entries(rules).reduce((prev, [key, rule]) => {
|
|
494
|
-
const computedValue = logic.getScope(parentID).validate(rule, formValues);
|
|
495
|
-
return prev.replaceAll(`{{${key}}}`, computedValue);
|
|
496
|
-
}, value);
|
|
497
|
-
return computedTemplateValue.replace(/\{\{([^{}]+)\}\}/g, (match, key) => {
|
|
498
|
-
return logic.getScope(parentID).applyComputedValueInField(key.trim(), formValues, fieldName);
|
|
499
|
-
});
|
|
500
|
-
}
|
|
501
|
-
return toReplace;
|
|
502
|
-
}
|
|
503
|
-
function calculateComputedAttributes(fieldParams, { parentID = "root" } = {}) {
|
|
504
|
-
return ({ logic, isRequired, config, formValues }) => {
|
|
505
|
-
const { name, computedAttributes } = fieldParams;
|
|
506
|
-
const attributes = Object.fromEntries(
|
|
507
|
-
Object.entries(computedAttributes).map(handleComputedAttribute(logic, formValues, parentID, name)).filter(([, value]) => value !== null)
|
|
508
|
-
);
|
|
509
|
-
return {
|
|
510
|
-
...attributes,
|
|
511
|
-
schema: buildYupSchema(
|
|
512
|
-
{ ...fieldParams, ...attributes, required: isRequired },
|
|
513
|
-
config,
|
|
514
|
-
logic
|
|
515
|
-
)
|
|
516
|
-
};
|
|
517
|
-
};
|
|
518
|
-
}
|
|
519
|
-
function handleComputedAttribute(logic, formValues, parentID, name) {
|
|
520
|
-
return ([key, value]) => {
|
|
521
|
-
switch (key) {
|
|
522
|
-
case "description":
|
|
523
|
-
return [key, replaceHandlebarsTemplates({ value, logic, formValues, parentID, name })];
|
|
524
|
-
case "title":
|
|
525
|
-
return ["label", replaceHandlebarsTemplates({ value, logic, formValues, parentID, name })];
|
|
526
|
-
case "x-jsf-errorMessage":
|
|
527
|
-
return [
|
|
528
|
-
"errorMessage",
|
|
529
|
-
handleNestedObjectForComputedValues(value, formValues, parentID, logic, name)
|
|
530
|
-
];
|
|
531
|
-
case "x-jsf-presentation": {
|
|
532
|
-
if (value.statement) {
|
|
533
|
-
return [
|
|
534
|
-
"statement",
|
|
535
|
-
handleNestedObjectForComputedValues(value.statement, formValues, parentID, logic, name)
|
|
536
|
-
];
|
|
537
|
-
}
|
|
538
|
-
return [
|
|
539
|
-
key,
|
|
540
|
-
handleNestedObjectForComputedValues(value.statement, formValues, parentID, logic, name)
|
|
541
|
-
];
|
|
542
|
-
}
|
|
543
|
-
case "const":
|
|
544
|
-
default: {
|
|
545
|
-
if (typeof value === "object" && value.rule) {
|
|
546
|
-
return [key, logic.getScope(parentID).validate(value.rule, formValues)];
|
|
547
|
-
}
|
|
548
|
-
return [key, logic.getScope(parentID).applyComputedValueInField(value, formValues, name)];
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
};
|
|
552
|
-
}
|
|
553
|
-
function handleNestedObjectForComputedValues(values, formValues, parentID, logic, name) {
|
|
554
|
-
return Object.fromEntries(
|
|
555
|
-
Object.entries(values).map(([key, value]) => {
|
|
556
|
-
return [key, replaceHandlebarsTemplates({ value, logic, formValues, parentID, name })];
|
|
557
|
-
})
|
|
558
|
-
);
|
|
559
|
-
}
|
|
560
|
-
function buildSampleEmptyObject(schema = {}) {
|
|
561
|
-
const sample = {};
|
|
562
|
-
if (typeof schema !== "object" || !schema.properties) {
|
|
563
|
-
return schema;
|
|
564
|
-
}
|
|
565
|
-
for (const key in schema.properties) {
|
|
566
|
-
if (schema.properties[key].type === "object") {
|
|
567
|
-
sample[key] = buildSampleEmptyObject(schema.properties[key]);
|
|
568
|
-
} else if (schema.properties[key].type === "array") {
|
|
569
|
-
const itemSchema = schema.properties[key].items;
|
|
570
|
-
sample[key] = buildSampleEmptyObject(itemSchema);
|
|
571
|
-
} else {
|
|
572
|
-
sample[key] = true;
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
return sample;
|
|
576
|
-
}
|
|
577
|
-
function validateInlineRules(jsonSchema, sampleEmptyObject) {
|
|
578
|
-
const properties = (jsonSchema?.properties || jsonSchema?.items?.properties) ?? {};
|
|
579
|
-
Object.entries(properties).filter(([, property]) => property["x-jsf-logic-computedAttrs"] !== void 0).forEach(([fieldName, property]) => {
|
|
580
|
-
Object.entries(property["x-jsf-logic-computedAttrs"]).filter(([, value]) => typeof value === "object").forEach(([key, item]) => {
|
|
581
|
-
Object.values(item).forEach((rule) => {
|
|
582
|
-
checkRuleIntegrity(
|
|
583
|
-
rule,
|
|
584
|
-
fieldName,
|
|
585
|
-
sampleEmptyObject,
|
|
586
|
-
(item2) => `[json-schema-form] json-logic error: fieldName "${item2.var}" doesn't exist in field "${fieldName}.x-jsf-logic-computedAttrs.${key}".`
|
|
587
|
-
);
|
|
588
|
-
});
|
|
589
|
-
});
|
|
590
|
-
});
|
|
591
|
-
}
|
|
592
|
-
function checkRuleIntegrity(rule, id, data, errorMessage = (item) => `[json-schema-form] json-logic error: rule "${id}" has no variable "${item.var}".`) {
|
|
593
|
-
Object.entries(rule ?? {}).map(([operator, subRule]) => {
|
|
594
|
-
if (!Array.isArray(subRule) && subRule !== null && subRule !== void 0)
|
|
595
|
-
return;
|
|
596
|
-
throwIfUnknownOperator(operator, subRule, id);
|
|
597
|
-
subRule.map((item) => {
|
|
598
|
-
const isVar = item !== null && typeof item === "object" && Object.hasOwn(item, "var");
|
|
599
|
-
if (isVar) {
|
|
600
|
-
const exists = jsonLogic.apply({ var: removeIndicesFromPath(item.var) }, data);
|
|
601
|
-
if (exists === null) {
|
|
602
|
-
throw Error(errorMessage(item));
|
|
603
|
-
}
|
|
604
|
-
} else {
|
|
605
|
-
checkRuleIntegrity(item, id, data);
|
|
606
|
-
}
|
|
607
|
-
});
|
|
608
|
-
});
|
|
609
|
-
}
|
|
610
|
-
function throwIfUnknownOperator(operator, subRule, id) {
|
|
611
|
-
try {
|
|
612
|
-
jsonLogic.apply({ [operator]: subRule });
|
|
613
|
-
} catch (e) {
|
|
614
|
-
if (e.message === `Unrecognized operation ${operator}`) {
|
|
615
|
-
throw Error(
|
|
616
|
-
`[json-schema-form] json-logic error: in "${id}" rule there is an unknown operator "${operator}".`
|
|
617
|
-
);
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
var regexToGetIndices = /\.\d+\./g;
|
|
622
|
-
function removeIndicesFromPath(path) {
|
|
623
|
-
const intermediatePath = path.replace(regexToGetIndices, ".");
|
|
624
|
-
return intermediatePath.replace(/\.\d+$/, "");
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
// src/yupSchema.js
|
|
628
399
|
var DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
|
|
629
400
|
var baseString = string().trim();
|
|
630
401
|
var todayDateHint = (/* @__PURE__ */ new Date()).toISOString().substring(0, 10);
|
|
@@ -820,118 +591,414 @@ function buildYupSchema(field, config, logic) {
|
|
|
820
591
|
}) : true
|
|
821
592
|
);
|
|
822
593
|
}
|
|
823
|
-
function withConst(yupSchema) {
|
|
824
|
-
return yupSchema.test(
|
|
825
|
-
"isConst",
|
|
826
|
-
errorMessage.const ?? errorMessageFromConfig.const ?? `The only accepted value is ${propertyFields.const}.`,
|
|
827
|
-
(value) => propertyFields.required === false && value === void 0 || value === null || value === propertyFields.const
|
|
594
|
+
function withConst(yupSchema) {
|
|
595
|
+
return yupSchema.test(
|
|
596
|
+
"isConst",
|
|
597
|
+
errorMessage.const ?? errorMessageFromConfig.const ?? `The only accepted value is ${propertyFields.const}.`,
|
|
598
|
+
(value) => propertyFields.required === false && value === void 0 || value === null || value === propertyFields.const
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
function withBaseSchema() {
|
|
602
|
+
const customErrorMsg = errorMessage.type || errorMessageFromConfig.type;
|
|
603
|
+
if (customErrorMsg) {
|
|
604
|
+
return baseSchema.typeError(customErrorMsg);
|
|
605
|
+
}
|
|
606
|
+
return baseSchema;
|
|
607
|
+
}
|
|
608
|
+
function buildFieldSetSchema(innerFields) {
|
|
609
|
+
const fieldSetShape = {};
|
|
610
|
+
innerFields.forEach((fieldSetfield) => {
|
|
611
|
+
if (fieldSetfield.fields) {
|
|
612
|
+
fieldSetShape[fieldSetfield.name] = object().shape(
|
|
613
|
+
buildFieldSetSchema(fieldSetfield.fields)
|
|
614
|
+
);
|
|
615
|
+
} else {
|
|
616
|
+
fieldSetShape[fieldSetfield.name] = buildYupSchema(
|
|
617
|
+
{
|
|
618
|
+
...fieldSetfield,
|
|
619
|
+
inputType: fieldSetfield.type
|
|
620
|
+
},
|
|
621
|
+
config
|
|
622
|
+
)();
|
|
623
|
+
}
|
|
624
|
+
});
|
|
625
|
+
return fieldSetShape;
|
|
626
|
+
}
|
|
627
|
+
function buildGroupArraySchema() {
|
|
628
|
+
return object().shape(
|
|
629
|
+
propertyFields.nthFieldGroup.fields().reduce(
|
|
630
|
+
(schema, groupArrayField) => ({
|
|
631
|
+
...schema,
|
|
632
|
+
[groupArrayField.name]: buildYupSchema(groupArrayField, config)()
|
|
633
|
+
}),
|
|
634
|
+
{}
|
|
635
|
+
)
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
const validators = [withBaseSchema];
|
|
639
|
+
if (inputType === supportedTypes.GROUP_ARRAY) {
|
|
640
|
+
validators[0] = () => withBaseSchema().of(buildGroupArraySchema());
|
|
641
|
+
} else if (inputType === supportedTypes.FIELDSET) {
|
|
642
|
+
validators[0] = () => withBaseSchema().shape(buildFieldSetSchema(propertyFields.fields));
|
|
643
|
+
}
|
|
644
|
+
if (propertyFields.required) {
|
|
645
|
+
validators.push(withRequired);
|
|
646
|
+
}
|
|
647
|
+
if (typeof propertyFields.minimum !== "undefined") {
|
|
648
|
+
validators.push(withMin);
|
|
649
|
+
}
|
|
650
|
+
if (typeof propertyFields.minLength !== "undefined") {
|
|
651
|
+
validators.push(withMinLength);
|
|
652
|
+
}
|
|
653
|
+
if (propertyFields.maximum !== void 0) {
|
|
654
|
+
validators.push(withMax);
|
|
655
|
+
}
|
|
656
|
+
if (propertyFields.maxLength) {
|
|
657
|
+
validators.push(withMaxLength);
|
|
658
|
+
}
|
|
659
|
+
if (propertyFields.pattern) {
|
|
660
|
+
validators.push(withMatches);
|
|
661
|
+
}
|
|
662
|
+
if (propertyFields.maxFileSize) {
|
|
663
|
+
validators.push(withMaxFileSize);
|
|
664
|
+
}
|
|
665
|
+
if (propertyFields.accept) {
|
|
666
|
+
validators.push(withFileFormat);
|
|
667
|
+
}
|
|
668
|
+
if (propertyFields.const) {
|
|
669
|
+
validators.push(withConst);
|
|
670
|
+
}
|
|
671
|
+
if (propertyFields.jsonLogicValidations) {
|
|
672
|
+
propertyFields.jsonLogicValidations.forEach(
|
|
673
|
+
(id) => validators.push(yupSchemaWithCustomJSONLogic({ field, id, logic, config }))
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
return flow(validators);
|
|
677
|
+
}
|
|
678
|
+
function getNoSortEdges(fields = []) {
|
|
679
|
+
return fields.reduce((list, field) => {
|
|
680
|
+
if (field.noSortEdges) {
|
|
681
|
+
list.push(field.name);
|
|
682
|
+
}
|
|
683
|
+
return list;
|
|
684
|
+
}, []);
|
|
685
|
+
}
|
|
686
|
+
function getSchema(fields = [], config) {
|
|
687
|
+
const newSchema = {};
|
|
688
|
+
fields.forEach((field) => {
|
|
689
|
+
if (field.schema) {
|
|
690
|
+
if (field.name) {
|
|
691
|
+
if (field.inputType === supportedTypes.FIELDSET) {
|
|
692
|
+
const fieldsetSchema = buildYupSchema(field, config)();
|
|
693
|
+
newSchema[field.name] = fieldsetSchema;
|
|
694
|
+
} else {
|
|
695
|
+
newSchema[field.name] = field.schema;
|
|
696
|
+
}
|
|
697
|
+
} else {
|
|
698
|
+
Object.assign(newSchema, getSchema(field.fields, config));
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
});
|
|
702
|
+
return newSchema;
|
|
703
|
+
}
|
|
704
|
+
function buildCompleteYupSchema(fields, config) {
|
|
705
|
+
return object().shape(getSchema(fields, config), getNoSortEdges(fields));
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// src/jsonLogic.js
|
|
709
|
+
function createValidationChecker(schema) {
|
|
710
|
+
const scopes = /* @__PURE__ */ new Map();
|
|
711
|
+
function createScopes(jsonSchema, key = "root") {
|
|
712
|
+
const sampleEmptyObject = buildSampleEmptyObject(schema);
|
|
713
|
+
scopes.set(key, createValidationsScope(jsonSchema));
|
|
714
|
+
Object.entries(jsonSchema?.properties ?? {}).filter(([, property]) => property.type === "object" || property.type === "array").forEach(([key2, property]) => {
|
|
715
|
+
if (property.type === "array") {
|
|
716
|
+
createScopes(property.items, `${key2}[]`);
|
|
717
|
+
} else {
|
|
718
|
+
createScopes(property, key2);
|
|
719
|
+
}
|
|
720
|
+
});
|
|
721
|
+
validateInlineRules(jsonSchema, sampleEmptyObject);
|
|
722
|
+
}
|
|
723
|
+
createScopes(schema);
|
|
724
|
+
return {
|
|
725
|
+
scopes,
|
|
726
|
+
getScope(name = "root") {
|
|
727
|
+
return scopes.get(name);
|
|
728
|
+
}
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
function createValidationsScope(schema) {
|
|
732
|
+
const validationMap = /* @__PURE__ */ new Map();
|
|
733
|
+
const computedValuesMap = /* @__PURE__ */ new Map();
|
|
734
|
+
const logic = schema?.["x-jsf-logic"] ?? {
|
|
735
|
+
validations: {},
|
|
736
|
+
computedValues: {}
|
|
737
|
+
};
|
|
738
|
+
const validations = Object.entries(logic.validations ?? {});
|
|
739
|
+
const computedValues = Object.entries(logic.computedValues ?? {});
|
|
740
|
+
const sampleEmptyObject = buildSampleEmptyObject(schema);
|
|
741
|
+
validations.forEach(([id, validation]) => {
|
|
742
|
+
if (!validation.rule) {
|
|
743
|
+
throw Error(`[json-schema-form] json-logic error: Validation "${id}" has missing rule.`);
|
|
744
|
+
}
|
|
745
|
+
checkRuleIntegrity(validation.rule, id, sampleEmptyObject);
|
|
746
|
+
validationMap.set(id, validation);
|
|
747
|
+
});
|
|
748
|
+
computedValues.forEach(([id, computedValue]) => {
|
|
749
|
+
if (!computedValue.rule) {
|
|
750
|
+
throw Error(`[json-schema-form] json-logic error: Computed value "${id}" has missing rule.`);
|
|
751
|
+
}
|
|
752
|
+
checkRuleIntegrity(computedValue.rule, id, sampleEmptyObject);
|
|
753
|
+
computedValuesMap.set(id, computedValue);
|
|
754
|
+
});
|
|
755
|
+
function validate(rule, values) {
|
|
756
|
+
return jsonLogic.apply(
|
|
757
|
+
rule,
|
|
758
|
+
replaceUndefinedValuesWithNulls({ ...sampleEmptyObject, ...values })
|
|
759
|
+
);
|
|
760
|
+
}
|
|
761
|
+
return {
|
|
762
|
+
validationMap,
|
|
763
|
+
computedValuesMap,
|
|
764
|
+
validate,
|
|
765
|
+
applyValidationRuleInCondition(id, values) {
|
|
766
|
+
const validation = validationMap.get(id);
|
|
767
|
+
return validate(validation.rule, values);
|
|
768
|
+
},
|
|
769
|
+
applyComputedValueInField(id, values, fieldName) {
|
|
770
|
+
const validation = computedValuesMap.get(id);
|
|
771
|
+
if (validation === void 0) {
|
|
772
|
+
throw Error(
|
|
773
|
+
`[json-schema-form] json-logic error: Computed value "${id}" doesn't exist in field "${fieldName}".`
|
|
774
|
+
);
|
|
775
|
+
}
|
|
776
|
+
return validate(validation.rule, values);
|
|
777
|
+
},
|
|
778
|
+
applyComputedValueRuleInCondition(id, values) {
|
|
779
|
+
const validation = computedValuesMap.get(id);
|
|
780
|
+
return validate(validation.rule, values);
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
function replaceUndefinedValuesWithNulls(values = {}) {
|
|
785
|
+
return Object.entries(values).reduce((prev, [key, value]) => {
|
|
786
|
+
return { ...prev, [key]: value === void 0 || value === null ? NaN : value };
|
|
787
|
+
}, {});
|
|
788
|
+
}
|
|
789
|
+
function yupSchemaWithCustomJSONLogic({ field, logic, config, id }) {
|
|
790
|
+
const { parentID = "root" } = config;
|
|
791
|
+
const validation = logic.getScope(parentID).validationMap.get(id);
|
|
792
|
+
if (validation === void 0) {
|
|
793
|
+
throw Error(
|
|
794
|
+
`[json-schema-form] json-logic error: "${field.name}" required validation "${id}" doesn't exist.`
|
|
795
|
+
);
|
|
796
|
+
}
|
|
797
|
+
return (yupSchema) => yupSchema.test(
|
|
798
|
+
`${field.name}-validation-${id}`,
|
|
799
|
+
validation?.errorMessage ?? "This field is invalid.",
|
|
800
|
+
(value, { parent }) => {
|
|
801
|
+
if (value === void 0 && !field.required)
|
|
802
|
+
return true;
|
|
803
|
+
return jsonLogic.apply(validation.rule, parent);
|
|
804
|
+
}
|
|
805
|
+
);
|
|
806
|
+
}
|
|
807
|
+
var HANDLEBARS_REGEX = /\{\{([^{}]+)\}\}/g;
|
|
808
|
+
function replaceHandlebarsTemplates({
|
|
809
|
+
value: toReplace,
|
|
810
|
+
logic,
|
|
811
|
+
formValues,
|
|
812
|
+
parentID,
|
|
813
|
+
name: fieldName
|
|
814
|
+
}) {
|
|
815
|
+
if (typeof toReplace === "string") {
|
|
816
|
+
return toReplace.replace(HANDLEBARS_REGEX, (match, key) => {
|
|
817
|
+
return logic.getScope(parentID).applyComputedValueInField(key.trim(), formValues, fieldName);
|
|
818
|
+
});
|
|
819
|
+
} else if (typeof toReplace === "object") {
|
|
820
|
+
const { value, ...rules } = toReplace;
|
|
821
|
+
if (Object.keys(rules).length > 1 && !value) {
|
|
822
|
+
throw Error("Cannot define multiple rules without a template string with key `value`.");
|
|
823
|
+
}
|
|
824
|
+
const computedTemplateValue = Object.entries(rules).reduce((prev, [key, rule]) => {
|
|
825
|
+
const computedValue = logic.getScope(parentID).validate(rule, formValues);
|
|
826
|
+
return prev.replaceAll(`{{${key}}}`, computedValue);
|
|
827
|
+
}, value);
|
|
828
|
+
return computedTemplateValue.replace(/\{\{([^{}]+)\}\}/g, (match, key) => {
|
|
829
|
+
return logic.getScope(parentID).applyComputedValueInField(key.trim(), formValues, fieldName);
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
return toReplace;
|
|
833
|
+
}
|
|
834
|
+
function calculateComputedAttributes(fieldParams, { parentID = "root" } = {}) {
|
|
835
|
+
return ({ logic, isRequired, config, formValues }) => {
|
|
836
|
+
const { name, computedAttributes } = fieldParams;
|
|
837
|
+
const attributes = Object.fromEntries(
|
|
838
|
+
Object.entries(computedAttributes).map(handleComputedAttribute(logic, formValues, parentID, name)).filter(([, value]) => value !== null)
|
|
828
839
|
);
|
|
840
|
+
return {
|
|
841
|
+
...attributes,
|
|
842
|
+
schema: buildYupSchema(
|
|
843
|
+
{ ...fieldParams, ...attributes, required: isRequired },
|
|
844
|
+
config,
|
|
845
|
+
logic
|
|
846
|
+
)
|
|
847
|
+
};
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
function handleComputedAttribute(logic, formValues, parentID, name) {
|
|
851
|
+
return ([key, value]) => {
|
|
852
|
+
switch (key) {
|
|
853
|
+
case "description":
|
|
854
|
+
return [key, replaceHandlebarsTemplates({ value, logic, formValues, parentID, name })];
|
|
855
|
+
case "title":
|
|
856
|
+
return ["label", replaceHandlebarsTemplates({ value, logic, formValues, parentID, name })];
|
|
857
|
+
case "x-jsf-errorMessage":
|
|
858
|
+
return [
|
|
859
|
+
"errorMessage",
|
|
860
|
+
handleNestedObjectForComputedValues(value, formValues, parentID, logic, name)
|
|
861
|
+
];
|
|
862
|
+
case "x-jsf-presentation": {
|
|
863
|
+
if (value.statement) {
|
|
864
|
+
return [
|
|
865
|
+
"statement",
|
|
866
|
+
handleNestedObjectForComputedValues(value.statement, formValues, parentID, logic, name)
|
|
867
|
+
];
|
|
868
|
+
}
|
|
869
|
+
return [
|
|
870
|
+
key,
|
|
871
|
+
handleNestedObjectForComputedValues(value.statement, formValues, parentID, logic, name)
|
|
872
|
+
];
|
|
873
|
+
}
|
|
874
|
+
case "const":
|
|
875
|
+
default: {
|
|
876
|
+
if (typeof value === "object" && value.rule) {
|
|
877
|
+
return [key, logic.getScope(parentID).validate(value.rule, formValues)];
|
|
878
|
+
}
|
|
879
|
+
return [key, logic.getScope(parentID).applyComputedValueInField(value, formValues, name)];
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
function handleNestedObjectForComputedValues(values, formValues, parentID, logic, name) {
|
|
885
|
+
return Object.fromEntries(
|
|
886
|
+
Object.entries(values).map(([key, value]) => {
|
|
887
|
+
return [key, replaceHandlebarsTemplates({ value, logic, formValues, parentID, name })];
|
|
888
|
+
})
|
|
889
|
+
);
|
|
890
|
+
}
|
|
891
|
+
function buildSampleEmptyObject(schema = {}) {
|
|
892
|
+
const sample = {};
|
|
893
|
+
if (typeof schema !== "object" || !schema.properties) {
|
|
894
|
+
return schema;
|
|
829
895
|
}
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
896
|
+
for (const key in schema.properties) {
|
|
897
|
+
if (schema.properties[key].type === "object") {
|
|
898
|
+
sample[key] = buildSampleEmptyObject(schema.properties[key]);
|
|
899
|
+
} else if (schema.properties[key].type === "array") {
|
|
900
|
+
const itemSchema = schema.properties[key].items;
|
|
901
|
+
sample[key] = buildSampleEmptyObject(itemSchema);
|
|
902
|
+
} else {
|
|
903
|
+
sample[key] = true;
|
|
834
904
|
}
|
|
835
|
-
return baseSchema;
|
|
836
905
|
}
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
906
|
+
return sample;
|
|
907
|
+
}
|
|
908
|
+
function validateInlineRules(jsonSchema, sampleEmptyObject) {
|
|
909
|
+
const properties = (jsonSchema?.properties || jsonSchema?.items?.properties) ?? {};
|
|
910
|
+
Object.entries(properties).filter(([, property]) => property["x-jsf-logic-computedAttrs"] !== void 0).forEach(([fieldName, property]) => {
|
|
911
|
+
Object.entries(property["x-jsf-logic-computedAttrs"]).filter(([, value]) => typeof value === "object").forEach(([key, item]) => {
|
|
912
|
+
Object.values(item).forEach((rule) => {
|
|
913
|
+
checkRuleIntegrity(
|
|
914
|
+
rule,
|
|
915
|
+
fieldName,
|
|
916
|
+
sampleEmptyObject,
|
|
917
|
+
(item2) => `[json-schema-form] json-logic error: fieldName "${item2.var}" doesn't exist in field "${fieldName}.x-jsf-logic-computedAttrs.${key}".`
|
|
843
918
|
);
|
|
919
|
+
});
|
|
920
|
+
});
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
function checkRuleIntegrity(rule, id, data, errorMessage = (item) => `[json-schema-form] json-logic error: rule "${id}" has no variable "${item.var}".`) {
|
|
924
|
+
Object.entries(rule ?? {}).map(([operator, subRule]) => {
|
|
925
|
+
if (!Array.isArray(subRule) && subRule !== null && subRule !== void 0)
|
|
926
|
+
return;
|
|
927
|
+
throwIfUnknownOperator(operator, subRule, id);
|
|
928
|
+
subRule.map((item) => {
|
|
929
|
+
const isVar = item !== null && typeof item === "object" && Object.hasOwn(item, "var");
|
|
930
|
+
if (isVar) {
|
|
931
|
+
const exists = jsonLogic.apply({ var: removeIndicesFromPath(item.var) }, data);
|
|
932
|
+
if (exists === null) {
|
|
933
|
+
throw Error(errorMessage(item));
|
|
934
|
+
}
|
|
844
935
|
} else {
|
|
845
|
-
|
|
846
|
-
{
|
|
847
|
-
...fieldSetfield,
|
|
848
|
-
inputType: fieldSetfield.type
|
|
849
|
-
},
|
|
850
|
-
config
|
|
851
|
-
)();
|
|
936
|
+
checkRuleIntegrity(item, id, data);
|
|
852
937
|
}
|
|
853
938
|
});
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
);
|
|
866
|
-
}
|
|
867
|
-
const validators = [withBaseSchema];
|
|
868
|
-
if (inputType === supportedTypes.GROUP_ARRAY) {
|
|
869
|
-
validators[0] = () => withBaseSchema().of(buildGroupArraySchema());
|
|
870
|
-
} else if (inputType === supportedTypes.FIELDSET) {
|
|
871
|
-
validators[0] = () => withBaseSchema().shape(buildFieldSetSchema(propertyFields.fields));
|
|
872
|
-
}
|
|
873
|
-
if (propertyFields.required) {
|
|
874
|
-
validators.push(withRequired);
|
|
875
|
-
}
|
|
876
|
-
if (typeof propertyFields.minimum !== "undefined") {
|
|
877
|
-
validators.push(withMin);
|
|
878
|
-
}
|
|
879
|
-
if (typeof propertyFields.minLength !== "undefined") {
|
|
880
|
-
validators.push(withMinLength);
|
|
881
|
-
}
|
|
882
|
-
if (propertyFields.maximum !== void 0) {
|
|
883
|
-
validators.push(withMax);
|
|
884
|
-
}
|
|
885
|
-
if (propertyFields.maxLength) {
|
|
886
|
-
validators.push(withMaxLength);
|
|
887
|
-
}
|
|
888
|
-
if (propertyFields.pattern) {
|
|
889
|
-
validators.push(withMatches);
|
|
890
|
-
}
|
|
891
|
-
if (propertyFields.maxFileSize) {
|
|
892
|
-
validators.push(withMaxFileSize);
|
|
893
|
-
}
|
|
894
|
-
if (propertyFields.accept) {
|
|
895
|
-
validators.push(withFileFormat);
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
function throwIfUnknownOperator(operator, subRule, id) {
|
|
942
|
+
try {
|
|
943
|
+
jsonLogic.apply({ [operator]: subRule });
|
|
944
|
+
} catch (e) {
|
|
945
|
+
if (e.message === `Unrecognized operation ${operator}`) {
|
|
946
|
+
throw Error(
|
|
947
|
+
`[json-schema-form] json-logic error: in "${id}" rule there is an unknown operator "${operator}".`
|
|
948
|
+
);
|
|
949
|
+
}
|
|
896
950
|
}
|
|
897
|
-
|
|
898
|
-
|
|
951
|
+
}
|
|
952
|
+
var regexToGetIndices = /\.\d+\./g;
|
|
953
|
+
function removeIndicesFromPath(path) {
|
|
954
|
+
const intermediatePath = path.replace(regexToGetIndices, ".");
|
|
955
|
+
return intermediatePath.replace(/\.\d+$/, "");
|
|
956
|
+
}
|
|
957
|
+
function processJSONLogicNode({
|
|
958
|
+
node,
|
|
959
|
+
formFields,
|
|
960
|
+
formValues,
|
|
961
|
+
accRequired,
|
|
962
|
+
parentID,
|
|
963
|
+
logic
|
|
964
|
+
}) {
|
|
965
|
+
const requiredFields = new Set(accRequired);
|
|
966
|
+
if (node.allOf) {
|
|
967
|
+
node.allOf.map(
|
|
968
|
+
(allOfNode) => processJSONLogicNode({ node: allOfNode, formValues, formFields, logic, parentID })
|
|
969
|
+
).forEach(({ required: allOfItemRequired }) => {
|
|
970
|
+
allOfItemRequired.forEach(requiredFields.add, requiredFields);
|
|
971
|
+
});
|
|
899
972
|
}
|
|
900
|
-
if (
|
|
901
|
-
|
|
902
|
-
|
|
973
|
+
if (node.if) {
|
|
974
|
+
const matchesPropertyCondition = checkIfConditionMatchesProperties(
|
|
975
|
+
node,
|
|
976
|
+
formValues,
|
|
977
|
+
formFields,
|
|
978
|
+
logic
|
|
903
979
|
);
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
if (field.noSortEdges) {
|
|
910
|
-
list.push(field.name);
|
|
980
|
+
const matchesValidationsAndComputedValues = matchesPropertyCondition && checkIfMatchesValidationsAndComputedValues(node, formValues, logic, parentID);
|
|
981
|
+
const isConditionMatch = matchesPropertyCondition && matchesValidationsAndComputedValues;
|
|
982
|
+
let nextNode;
|
|
983
|
+
if (isConditionMatch && node.then) {
|
|
984
|
+
nextNode = node.then;
|
|
911
985
|
}
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
}
|
|
915
|
-
function getSchema(fields = [], config) {
|
|
916
|
-
const newSchema = {};
|
|
917
|
-
fields.forEach((field) => {
|
|
918
|
-
if (field.schema) {
|
|
919
|
-
if (field.name) {
|
|
920
|
-
if (field.inputType === supportedTypes.FIELDSET) {
|
|
921
|
-
const fieldsetSchema = buildYupSchema(field, config)();
|
|
922
|
-
newSchema[field.name] = fieldsetSchema;
|
|
923
|
-
} else {
|
|
924
|
-
newSchema[field.name] = field.schema;
|
|
925
|
-
}
|
|
926
|
-
} else {
|
|
927
|
-
Object.assign(newSchema, getSchema(field.fields, config));
|
|
928
|
-
}
|
|
986
|
+
if (!isConditionMatch && node.else) {
|
|
987
|
+
nextNode = node.else;
|
|
929
988
|
}
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
989
|
+
if (nextNode) {
|
|
990
|
+
const { required: branchRequired } = processNode({
|
|
991
|
+
node: nextNode,
|
|
992
|
+
formValues,
|
|
993
|
+
formFields,
|
|
994
|
+
accRequired,
|
|
995
|
+
logic,
|
|
996
|
+
parentID
|
|
997
|
+
});
|
|
998
|
+
branchRequired.forEach((field) => requiredFields.add(field));
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
return { required: requiredFields };
|
|
935
1002
|
}
|
|
936
1003
|
|
|
937
1004
|
// src/helpers.js
|
|
@@ -1050,7 +1117,11 @@ function updateField(field, requiredFields, node, formValues, logic, config) {
|
|
|
1050
1117
|
updateValues(computedFieldValues);
|
|
1051
1118
|
}
|
|
1052
1119
|
if (field.calculateConditionalProperties) {
|
|
1053
|
-
const newFieldValues = field.calculateConditionalProperties(
|
|
1120
|
+
const newFieldValues = field.calculateConditionalProperties({
|
|
1121
|
+
isRequired: fieldIsRequired,
|
|
1122
|
+
conditionBranch: node,
|
|
1123
|
+
formValues
|
|
1124
|
+
});
|
|
1054
1125
|
updateValues(newFieldValues);
|
|
1055
1126
|
}
|
|
1056
1127
|
if (field.calculateCustomValidationProperties) {
|
|
@@ -1082,7 +1153,7 @@ function processNode({
|
|
|
1082
1153
|
});
|
|
1083
1154
|
});
|
|
1084
1155
|
if (node.if) {
|
|
1085
|
-
const matchesCondition =
|
|
1156
|
+
const matchesCondition = checkIfConditionMatchesProperties(node, formValues, formFields, logic);
|
|
1086
1157
|
if (matchesCondition && node.then) {
|
|
1087
1158
|
const { required: branchRequired } = processNode({
|
|
1088
1159
|
node: node.then,
|
|
@@ -1145,6 +1216,17 @@ function processNode({
|
|
|
1145
1216
|
}
|
|
1146
1217
|
});
|
|
1147
1218
|
}
|
|
1219
|
+
if (node["x-jsf-logic"]) {
|
|
1220
|
+
const { required: requiredFromLogic } = processJSONLogicNode({
|
|
1221
|
+
node: node["x-jsf-logic"],
|
|
1222
|
+
formValues,
|
|
1223
|
+
formFields,
|
|
1224
|
+
accRequired: requiredFields,
|
|
1225
|
+
parentID,
|
|
1226
|
+
logic
|
|
1227
|
+
});
|
|
1228
|
+
requiredFromLogic.forEach((field) => requiredFields.add(field));
|
|
1229
|
+
}
|
|
1148
1230
|
return {
|
|
1149
1231
|
required: requiredFields
|
|
1150
1232
|
};
|
|
@@ -1206,7 +1288,7 @@ function extractParametersFromNode(schemaNode) {
|
|
|
1206
1288
|
}
|
|
1207
1289
|
const presentation = pickXKey(schemaNode, "presentation") ?? {};
|
|
1208
1290
|
const errorMessage = pickXKey(schemaNode, "errorMessage") ?? {};
|
|
1209
|
-
const
|
|
1291
|
+
const jsonLogicValidations = schemaNode["x-jsf-logic-validations"];
|
|
1210
1292
|
const computedAttributes = schemaNode["x-jsf-logic-computedAttrs"];
|
|
1211
1293
|
const decoratedComputedAttributes = getDecoratedComputedAttributes(computedAttributes);
|
|
1212
1294
|
const node = omit(schemaNode, ["x-jsf-presentation", "presentation"]);
|
|
@@ -1250,7 +1332,7 @@ function extractParametersFromNode(schemaNode) {
|
|
|
1250
1332
|
},
|
|
1251
1333
|
// Handle [name].presentation
|
|
1252
1334
|
...presentation,
|
|
1253
|
-
|
|
1335
|
+
jsonLogicValidations,
|
|
1254
1336
|
computedAttributes: decoratedComputedAttributes,
|
|
1255
1337
|
description: containsHTML(description) ? wrapWithSpan(description, {
|
|
1256
1338
|
class: "jsf-description"
|
|
@@ -1350,8 +1432,8 @@ function rebuildFieldset(fields, property) {
|
|
|
1350
1432
|
required: isFieldRequired(property, field)
|
|
1351
1433
|
}));
|
|
1352
1434
|
}
|
|
1353
|
-
function calculateConditionalProperties(fieldParams, customProperties) {
|
|
1354
|
-
return (isRequired, conditionBranch) => {
|
|
1435
|
+
function calculateConditionalProperties({ fieldParams, customProperties, logic, config }) {
|
|
1436
|
+
return ({ isRequired, conditionBranch, formValues }) => {
|
|
1355
1437
|
const conditionalProperty = conditionBranch?.properties?.[fieldParams.name];
|
|
1356
1438
|
if (conditionalProperty) {
|
|
1357
1439
|
const presentation = pickXKey(conditionalProperty, "presentation") ?? {};
|
|
@@ -1365,17 +1447,31 @@ function calculateConditionalProperties(fieldParams, customProperties) {
|
|
|
1365
1447
|
fieldSetFields = rebuildFieldset(fieldParams.fields, conditionalProperty);
|
|
1366
1448
|
newFieldParams.fields = fieldSetFields;
|
|
1367
1449
|
}
|
|
1450
|
+
const { computedAttributes, ...restNewFieldParams } = newFieldParams;
|
|
1451
|
+
const calculatedComputedAttributes = computedAttributes ? calculateComputedAttributes(newFieldParams, config)({ logic, formValues }) : {};
|
|
1452
|
+
const jsonLogicValidations = [
|
|
1453
|
+
...fieldParams.jsonLogicValidations ?? [],
|
|
1454
|
+
...restNewFieldParams.jsonLogicValidations ?? []
|
|
1455
|
+
];
|
|
1368
1456
|
const base = {
|
|
1369
1457
|
isVisible: true,
|
|
1370
1458
|
required: isRequired,
|
|
1371
1459
|
...presentation?.inputType && { type: presentation.inputType },
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1460
|
+
...calculatedComputedAttributes,
|
|
1461
|
+
...calculatedComputedAttributes.value ? { value: calculatedComputedAttributes.value } : { value: void 0 },
|
|
1462
|
+
schema: buildYupSchema(
|
|
1463
|
+
{
|
|
1464
|
+
...fieldParams,
|
|
1465
|
+
...restNewFieldParams,
|
|
1466
|
+
...calculatedComputedAttributes,
|
|
1467
|
+
jsonLogicValidations,
|
|
1468
|
+
// If there are inner fields (case of fieldset) they need to be updated based on the condition
|
|
1469
|
+
fields: fieldSetFields,
|
|
1470
|
+
required: isRequired
|
|
1471
|
+
},
|
|
1472
|
+
config,
|
|
1473
|
+
logic
|
|
1474
|
+
)
|
|
1379
1475
|
};
|
|
1380
1476
|
return omit2(merge2(base, presentation, newFieldParams), ["inputType"]);
|
|
1381
1477
|
}
|
|
@@ -1469,14 +1565,19 @@ function sortByOrderOrPosition(a, b, order) {
|
|
|
1469
1565
|
function removeInvalidAttributes(fields) {
|
|
1470
1566
|
return omit3(fields, ["items", "maxFileSize", "isDynamic"]);
|
|
1471
1567
|
}
|
|
1472
|
-
function buildFieldParameters(name, fieldProperties, required = [], config = {}) {
|
|
1568
|
+
function buildFieldParameters(name, fieldProperties, required = [], config = {}, logic) {
|
|
1473
1569
|
const { position } = pickXKey(fieldProperties, "presentation") ?? {};
|
|
1474
1570
|
let fields;
|
|
1475
1571
|
const inputType = getInputType(fieldProperties, config.strictInputType, name);
|
|
1476
1572
|
if (inputType === supportedTypes.FIELDSET) {
|
|
1477
|
-
fields = getFieldsFromJSONSchema(
|
|
1478
|
-
|
|
1479
|
-
|
|
1573
|
+
fields = getFieldsFromJSONSchema(
|
|
1574
|
+
fieldProperties,
|
|
1575
|
+
{
|
|
1576
|
+
customProperties: get3(config, `customProperties.${name}`, {}),
|
|
1577
|
+
parentID: name
|
|
1578
|
+
},
|
|
1579
|
+
logic
|
|
1580
|
+
);
|
|
1480
1581
|
}
|
|
1481
1582
|
const result = {
|
|
1482
1583
|
name,
|
|
@@ -1533,7 +1634,7 @@ function buildField(fieldParams, config, scopedJsonSchema, logic) {
|
|
|
1533
1634
|
const customProperties = getCustomPropertiesForField(fieldParams, config);
|
|
1534
1635
|
const composeFn = getComposeFunctionForField(fieldParams, !!customProperties);
|
|
1535
1636
|
const yupSchema = buildYupSchema(fieldParams, config, logic);
|
|
1536
|
-
const calculateConditionalFieldsClosure = fieldParams.isDynamic && calculateConditionalProperties(fieldParams, customProperties);
|
|
1637
|
+
const calculateConditionalFieldsClosure = fieldParams.isDynamic && calculateConditionalProperties({ fieldParams, customProperties, logic, config });
|
|
1537
1638
|
const calculateCustomValidationPropertiesClosure = calculateCustomValidationProperties(
|
|
1538
1639
|
fieldParams,
|
|
1539
1640
|
customProperties
|