@rytass/bpm-core-nestjs-module 0.11.0 → 0.12.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 +30 -0
- package/package.json +2 -2
- package/src/lib/condition/condition.service.js +4 -1
- package/src/lib/condition/condition.service.js.map +1 -1
- package/src/lib/form/form-schema.validator.js +277 -56
- package/src/lib/form/form-schema.validator.js.map +1 -1
- package/src/lib/form/form-table-reference.d.ts +28 -0
- package/src/lib/form/form-table-reference.js +53 -0
- package/src/lib/form/form-table-reference.js.map +1 -0
- package/src/lib/form-data-source/form-data-source-value-resolver.service.js +81 -13
- package/src/lib/form-data-source/form-data-source-value-resolver.service.js.map +1 -1
- package/src/lib/form-data-source/form-data-source.queries.d.ts +4 -0
- package/src/lib/form-data-source/form-data-source.queries.js +31 -0
- package/src/lib/form-data-source/form-data-source.queries.js.map +1 -1
- package/src/lib/form-data-source/form-data-source.service.d.ts +21 -0
- package/src/lib/form-data-source/form-data-source.service.js +70 -17
- package/src/lib/form-data-source/form-data-source.service.js.map +1 -1
- package/src/lib/form-data-source/form-data-source.types.d.ts +6 -0
- package/src/lib/form-data-source/form-data-source.types.js.map +1 -1
- package/src/lib/form-data-source/form-data-source.validation.d.ts +12 -1
- package/src/lib/form-data-source/form-data-source.validation.js +34 -5
- package/src/lib/form-data-source/form-data-source.validation.js.map +1 -1
- package/src/lib/template/template.service.js +34 -1
- package/src/lib/template/template.service.js.map +1 -1
- package/src/lib/workflow-engine/workflow-condition-evaluator.js +18 -0
- package/src/lib/workflow-engine/workflow-condition-evaluator.js.map +1 -1
- package/src/lib/workflow-engine/workflow-engine.service.js +119 -6
- package/src/lib/workflow-engine/workflow-engine.service.js.map +1 -1
|
@@ -21,6 +21,14 @@ function evaluateWorkflowEdgeCondition(edge, context) {
|
|
|
21
21
|
return compareWorkflowValues(actualValue, operator, expectedValue);
|
|
22
22
|
}
|
|
23
23
|
function compareWorkflowValues(actualValue, operator, expectedValue) {
|
|
24
|
+
// A table value is a list of rows. Only IS_FILLED / IS_EMPTY (handled above,
|
|
25
|
+
// by row count) are defined for it; the designer offers nothing else, but a
|
|
26
|
+
// hand-written or imported definition can still carry a value comparison.
|
|
27
|
+
// Stringifying a row record would compare `[object Object]` and route the
|
|
28
|
+
// instance on a coincidence, so it never matches (ADR 16 §3.8).
|
|
29
|
+
if (isNonPrimitiveValue(actualValue)) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
24
32
|
if (Array.isArray(actualValue)) {
|
|
25
33
|
return compareArrayValue(actualValue, operator, expectedValue);
|
|
26
34
|
}
|
|
@@ -67,6 +75,16 @@ function compareOrderedValue(actualValue, operator, expectedValue) {
|
|
|
67
75
|
}
|
|
68
76
|
return false;
|
|
69
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* True for a row record, or for a list holding any of them. Multi-select values
|
|
80
|
+
* (`readonly string[]`) stay comparable.
|
|
81
|
+
*/
|
|
82
|
+
function isNonPrimitiveValue(value) {
|
|
83
|
+
if (Array.isArray(value)) {
|
|
84
|
+
return value.some((entry) => isNonPrimitiveValue(entry));
|
|
85
|
+
}
|
|
86
|
+
return typeof value === 'object' && value !== null;
|
|
87
|
+
}
|
|
70
88
|
function isEmptyWorkflowValue(value) {
|
|
71
89
|
if (value === null || typeof value === 'undefined') {
|
|
72
90
|
return true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-condition-evaluator.js","sourceRoot":"","sources":["../../../../../../libs/bpm-core/src/lib/workflow-engine/workflow-condition-evaluator.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"workflow-condition-evaluator.js","sourceRoot":"","sources":["../../../../../../libs/bpm-core/src/lib/workflow-engine/workflow-condition-evaluator.ts"],"names":[],"mappings":"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"}
|
|
@@ -3314,6 +3314,15 @@ function writeValueAtPath(value, path, nextValue) {
|
|
|
3314
3314
|
if (segments.length === 0) {
|
|
3315
3315
|
return value;
|
|
3316
3316
|
}
|
|
3317
|
+
// An index step is not understood by this writer — `items[0].qty` splits into
|
|
3318
|
+
// the literal segment `items[0]`, which would be created as a brand new
|
|
3319
|
+
// top-level key rather than reaching the row. That is not the no-op ADR 16
|
|
3320
|
+
// §3.8 asks for: it pollutes `formData` (and therefore the CEL context) with
|
|
3321
|
+
// a key no schema declares, and makes `readValueAtPath` start answering for
|
|
3322
|
+
// a path it should never resolve.
|
|
3323
|
+
if (segments.some((segment) => /[[\]]/u.test(segment))) {
|
|
3324
|
+
return value;
|
|
3325
|
+
}
|
|
3317
3326
|
return writeNestedValue(value, segments, nextValue);
|
|
3318
3327
|
}
|
|
3319
3328
|
function writeNestedValue(value, segments, nextValue) {
|
|
@@ -3325,6 +3334,15 @@ function writeNestedValue(value, segments, nextValue) {
|
|
|
3325
3334
|
return { ...value, [currentSegment]: nextValue };
|
|
3326
3335
|
}
|
|
3327
3336
|
const currentValue = value[currentSegment];
|
|
3337
|
+
// Descending through something that already holds a non-record value must be
|
|
3338
|
+
// a no-op, not an overwrite. `items.qty` against a table would otherwise
|
|
3339
|
+
// replace the whole row array with `{ qty: ... }`; the same guard protects a
|
|
3340
|
+
// scalar from being swallowed by a nested write (ADR 16 §3.8).
|
|
3341
|
+
if (typeof currentValue !== 'undefined' &&
|
|
3342
|
+
currentValue !== null &&
|
|
3343
|
+
!isRecord(currentValue)) {
|
|
3344
|
+
return value;
|
|
3345
|
+
}
|
|
3328
3346
|
const nestedValue = isRecord(currentValue) ? currentValue : {};
|
|
3329
3347
|
return {
|
|
3330
3348
|
...value,
|
|
@@ -3616,17 +3634,105 @@ function readCandidateStatusForClosedTask(taskStatus) {
|
|
|
3616
3634
|
return workflow_engine_enums_1.TaskCandidateStatusEnum.CANCELLED;
|
|
3617
3635
|
}
|
|
3618
3636
|
function validateSubmittedFormData(schema, formData) {
|
|
3619
|
-
const
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3637
|
+
const visibleFields = schema.fields.filter((field) => isSubmittedFormFieldVisible(field, schema.fields, formData));
|
|
3638
|
+
// Tables answer "is this filled?" by row count and report per-row problems
|
|
3639
|
+
// with an instance path, so they get their own pass instead of the flat
|
|
3640
|
+
// "missing required fields" list (ADR 16 §3.7).
|
|
3641
|
+
const missingFields = visibleFields.filter((field) => !(0, form_1.isTableFieldDefinition)(field) &&
|
|
3642
|
+
isSubmittedFormFieldRequired(field, schema.fields, formData) &&
|
|
3643
|
+
!isSubmittedFieldValuePresent(formData[field.fieldKey]));
|
|
3625
3644
|
if (missingFields.length > 0) {
|
|
3626
3645
|
throw new common_1.BadRequestException(`Form data is missing required fields: ${missingFields
|
|
3627
3646
|
.map((field) => field.label || field.fieldKey)
|
|
3628
3647
|
.join(', ')}`);
|
|
3629
3648
|
}
|
|
3649
|
+
const tableErrors = visibleFields
|
|
3650
|
+
.filter(form_1.isTableFieldDefinition)
|
|
3651
|
+
.flatMap((field) => validateSubmittedTableValue(field, formData[field.fieldKey], isSubmittedFormFieldRequired(field, schema.fields, formData)));
|
|
3652
|
+
if (tableErrors.length > 0) {
|
|
3653
|
+
throw new common_1.BadRequestException(tableErrors.join('; '));
|
|
3654
|
+
}
|
|
3655
|
+
}
|
|
3656
|
+
/**
|
|
3657
|
+
* Authoritative shape, row-count and per-row required checks for one table
|
|
3658
|
+
* value (ADR 16 §3.7). Depth stops here on purpose: like the flat fields, only
|
|
3659
|
+
* required / shape / row count are enforced server side; `minimum`, `maxLength`
|
|
3660
|
+
* and friends stay a frontend hint.
|
|
3661
|
+
*/
|
|
3662
|
+
function validateSubmittedTableValue(field, value, required) {
|
|
3663
|
+
const path = `formData.${field.fieldKey}`;
|
|
3664
|
+
if (typeof value === 'undefined' || value === null) {
|
|
3665
|
+
return required || readTableMinRows(field) > 0
|
|
3666
|
+
? [`${path} must include at least ${readTableRequiredRowCount(field)} row(s)`]
|
|
3667
|
+
: [];
|
|
3668
|
+
}
|
|
3669
|
+
if (!Array.isArray(value)) {
|
|
3670
|
+
return [`${path} must be an array of rows`];
|
|
3671
|
+
}
|
|
3672
|
+
const rowErrors = value.flatMap((row, index) => validateSubmittedTableRow(field, row, `${path}[${index}]`));
|
|
3673
|
+
// A malformed row cannot be counted against min/max meaningfully; report the
|
|
3674
|
+
// shape problem first so the submitter fixes the real cause.
|
|
3675
|
+
if (rowErrors.length > 0) {
|
|
3676
|
+
return rowErrors;
|
|
3677
|
+
}
|
|
3678
|
+
return validateSubmittedTableRowCount(field, value.length, required, path);
|
|
3679
|
+
}
|
|
3680
|
+
function validateSubmittedTableRowCount(field, rowCount, required, path) {
|
|
3681
|
+
const minRows = required
|
|
3682
|
+
? Math.max(readTableMinRows(field), 1)
|
|
3683
|
+
: readTableMinRows(field);
|
|
3684
|
+
const maxRows = readTableMaxRows(field);
|
|
3685
|
+
return [
|
|
3686
|
+
...(rowCount < minRows
|
|
3687
|
+
? [`${path} must include at least ${minRows} row(s)`]
|
|
3688
|
+
: []),
|
|
3689
|
+
...(rowCount > maxRows
|
|
3690
|
+
? [`${path} must include at most ${maxRows} row(s)`]
|
|
3691
|
+
: []),
|
|
3692
|
+
];
|
|
3693
|
+
}
|
|
3694
|
+
function validateSubmittedTableRow(field, row, path) {
|
|
3695
|
+
if (!isRecord(row)) {
|
|
3696
|
+
return [`${path} must be an object`];
|
|
3697
|
+
}
|
|
3698
|
+
const columnsByKey = new Map(field.columns.map((column) => [column.fieldKey, column]));
|
|
3699
|
+
const unknownKeyErrors = Object.keys(row)
|
|
3700
|
+
.filter((columnKey) => !columnsByKey.has(columnKey))
|
|
3701
|
+
.map((columnKey) => `${path}.${columnKey} is not a table column`);
|
|
3702
|
+
const cellErrors = Object.entries(row)
|
|
3703
|
+
.filter(([columnKey, cellValue]) => columnsByKey.has(columnKey) && !(0, form_1.isFormTableCellValue)(cellValue))
|
|
3704
|
+
.map(([columnKey]) => `${path}.${columnKey} is not a valid cell value`);
|
|
3705
|
+
const requiredErrors = field.columns
|
|
3706
|
+
.filter((column) => isSubmittedTableColumnRequired(column) &&
|
|
3707
|
+
!isSubmittedFieldValuePresent(readRowCellValue(row, column.fieldKey)))
|
|
3708
|
+
.map((column) => `${path}.${column.fieldKey} is required`);
|
|
3709
|
+
return [...unknownKeyErrors, ...cellErrors, ...requiredErrors];
|
|
3710
|
+
}
|
|
3711
|
+
/**
|
|
3712
|
+
* Own-property read. `constructor`, `toString` and friends all satisfy the
|
|
3713
|
+
* column key identifier rule, so a plain index would walk the prototype chain
|
|
3714
|
+
* and report an empty row as filled.
|
|
3715
|
+
*/
|
|
3716
|
+
function readRowCellValue(row, columnKey) {
|
|
3717
|
+
return Object.prototype.hasOwnProperty.call(row, columnKey)
|
|
3718
|
+
? row[columnKey]
|
|
3719
|
+
: undefined;
|
|
3720
|
+
}
|
|
3721
|
+
/**
|
|
3722
|
+
* Columns carry no `requiredWhen`: row-scoped conditions are out of scope for
|
|
3723
|
+
* V1 and the structural lint rejects them (ADR 16 §3.1).
|
|
3724
|
+
*/
|
|
3725
|
+
function isSubmittedTableColumnRequired(column) {
|
|
3726
|
+
return column.required === true;
|
|
3727
|
+
}
|
|
3728
|
+
function readTableMinRows(field) {
|
|
3729
|
+
return typeof field.minRows === 'number' ? field.minRows : 0;
|
|
3730
|
+
}
|
|
3731
|
+
function readTableMaxRows(field) {
|
|
3732
|
+
return typeof field.maxRows === 'number' ? field.maxRows : form_1.FORM_TABLE_MAX_ROWS;
|
|
3733
|
+
}
|
|
3734
|
+
function readTableRequiredRowCount(field) {
|
|
3735
|
+
return Math.max(readTableMinRows(field), 1);
|
|
3630
3736
|
}
|
|
3631
3737
|
function readFormDefinitionSnapshotSchema(snapshot) {
|
|
3632
3738
|
const schema = snapshot.schema;
|
|
@@ -3663,6 +3769,13 @@ function evaluateFormConditionExpression(expression, fields, values, fallback) {
|
|
|
3663
3769
|
if (!field) {
|
|
3664
3770
|
return fallback;
|
|
3665
3771
|
}
|
|
3772
|
+
// A table value is a list of rows; comparing it with `==` / `>` has no
|
|
3773
|
+
// meaning, and the structural lint already rejects cell-level references.
|
|
3774
|
+
// Falling back keeps a table operand from silently deciding visibility
|
|
3775
|
+
// through a string comparison against row records (ADR 16 §3.8).
|
|
3776
|
+
if ((0, form_1.isTableFieldDefinition)(field)) {
|
|
3777
|
+
return fallback;
|
|
3778
|
+
}
|
|
3666
3779
|
return evaluateFormConditionRule(rule, field, values[field.fieldKey]);
|
|
3667
3780
|
}
|
|
3668
3781
|
function parseFormConditionRule(expression) {
|