nox-validation 1.3.1 → 1.3.3
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/README.md +20 -6
- package/lib/helpers.js +217 -76
- package/lib/validate.js +279 -99
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,9 +32,9 @@ npm install nox-validation
|
|
|
32
32
|
```javascript
|
|
33
33
|
const { validate, helpers } = require("nox-validation");
|
|
34
34
|
|
|
35
|
-
const all_fields = [];
|
|
35
|
+
const all_fields = [];
|
|
36
36
|
|
|
37
|
-
const relations = [];
|
|
37
|
+
const relations = [];
|
|
38
38
|
|
|
39
39
|
const schema = [
|
|
40
40
|
{
|
|
@@ -113,6 +113,8 @@ const result = validate({
|
|
|
113
113
|
apiVersion: "v1",
|
|
114
114
|
language: "nl",
|
|
115
115
|
maxLevel: 3,
|
|
116
|
+
onlyFormFields: false,
|
|
117
|
+
language_codes:[]
|
|
116
118
|
});
|
|
117
119
|
```
|
|
118
120
|
|
|
@@ -133,7 +135,9 @@ const result = validate({
|
|
|
133
135
|
byPassKeys: [],
|
|
134
136
|
apiVersion: "v1",
|
|
135
137
|
language: "nl",
|
|
136
|
-
maxLevel: 3
|
|
138
|
+
maxLevel: 3,
|
|
139
|
+
onlyFormFields: false,
|
|
140
|
+
language_codes:[]
|
|
137
141
|
});
|
|
138
142
|
```
|
|
139
143
|
|
|
@@ -141,9 +145,9 @@ const result = validate({
|
|
|
141
145
|
|
|
142
146
|
```javascript
|
|
143
147
|
{
|
|
144
|
-
status: true,
|
|
145
|
-
error: {},
|
|
146
|
-
data: {}
|
|
148
|
+
status: true,
|
|
149
|
+
error: {},
|
|
150
|
+
data: {}
|
|
147
151
|
}
|
|
148
152
|
```
|
|
149
153
|
|
|
@@ -207,6 +211,16 @@ const result = validate({
|
|
|
207
211
|
|
|
208
212
|
- Defines the level for generate tree structure.
|
|
209
213
|
|
|
214
|
+
#### `onlyFormFields` (Boolean)
|
|
215
|
+
|
|
216
|
+
Indicates the context in which the form is being used.
|
|
217
|
+
- `false`: Used when creating new data (full form).
|
|
218
|
+
- `true`: Used when editing or displaying only existing form fields.
|
|
219
|
+
|
|
220
|
+
#### `language_codes` (Array of IDs)
|
|
221
|
+
- Represents the selected translation language IDs.
|
|
222
|
+
- Each item in the array should be a valid language code or `_id` used for handling multilingual content.
|
|
223
|
+
|
|
210
224
|
## Result
|
|
211
225
|
|
|
212
226
|
The `validate` function returns an object with two keys:
|
package/lib/helpers.js
CHANGED
|
@@ -10,7 +10,9 @@ const getAllFields = (obj, parentPath = "") => {
|
|
|
10
10
|
: `${path}[${index}]`
|
|
11
11
|
);
|
|
12
12
|
}
|
|
13
|
-
return typeof obj[key] === "object" &&
|
|
13
|
+
return typeof obj[key] === "object" &&
|
|
14
|
+
obj[key] !== null &&
|
|
15
|
+
!Array.isArray(obj[key])
|
|
14
16
|
? getAllFields(obj[key], path)
|
|
15
17
|
: path;
|
|
16
18
|
});
|
|
@@ -133,7 +135,10 @@ const getFormPath = (inputFields, fieldPath) => {
|
|
|
133
135
|
const field = inputFields.find((f) => f.path === currentPath);
|
|
134
136
|
|
|
135
137
|
if (field) {
|
|
136
|
-
formPath +=
|
|
138
|
+
formPath +=
|
|
139
|
+
field.type === constants.types.ARRAY
|
|
140
|
+
? `${field.field}[0]`
|
|
141
|
+
: field.field;
|
|
137
142
|
} else {
|
|
138
143
|
formPath += `.${pathParts[i]}`;
|
|
139
144
|
}
|
|
@@ -142,7 +147,9 @@ const getFormPath = (inputFields, fieldPath) => {
|
|
|
142
147
|
}
|
|
143
148
|
|
|
144
149
|
const cleanedFormPath = formPath.replace(/\.\[/g, "[");
|
|
145
|
-
return cleanedFormPath.endsWith("[0]")
|
|
150
|
+
return cleanedFormPath.endsWith("[0]")
|
|
151
|
+
? cleanedFormPath.split("[0]")[0]
|
|
152
|
+
: cleanedFormPath;
|
|
146
153
|
};
|
|
147
154
|
|
|
148
155
|
const isEmpty = (val) => {
|
|
@@ -157,28 +164,6 @@ const isEmpty = (val) => {
|
|
|
157
164
|
return false;
|
|
158
165
|
};
|
|
159
166
|
|
|
160
|
-
// field?.field_type (Possible Values => Single, Object, Array)
|
|
161
|
-
// 1. Single => Root Field Then Its Single
|
|
162
|
-
// 2. Object => Nested Field Like Inside Array Or Object
|
|
163
|
-
// 3. Array => Any Kind Of Array Array Of String, Object etc
|
|
164
|
-
|
|
165
|
-
// field?.type (Possible Values)
|
|
166
|
-
// 1. String
|
|
167
|
-
// 2. Number
|
|
168
|
-
// 3. Date
|
|
169
|
-
// 4. Buffer
|
|
170
|
-
// 5. Boolean
|
|
171
|
-
// 6. Mixed
|
|
172
|
-
// 7. ObjectId
|
|
173
|
-
// 8. Object
|
|
174
|
-
// 9. Array
|
|
175
|
-
// 10. Alias
|
|
176
|
-
|
|
177
|
-
// field?.schema_definition?.type
|
|
178
|
-
// it is used for specially when field?.type is Array, but some times both are Array Then We Have to check
|
|
179
|
-
|
|
180
|
-
// field?.meta?.interface
|
|
181
|
-
|
|
182
167
|
const generateType = (field, api) => {
|
|
183
168
|
let { type, schema_definition, meta } = field;
|
|
184
169
|
let interfaceType = meta?.interface;
|
|
@@ -201,9 +186,10 @@ const generateType = (field, api) => {
|
|
|
201
186
|
if (interfaceType && interfaceType !== "none") {
|
|
202
187
|
// We Need to find Relation
|
|
203
188
|
if (
|
|
204
|
-
[
|
|
205
|
-
|
|
206
|
-
|
|
189
|
+
[
|
|
190
|
+
constants.interfaces.MANY_TO_ANY,
|
|
191
|
+
constants.interfaces.TRANSLATIONS,
|
|
192
|
+
].includes(interfaceType)
|
|
207
193
|
) {
|
|
208
194
|
find_relations = true;
|
|
209
195
|
// update type and array type accordingly interface
|
|
@@ -274,13 +260,21 @@ const generateField = (
|
|
|
274
260
|
type,
|
|
275
261
|
childrenFields = [],
|
|
276
262
|
relationType = "none",
|
|
277
|
-
alternateType=[]
|
|
263
|
+
alternateType = [],
|
|
264
|
+
meta = {
|
|
265
|
+
required: false,
|
|
266
|
+
nullable: false,
|
|
267
|
+
hidden: false,
|
|
268
|
+
}
|
|
278
269
|
) => {
|
|
279
270
|
childrenFields = childrenFields?.map((child) => {
|
|
271
|
+
const childKey = path ? `${path}.${child.key}` : child.key;
|
|
272
|
+
const childValue = path ? `${path}.${child.value}` : child.value;
|
|
273
|
+
|
|
280
274
|
return {
|
|
281
275
|
...child,
|
|
282
|
-
value:
|
|
283
|
-
key:
|
|
276
|
+
value: childKey,
|
|
277
|
+
key: childValue,
|
|
284
278
|
};
|
|
285
279
|
});
|
|
286
280
|
|
|
@@ -293,9 +287,7 @@ const generateField = (
|
|
|
293
287
|
type: type,
|
|
294
288
|
alternateType,
|
|
295
289
|
meta: {
|
|
296
|
-
|
|
297
|
-
nullable: false,
|
|
298
|
-
hidden: false,
|
|
290
|
+
...meta,
|
|
299
291
|
interface: relationType,
|
|
300
292
|
},
|
|
301
293
|
validations: [],
|
|
@@ -321,7 +313,11 @@ const createChildrenFieldsFiles = (key) => {
|
|
|
321
313
|
return [existingField, deleteField];
|
|
322
314
|
};
|
|
323
315
|
|
|
324
|
-
const generateRelationalFieldV1 = (
|
|
316
|
+
const generateRelationalFieldV1 = (
|
|
317
|
+
key = "",
|
|
318
|
+
collectionFields = [],
|
|
319
|
+
relationType
|
|
320
|
+
) => {
|
|
325
321
|
if (relationType === constants.interfaces.MANY_TO_ANY) {
|
|
326
322
|
return [
|
|
327
323
|
generateField(
|
|
@@ -330,16 +326,35 @@ const generateRelationalFieldV1 = (key = "", collectionFields = [], relationType
|
|
|
330
326
|
constants.types.STRING,
|
|
331
327
|
constants.types.STRING
|
|
332
328
|
),
|
|
333
|
-
generateField(
|
|
334
|
-
|
|
329
|
+
generateField(
|
|
330
|
+
"sort",
|
|
331
|
+
`${key}.sort`,
|
|
332
|
+
constants.types.NUMBER,
|
|
333
|
+
constants.types.NUMBER
|
|
334
|
+
),
|
|
335
|
+
generateField(
|
|
336
|
+
"item",
|
|
337
|
+
`${key}.item`,
|
|
338
|
+
constants.types.OBJECT_ID,
|
|
339
|
+
constants.types.OBJECT_ID
|
|
340
|
+
),
|
|
335
341
|
];
|
|
336
342
|
} else {
|
|
337
343
|
return null;
|
|
338
344
|
}
|
|
339
345
|
};
|
|
340
346
|
|
|
341
|
-
const generateRelationalField = (
|
|
347
|
+
const generateRelationalField = (
|
|
348
|
+
key = "",
|
|
349
|
+
collectionFields = [],
|
|
350
|
+
relationType
|
|
351
|
+
) => {
|
|
342
352
|
if (relationType === constants.interfaces.MANY_TO_ANY) {
|
|
353
|
+
const collection_id_meta = {
|
|
354
|
+
required: true,
|
|
355
|
+
nullable: false,
|
|
356
|
+
hidden: false,
|
|
357
|
+
};
|
|
343
358
|
const existingField = generateField(
|
|
344
359
|
"existing",
|
|
345
360
|
`${key}.existing`,
|
|
@@ -353,7 +368,12 @@ const generateRelationalField = (key = "", collectionFields = [], relationType)
|
|
|
353
368
|
constants.types.STRING,
|
|
354
369
|
constants.types.STRING
|
|
355
370
|
),
|
|
356
|
-
generateField(
|
|
371
|
+
generateField(
|
|
372
|
+
"sort",
|
|
373
|
+
`${key}.existing.sort`,
|
|
374
|
+
constants.types.NUMBER,
|
|
375
|
+
constants.types.NUMBER
|
|
376
|
+
),
|
|
357
377
|
generateField(
|
|
358
378
|
"item",
|
|
359
379
|
`${key}.existing.item`,
|
|
@@ -374,7 +394,12 @@ const generateRelationalField = (key = "", collectionFields = [], relationType)
|
|
|
374
394
|
constants.types.STRING,
|
|
375
395
|
constants.types.STRING
|
|
376
396
|
),
|
|
377
|
-
generateField(
|
|
397
|
+
generateField(
|
|
398
|
+
"sort",
|
|
399
|
+
`${key}.delete.sort`,
|
|
400
|
+
constants.types.NUMBER,
|
|
401
|
+
constants.types.NUMBER
|
|
402
|
+
),
|
|
378
403
|
generateField(
|
|
379
404
|
"item",
|
|
380
405
|
`${key}.delete.item`,
|
|
@@ -396,15 +421,31 @@ const generateRelationalField = (key = "", collectionFields = [], relationType)
|
|
|
396
421
|
constants.types.STRING,
|
|
397
422
|
constants.types.STRING
|
|
398
423
|
),
|
|
399
|
-
generateField(
|
|
424
|
+
generateField(
|
|
425
|
+
"collection",
|
|
426
|
+
`${key}.create.collection_id`,
|
|
427
|
+
constants.types.OBJECT_ID,
|
|
428
|
+
constants.types.OBJECT_ID,
|
|
429
|
+
[],
|
|
430
|
+
"none",
|
|
431
|
+
[],
|
|
432
|
+
collection_id_meta
|
|
433
|
+
),
|
|
434
|
+
generateField(
|
|
435
|
+
"sort",
|
|
436
|
+
`${key}.create.sort`,
|
|
437
|
+
constants.types.NUMBER,
|
|
438
|
+
constants.types.NUMBER
|
|
439
|
+
),
|
|
400
440
|
generateField(
|
|
401
441
|
"item",
|
|
402
442
|
`${key}.create.item`,
|
|
403
443
|
constants.types.OBJECT,
|
|
404
444
|
constants.types.OBJECT,
|
|
405
445
|
collectionFields,
|
|
406
|
-
|
|
407
|
-
[constants.types.OBJECT_ID]
|
|
446
|
+
"none",
|
|
447
|
+
[constants.types.OBJECT_ID],
|
|
448
|
+
{ ...collection_id_meta, is_m2a_item: true }
|
|
408
449
|
),
|
|
409
450
|
];
|
|
410
451
|
const updateField = generateField(
|
|
@@ -421,7 +462,22 @@ const generateRelationalField = (key = "", collectionFields = [], relationType)
|
|
|
421
462
|
constants.types.STRING,
|
|
422
463
|
constants.types.STRING
|
|
423
464
|
),
|
|
424
|
-
generateField(
|
|
465
|
+
generateField(
|
|
466
|
+
"collection",
|
|
467
|
+
`${key}.update.collection_id`,
|
|
468
|
+
constants.types.OBJECT_ID,
|
|
469
|
+
constants.types.OBJECT_ID,
|
|
470
|
+
[],
|
|
471
|
+
"none",
|
|
472
|
+
[],
|
|
473
|
+
collection_id_meta
|
|
474
|
+
),
|
|
475
|
+
generateField(
|
|
476
|
+
"sort",
|
|
477
|
+
`${key}.update.sort`,
|
|
478
|
+
constants.types.NUMBER,
|
|
479
|
+
constants.types.NUMBER
|
|
480
|
+
),
|
|
425
481
|
generateField(
|
|
426
482
|
"item",
|
|
427
483
|
`${key}.update.item`,
|
|
@@ -441,7 +497,12 @@ const generateRelationalField = (key = "", collectionFields = [], relationType)
|
|
|
441
497
|
constants.types.OBJECT_ID,
|
|
442
498
|
constants.types.ARRAY
|
|
443
499
|
),
|
|
444
|
-
generateField(
|
|
500
|
+
generateField(
|
|
501
|
+
"delete",
|
|
502
|
+
`${key}.delete`,
|
|
503
|
+
constants.types.OBJECT_ID,
|
|
504
|
+
constants.types.ARRAY
|
|
505
|
+
),
|
|
445
506
|
generateField(
|
|
446
507
|
"create",
|
|
447
508
|
`${key}.create`,
|
|
@@ -510,7 +571,8 @@ const getForeignCollectionDetails = ({
|
|
|
510
571
|
? relational?.one_allowed_collections_id
|
|
511
572
|
: mainTable.many_collection_id,
|
|
512
573
|
foreign_field:
|
|
513
|
-
iFace === constants.interfaces.MANY_TO_MANY ||
|
|
574
|
+
iFace === constants.interfaces.MANY_TO_MANY ||
|
|
575
|
+
iFace === constants.interfaces.TRANSLATIONS
|
|
514
576
|
? "_id"
|
|
515
577
|
: iFace === constants.interfaces.MANY_TO_ANY
|
|
516
578
|
? "Primary Key"
|
|
@@ -519,11 +581,14 @@ const getForeignCollectionDetails = ({
|
|
|
519
581
|
junction_collection: relational?.many_collection_id,
|
|
520
582
|
junction_field_this: relational?.junction_field,
|
|
521
583
|
junction_field_foreign:
|
|
522
|
-
iFace === constants.interfaces.MANY_TO_ANY
|
|
584
|
+
iFace === constants.interfaces.MANY_TO_ANY
|
|
585
|
+
? "item"
|
|
586
|
+
: relational?.many_field,
|
|
523
587
|
}),
|
|
524
588
|
...(iFace === constants.interfaces.MANY_TO_ANY && {
|
|
525
589
|
junction_field_ref: "collection",
|
|
526
|
-
foreign_collection_ref:
|
|
590
|
+
foreign_collection_ref:
|
|
591
|
+
relational?.one_allowed_collections?.join(", "),
|
|
527
592
|
}),
|
|
528
593
|
};
|
|
529
594
|
}
|
|
@@ -576,7 +641,9 @@ const getCachedFields = (relationDetail, relational_fields) => {
|
|
|
576
641
|
if (!isMultiple) {
|
|
577
642
|
return getField(relationDetail.foreign_collection);
|
|
578
643
|
} else {
|
|
579
|
-
return relationDetail.foreign_collection.flatMap((schemaId) =>
|
|
644
|
+
return relationDetail.foreign_collection.flatMap((schemaId) =>
|
|
645
|
+
getField(schemaId)
|
|
646
|
+
);
|
|
580
647
|
}
|
|
581
648
|
};
|
|
582
649
|
|
|
@@ -585,12 +652,19 @@ const getCachedOrFetchFields = (schemaId, allFields, relational_fields) => {
|
|
|
585
652
|
return relational_fields[schemaId]; // Return cached fields if available
|
|
586
653
|
}
|
|
587
654
|
|
|
588
|
-
const fields =
|
|
655
|
+
const fields =
|
|
656
|
+
allFields?.filter((field) => field.schema_id === schemaId) || [];
|
|
589
657
|
relational_fields[schemaId] = fields; // Cache the fields
|
|
590
658
|
return fields;
|
|
591
659
|
};
|
|
592
660
|
|
|
593
|
-
const getChildFields = (
|
|
661
|
+
const getChildFields = (
|
|
662
|
+
relationDetail,
|
|
663
|
+
allFields,
|
|
664
|
+
relational_fields,
|
|
665
|
+
isTranslation,
|
|
666
|
+
name
|
|
667
|
+
) => {
|
|
594
668
|
let key = isTranslation
|
|
595
669
|
? [
|
|
596
670
|
...(Array.isArray(relationDetail.junction_collection)
|
|
@@ -640,7 +714,9 @@ const buildNestedStructure = ({
|
|
|
640
714
|
const root = {};
|
|
641
715
|
const nodeMap = new Map();
|
|
642
716
|
|
|
643
|
-
schemaFields.sort(
|
|
717
|
+
schemaFields.sort(
|
|
718
|
+
(a, b) => a.path.split(".").length - b.path.split(".").length
|
|
719
|
+
);
|
|
644
720
|
|
|
645
721
|
schemaFields.forEach((item) => {
|
|
646
722
|
const pathParts = item.path.split(".");
|
|
@@ -653,7 +729,8 @@ const buildNestedStructure = ({
|
|
|
653
729
|
constants.interfaces.FILE_IMAGE,
|
|
654
730
|
].includes(item?.meta?.interface);
|
|
655
731
|
|
|
656
|
-
const currentDepth =
|
|
732
|
+
const currentDepth =
|
|
733
|
+
currentDepthMap.get(isRoot ? item.path : rootPath) || 0;
|
|
657
734
|
|
|
658
735
|
let childFields;
|
|
659
736
|
|
|
@@ -706,7 +783,8 @@ const buildNestedStructure = ({
|
|
|
706
783
|
}
|
|
707
784
|
|
|
708
785
|
const isArray =
|
|
709
|
-
item.type === item?.schema_definition.type &&
|
|
786
|
+
item.type === item?.schema_definition.type &&
|
|
787
|
+
item.type === constants.types.ARRAY;
|
|
710
788
|
|
|
711
789
|
let children = [];
|
|
712
790
|
|
|
@@ -717,9 +795,17 @@ const buildNestedStructure = ({
|
|
|
717
795
|
apiVersion === constants.API_VERSION.V1 &&
|
|
718
796
|
item.meta?.interface !== constants.interfaces.TRANSLATIONS
|
|
719
797
|
) {
|
|
720
|
-
children = generateRelationalFieldV1(
|
|
798
|
+
children = generateRelationalFieldV1(
|
|
799
|
+
key,
|
|
800
|
+
childFields,
|
|
801
|
+
item.meta?.interface
|
|
802
|
+
);
|
|
721
803
|
} else {
|
|
722
|
-
children = generateRelationalField(
|
|
804
|
+
children = generateRelationalField(
|
|
805
|
+
key,
|
|
806
|
+
childFields,
|
|
807
|
+
item.meta?.interface
|
|
808
|
+
);
|
|
723
809
|
}
|
|
724
810
|
}
|
|
725
811
|
|
|
@@ -730,7 +816,7 @@ const buildNestedStructure = ({
|
|
|
730
816
|
display_label: "_id",
|
|
731
817
|
key: `${key}._id`,
|
|
732
818
|
value: `${key}._id`,
|
|
733
|
-
alternateType:[],
|
|
819
|
+
alternateType: [],
|
|
734
820
|
meta: {
|
|
735
821
|
interface: "none",
|
|
736
822
|
required: false,
|
|
@@ -761,17 +847,20 @@ const buildNestedStructure = ({
|
|
|
761
847
|
hidden: item.meta?.hidden || false,
|
|
762
848
|
options: item.meta?.options || {},
|
|
763
849
|
},
|
|
764
|
-
validations: generateModifiedRules(
|
|
850
|
+
validations: generateModifiedRules(
|
|
851
|
+
item?.meta,
|
|
852
|
+
item?.meta?.validations?.validation_msg
|
|
853
|
+
),
|
|
765
854
|
custom_error_message: item?.meta?.validations?.validation_msg,
|
|
766
855
|
children,
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
856
|
+
// childFields?.length > 0
|
|
857
|
+
// ? isV2File
|
|
858
|
+
// ? childFields
|
|
859
|
+
// : apiVersion === constants.API_VERSION.V1 &&
|
|
860
|
+
// item.meta?.interface !== constants.interfaces.TRANSLATIONS
|
|
861
|
+
// ? generateRelationalFieldV1(key, childFields, item.meta?.interface)
|
|
862
|
+
// : generateRelationalField(key, childFields, item.meta?.interface)
|
|
863
|
+
// : [],
|
|
775
864
|
type: definedType.type,
|
|
776
865
|
array_type: definedType.array_type,
|
|
777
866
|
default_value: item?.schema_definition?.default,
|
|
@@ -792,7 +881,9 @@ const buildNestedStructure = ({
|
|
|
792
881
|
|
|
793
882
|
const removeEmptyChildren = (nodes) => {
|
|
794
883
|
return nodes.map(({ children, ...node }) =>
|
|
795
|
-
children && children?.length
|
|
884
|
+
children && children?.length
|
|
885
|
+
? { ...node, children: removeEmptyChildren(children) }
|
|
886
|
+
: node
|
|
796
887
|
);
|
|
797
888
|
};
|
|
798
889
|
|
|
@@ -801,7 +892,6 @@ const buildNestedStructure = ({
|
|
|
801
892
|
|
|
802
893
|
const getAllKeys = (structure) => {
|
|
803
894
|
const keys = new Set();
|
|
804
|
-
|
|
805
895
|
const traverse = (nodes) => {
|
|
806
896
|
nodes.forEach((node) => {
|
|
807
897
|
keys.add(node.key);
|
|
@@ -825,7 +915,9 @@ const findDisallowedKeys = (formData, structure, maxLevel) => {
|
|
|
825
915
|
const keyParts = normalizeKey(key).split(".");
|
|
826
916
|
const keyLevel = keyParts.length;
|
|
827
917
|
const levelParent = keyParts.slice(0, maxLevel - 1).join(".");
|
|
828
|
-
return !validKeys.has(
|
|
918
|
+
return !validKeys.has(
|
|
919
|
+
normalizeKey(keyLevel > maxLevel ? levelParent : key)
|
|
920
|
+
);
|
|
829
921
|
});
|
|
830
922
|
};
|
|
831
923
|
|
|
@@ -922,7 +1014,8 @@ const generateFieldCompareRules = (rule) => {
|
|
|
922
1014
|
break;
|
|
923
1015
|
case "greaterThanOrEqualTo":
|
|
924
1016
|
modifiedRule.case = constants.rulesTypes.OPERATOR;
|
|
925
|
-
modifiedRule.options.operator =
|
|
1017
|
+
modifiedRule.options.operator =
|
|
1018
|
+
constants.operatorTypes.GREATER_THAN_EQUAL;
|
|
926
1019
|
modifiedRule.value = [rule[rule.type].value];
|
|
927
1020
|
break;
|
|
928
1021
|
case "isEmpty":
|
|
@@ -1032,7 +1125,8 @@ const generateModifiedRules = (meta, custom_message) => {
|
|
|
1032
1125
|
break;
|
|
1033
1126
|
case "lessThanOrEqualTo":
|
|
1034
1127
|
modifiedRule.case = constants.rulesTypes.OPERATOR;
|
|
1035
|
-
modifiedRule.options.operator =
|
|
1128
|
+
modifiedRule.options.operator =
|
|
1129
|
+
constants.operatorTypes.LESS_THAN_EQUAL;
|
|
1036
1130
|
modifiedRule.value = [rule[rule.rule].value];
|
|
1037
1131
|
break;
|
|
1038
1132
|
case "greaterThan":
|
|
@@ -1042,7 +1136,8 @@ const generateModifiedRules = (meta, custom_message) => {
|
|
|
1042
1136
|
break;
|
|
1043
1137
|
case "greaterThanOrEqualTo":
|
|
1044
1138
|
modifiedRule.case = constants.rulesTypes.OPERATOR;
|
|
1045
|
-
modifiedRule.options.operator =
|
|
1139
|
+
modifiedRule.options.operator =
|
|
1140
|
+
constants.operatorTypes.GREATER_THAN_EQUAL;
|
|
1046
1141
|
modifiedRule.value = [rule[rule.rule].value];
|
|
1047
1142
|
break;
|
|
1048
1143
|
case "isEmpty":
|
|
@@ -1111,13 +1206,21 @@ const getDefaultValues = (tree) => {
|
|
|
1111
1206
|
const cleanKey = keyParts[keyParts.length - 1];
|
|
1112
1207
|
|
|
1113
1208
|
if (type === "Object") {
|
|
1114
|
-
setValue(
|
|
1209
|
+
setValue(
|
|
1210
|
+
defaultValues,
|
|
1211
|
+
cleanKey,
|
|
1212
|
+
children?.length ? getDefaultValues(children) : {}
|
|
1213
|
+
);
|
|
1115
1214
|
} else if (type === "Array") {
|
|
1116
1215
|
if (array_type === "String" || array_type === "Number") {
|
|
1117
1216
|
setValue(defaultValues, cleanKey, []);
|
|
1118
1217
|
} else {
|
|
1119
1218
|
// Prevent extra nesting by ensuring the array contains objects, not arrays
|
|
1120
|
-
setValue(
|
|
1219
|
+
setValue(
|
|
1220
|
+
defaultValues,
|
|
1221
|
+
cleanKey,
|
|
1222
|
+
children?.length ? [getDefaultValues(children)] : []
|
|
1223
|
+
);
|
|
1121
1224
|
}
|
|
1122
1225
|
} else {
|
|
1123
1226
|
setValue(defaultValues, cleanKey, defaultValue);
|
|
@@ -1128,17 +1231,53 @@ const getDefaultValues = (tree) => {
|
|
|
1128
1231
|
};
|
|
1129
1232
|
|
|
1130
1233
|
const extractRelationalParents = (path) => {
|
|
1131
|
-
const match = path?.match(
|
|
1234
|
+
const match = path?.match(
|
|
1235
|
+
/^([^.\[\]]+)\.(create|update|delete|existing)\[(\d+)\](?:\.(.*))?/
|
|
1236
|
+
);
|
|
1132
1237
|
if (match) {
|
|
1133
1238
|
const secondParent = match[1];
|
|
1134
1239
|
const firstParent =
|
|
1135
|
-
match[0].split(".")[0] +
|
|
1240
|
+
match[0].split(".")[0] +
|
|
1241
|
+
"." +
|
|
1242
|
+
match[2] +
|
|
1243
|
+
"[" +
|
|
1244
|
+
path.match(/\[(\d+)\]/)[1] +
|
|
1245
|
+
"]"; //
|
|
1136
1246
|
const afterKey = match[3] || "";
|
|
1137
1247
|
return { firstParent, secondParent, afterKey };
|
|
1138
1248
|
}
|
|
1139
1249
|
return null;
|
|
1140
1250
|
};
|
|
1141
1251
|
|
|
1252
|
+
const getM2AItemParentPath = (path) => {
|
|
1253
|
+
const match = path.match(/^(.*)\.(create|update)\[\d+\]\.item$/);
|
|
1254
|
+
return match ? path.replace(".item", "") : null;
|
|
1255
|
+
};
|
|
1256
|
+
|
|
1257
|
+
const getSelectedNodes = ({
|
|
1258
|
+
node,
|
|
1259
|
+
skipFn= (node)=> node.meta?.interface === constants.interfaces.MANY_TO_ANY,
|
|
1260
|
+
conditionFn = (node) => node.meta?.required === true,
|
|
1261
|
+
mapFn = (node) => ({ key: node.key, label: node.display_label }),
|
|
1262
|
+
actionFn = (node) => {},
|
|
1263
|
+
}) => {
|
|
1264
|
+
const result = [];
|
|
1265
|
+
|
|
1266
|
+
function traverse(currentNode) {
|
|
1267
|
+
if (conditionFn(currentNode)) {
|
|
1268
|
+
actionFn(currentNode);
|
|
1269
|
+
result.push(mapFn(currentNode));
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
if (Array.isArray(currentNode.children)) {
|
|
1273
|
+
currentNode.children.forEach((child) =>{ if(!skipFn(currentNode)) traverse(child,)});
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
traverse(node);
|
|
1278
|
+
return result;
|
|
1279
|
+
};
|
|
1280
|
+
|
|
1142
1281
|
module.exports = {
|
|
1143
1282
|
generateModifiedRules,
|
|
1144
1283
|
getFieldsGroupBySchemaId,
|
|
@@ -1158,4 +1297,6 @@ module.exports = {
|
|
|
1158
1297
|
getForeignCollectionDetails,
|
|
1159
1298
|
getDefaultValues,
|
|
1160
1299
|
extractRelationalParents,
|
|
1300
|
+
getM2AItemParentPath,
|
|
1301
|
+
getSelectedNodes,
|
|
1161
1302
|
};
|
package/lib/validate.js
CHANGED
|
@@ -9,6 +9,8 @@ const {
|
|
|
9
9
|
isEmpty,
|
|
10
10
|
getParentKey,
|
|
11
11
|
extractRelationalParents,
|
|
12
|
+
getM2AItemParentPath,
|
|
13
|
+
getSelectedNodes,
|
|
12
14
|
} = require("./helpers");
|
|
13
15
|
|
|
14
16
|
const choices = ["radio", "checkboxes", "dropdown_multiple", "dropdown"];
|
|
@@ -69,11 +71,14 @@ const typeChecks = {
|
|
|
69
71
|
},
|
|
70
72
|
|
|
71
73
|
[constants.types.TIME]: (val) =>
|
|
72
|
-
typeof val === "string" &&
|
|
74
|
+
typeof val === "string" &&
|
|
75
|
+
/^([01]\d|2[0-3]):([0-5]\d):([0-5]\d)$/.test(val),
|
|
73
76
|
[constants.types.STRING]: (val) => typeof val === "string",
|
|
74
|
-
[constants.types.OBJECT]: (val) =>
|
|
77
|
+
[constants.types.OBJECT]: (val) =>
|
|
78
|
+
typeof val === "object" && val !== null && !Array.isArray(val),
|
|
75
79
|
[constants.types.ARRAY]: (val) => Array.isArray(val),
|
|
76
|
-
[constants.types.OBJECT_ID]: (val) =>
|
|
80
|
+
[constants.types.OBJECT_ID]: (val) =>
|
|
81
|
+
typeof val === "string" && /^[0-9a-fA-F]{24}$/.test(val),
|
|
77
82
|
[constants.types.MIXED]: (val) => (val ? true : false),
|
|
78
83
|
[constants.types.BUFFER]: (val) => val instanceof Buffer,
|
|
79
84
|
[constants.types.ALIAS]: (val) => (val ? true : false),
|
|
@@ -99,14 +104,18 @@ const handleMinMaxValidation = (
|
|
|
99
104
|
fieldValue?.length < ruleValue[0]
|
|
100
105
|
) {
|
|
101
106
|
message = custom_message ?? error_messages.MIN_STRING;
|
|
102
|
-
message = message
|
|
107
|
+
message = message
|
|
108
|
+
?.replace(`{field}`, fieldLabel)
|
|
109
|
+
.replace(`{min}`, ruleValue[0]);
|
|
103
110
|
} else if (
|
|
104
111
|
field.type === constants.types.NUMBER &&
|
|
105
112
|
typeChecks[constants.types.NUMBER](fieldValue) &&
|
|
106
113
|
fieldValue < ruleValue[0]
|
|
107
114
|
) {
|
|
108
115
|
message = custom_message ?? error_messages.MIN_NUMBER;
|
|
109
|
-
message = message
|
|
116
|
+
message = message
|
|
117
|
+
?.replace(`{field}`, fieldLabel)
|
|
118
|
+
.replace(`{min}`, ruleValue[0]);
|
|
110
119
|
}
|
|
111
120
|
}
|
|
112
121
|
|
|
@@ -117,14 +126,18 @@ const handleMinMaxValidation = (
|
|
|
117
126
|
fieldValue?.length > ruleValue[0]
|
|
118
127
|
) {
|
|
119
128
|
message = custom_message ?? error_messages.MAX_STRING;
|
|
120
|
-
message = message
|
|
129
|
+
message = message
|
|
130
|
+
?.replace(`{field}`, fieldLabel)
|
|
131
|
+
.replace(`{max}`, ruleValue[0]);
|
|
121
132
|
} else if (
|
|
122
133
|
field.type === constants.types.NUMBER &&
|
|
123
134
|
typeChecks[constants.types.NUMBER](fieldValue) &&
|
|
124
135
|
fieldValue > ruleValue[0]
|
|
125
136
|
) {
|
|
126
137
|
message = custom_message ?? error_messages.MAX_NUMBER;
|
|
127
|
-
message = message
|
|
138
|
+
message = message
|
|
139
|
+
?.replace(`{field}`, fieldLabel)
|
|
140
|
+
.replace(`{max}`, ruleValue[0]);
|
|
128
141
|
}
|
|
129
142
|
}
|
|
130
143
|
|
|
@@ -138,7 +151,10 @@ const handleMinMaxValidation = (
|
|
|
138
151
|
};
|
|
139
152
|
|
|
140
153
|
const isEmptyRelational = ({ api_version, value, interface }) => {
|
|
141
|
-
if (
|
|
154
|
+
if (
|
|
155
|
+
api_version === constants.API_VERSION.V1 &&
|
|
156
|
+
interface !== constants.interfaces.TRANSLATIONS
|
|
157
|
+
) {
|
|
142
158
|
if (interface === constants.interfaces.MANY_TO_ANY) {
|
|
143
159
|
return (
|
|
144
160
|
value &&
|
|
@@ -157,7 +173,11 @@ const isEmptyRelational = ({ api_version, value, interface }) => {
|
|
|
157
173
|
return value?.length > 0;
|
|
158
174
|
}
|
|
159
175
|
} else {
|
|
160
|
-
return
|
|
176
|
+
return (
|
|
177
|
+
value?.create?.length > 0 ||
|
|
178
|
+
value?.existing?.length > 0 ||
|
|
179
|
+
value?.update?.length > 0
|
|
180
|
+
);
|
|
161
181
|
}
|
|
162
182
|
return false;
|
|
163
183
|
};
|
|
@@ -172,11 +192,13 @@ const validateMetaRules = (
|
|
|
172
192
|
onlyFormFields,
|
|
173
193
|
apiVersion,
|
|
174
194
|
fieldOptions,
|
|
175
|
-
formData
|
|
195
|
+
formData,
|
|
196
|
+
language_codes = []
|
|
176
197
|
) => {
|
|
177
198
|
const fieldValue = providedValue;
|
|
199
|
+
|
|
178
200
|
const { required = false, nullable = false, options } = field?.meta ?? {};
|
|
179
|
-
const
|
|
201
|
+
const relational_interfaces = [
|
|
180
202
|
constants.interfaces.FILES,
|
|
181
203
|
constants.interfaces.FILE,
|
|
182
204
|
constants.interfaces.FILE_IMAGE,
|
|
@@ -185,13 +207,17 @@ const validateMetaRules = (
|
|
|
185
207
|
constants.interfaces.MANY_TO_ONE,
|
|
186
208
|
constants.interfaces.MANY_TO_ANY,
|
|
187
209
|
constants.interfaces.TRANSLATIONS,
|
|
188
|
-
]
|
|
210
|
+
];
|
|
211
|
+
const isRelational = relational_interfaces.includes(field?.meta?.interface);
|
|
189
212
|
|
|
190
213
|
if (
|
|
191
214
|
choices.includes(field?.meta?.interface) &&
|
|
192
215
|
(!options?.choices || !options?.choices?.length > 0)
|
|
193
216
|
) {
|
|
194
|
-
const message = error_messages.ADD_CHOICE.replace(
|
|
217
|
+
const message = error_messages.ADD_CHOICE.replace(
|
|
218
|
+
`{field}`,
|
|
219
|
+
formatLabel(field.display_label)
|
|
220
|
+
);
|
|
195
221
|
addError(
|
|
196
222
|
currentPath,
|
|
197
223
|
{
|
|
@@ -214,7 +240,10 @@ const validateMetaRules = (
|
|
|
214
240
|
: true;
|
|
215
241
|
|
|
216
242
|
if ((required && isEmpty(fieldValue)) || !isValidRelational) {
|
|
217
|
-
const message = error_messages.REQUIRED.replace(
|
|
243
|
+
const message = error_messages.REQUIRED.replace(
|
|
244
|
+
`{field}`,
|
|
245
|
+
formatLabel(field.display_label)
|
|
246
|
+
);
|
|
218
247
|
addError(
|
|
219
248
|
currentPath,
|
|
220
249
|
{
|
|
@@ -244,15 +273,79 @@ const validateMetaRules = (
|
|
|
244
273
|
);
|
|
245
274
|
}
|
|
246
275
|
|
|
276
|
+
if (
|
|
277
|
+
!fieldValue &&
|
|
278
|
+
!field?.meta?.hidden &&
|
|
279
|
+
![
|
|
280
|
+
constants.interfaces.FILES,
|
|
281
|
+
constants.interfaces.FILE,
|
|
282
|
+
constants.interfaces.FILE_IMAGE,
|
|
283
|
+
constants.interfaces.MANY_TO_MANY,
|
|
284
|
+
constants.interfaces.ONE_TO_MANY,
|
|
285
|
+
constants.interfaces.MANY_TO_ONE,
|
|
286
|
+
constants.interfaces.MANY_TO_ANY,
|
|
287
|
+
].includes(field?.meta?.interface)
|
|
288
|
+
) {
|
|
289
|
+
const isTranslationChild =
|
|
290
|
+
field?.meta?.interface === constants.interfaces.TRANSLATIONS;
|
|
291
|
+
|
|
292
|
+
getSelectedNodes({
|
|
293
|
+
node: field,
|
|
294
|
+
skipFn: (node) =>
|
|
295
|
+
node.meta?.interface === constants.interfaces.MANY_TO_ANY,
|
|
296
|
+
conditionFn: (node) => node.meta?.required === true,
|
|
297
|
+
mapFn: (node) => ({ key: node.key, label: node.display_label }),
|
|
298
|
+
actionFn: (node) => {
|
|
299
|
+
let fPath = node.key;
|
|
300
|
+
|
|
301
|
+
if (fPath.includes("update.")) return;
|
|
302
|
+
|
|
303
|
+
const label = formatLabel(node.display_label);
|
|
304
|
+
const message = error_messages.REQUIRED.replace("{field}", label);
|
|
305
|
+
|
|
306
|
+
const buildError = (path, translated) => {
|
|
307
|
+
let obj = {
|
|
308
|
+
label,
|
|
309
|
+
fieldPath: path,
|
|
310
|
+
description: "",
|
|
311
|
+
message,
|
|
312
|
+
};
|
|
313
|
+
if (translated) {
|
|
314
|
+
obj.translation_path = translated;
|
|
315
|
+
}
|
|
316
|
+
addError(path, obj, node);
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
const isMultiLang =
|
|
320
|
+
isTranslationChild &&
|
|
321
|
+
Array.isArray(language_codes) &&
|
|
322
|
+
language_codes.length > 1;
|
|
323
|
+
|
|
324
|
+
if (isMultiLang) {
|
|
325
|
+
language_codes.forEach((lang, index) => {
|
|
326
|
+
const langPath = fPath.replace("create.", `create[${index}].`);
|
|
327
|
+
const transformedPath = fPath.replace("create.", `${lang}.`);
|
|
328
|
+
buildError(langPath, transformedPath);
|
|
329
|
+
});
|
|
330
|
+
} else {
|
|
331
|
+
const singlePath = fPath.replace(
|
|
332
|
+
"create.",
|
|
333
|
+
isTranslationChild ? "create[lang_code]." : "create[0]."
|
|
334
|
+
);
|
|
335
|
+
buildError(singlePath);
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
|
|
247
341
|
const validType =
|
|
248
342
|
field?.alternateType?.length > 0
|
|
249
|
-
? [field.type, ...field?.alternateType].some((type) =>
|
|
250
|
-
|
|
343
|
+
? [field.type, ...field?.alternateType].some((type) =>
|
|
344
|
+
typeChecks[type](fieldValue)
|
|
345
|
+
)
|
|
346
|
+
: typeChecks[field.type](fieldValue, { key: currentPath, updateValue });
|
|
251
347
|
|
|
252
|
-
if (
|
|
253
|
-
!isEmpty(fieldValue) &&
|
|
254
|
-
!validType
|
|
255
|
-
) {
|
|
348
|
+
if (!isEmpty(fieldValue) && !validType) {
|
|
256
349
|
const message = error_messages.INVALID_TYPE.replace(
|
|
257
350
|
`{field}`,
|
|
258
351
|
formatLabel(field.display_label)
|
|
@@ -281,9 +374,9 @@ const handleRegexValidation = (
|
|
|
281
374
|
) => {
|
|
282
375
|
let message = "";
|
|
283
376
|
const fieldLabel = formatLabel(field.display_label);
|
|
284
|
-
const flags = `${rule.options.case_sensitive ? "" : "i"}${
|
|
285
|
-
rule.options.
|
|
286
|
-
}`;
|
|
377
|
+
const flags = `${rule.options.case_sensitive ? "" : "i"}${
|
|
378
|
+
rule.options.multiline ? "m" : ""
|
|
379
|
+
}${rule.options.global ? "g" : ""}`;
|
|
287
380
|
const regex = new RegExp(rule.value[0], flags);
|
|
288
381
|
|
|
289
382
|
const isValid = (() => {
|
|
@@ -293,22 +386,32 @@ const handleRegexValidation = (
|
|
|
293
386
|
return regex.test(fieldValue);
|
|
294
387
|
case constants.regexTypes.START_WITH:
|
|
295
388
|
message = custom_message ?? error_messages.REGEX_START_WITH;
|
|
296
|
-
return fieldValue?.startsWith(
|
|
389
|
+
return fieldValue?.startsWith(
|
|
390
|
+
rule.value[0].replace(/^\//, "").replace(/\/$/, "")
|
|
391
|
+
);
|
|
297
392
|
case constants.regexTypes.ENDS_WITH:
|
|
298
393
|
message = custom_message ?? error_messages.REGEX_ENDS_WITH;
|
|
299
|
-
return fieldValue?.endsWith(
|
|
394
|
+
return fieldValue?.endsWith(
|
|
395
|
+
rule.value[0].replace(/^\//, "").replace(/\/$/, "")
|
|
396
|
+
);
|
|
300
397
|
case constants.regexTypes.CONTAINS:
|
|
301
398
|
message = custom_message ?? error_messages.REGEX_CONTAINS;
|
|
302
399
|
return regex.test(fieldValue);
|
|
303
400
|
case constants.regexTypes.EXACT:
|
|
304
401
|
message = custom_message ?? error_messages.REGEX_EXACT;
|
|
305
|
-
return
|
|
402
|
+
return (
|
|
403
|
+
fieldValue === rule.value[0].replace(/^\//, "").replace(/\/$/, "")
|
|
404
|
+
);
|
|
306
405
|
case constants.regexTypes.NOT_START_WITH:
|
|
307
406
|
message = custom_message ?? error_messages.REGEX_NOT_START_WITH;
|
|
308
|
-
return !fieldValue?.startsWith(
|
|
407
|
+
return !fieldValue?.startsWith(
|
|
408
|
+
rule.value[0].replace(/^\//, "").replace(/\/$/, "")
|
|
409
|
+
);
|
|
309
410
|
case constants.regexTypes.NOT_ENDS_WITH:
|
|
310
411
|
message = custom_message ?? error_messages.REGEX_NOT_ENDS_WITH;
|
|
311
|
-
return !fieldValue?.endsWith(
|
|
412
|
+
return !fieldValue?.endsWith(
|
|
413
|
+
rule.value[0].replace(/^\//, "").replace(/\/$/, "")
|
|
414
|
+
);
|
|
312
415
|
case constants.regexTypes.NOT_CONTAINS:
|
|
313
416
|
message = custom_message ?? error_messages.REGEX_NOT_CONTAINS;
|
|
314
417
|
return !regex.test(fieldValue);
|
|
@@ -318,7 +421,9 @@ const handleRegexValidation = (
|
|
|
318
421
|
})();
|
|
319
422
|
|
|
320
423
|
if (!isValid) {
|
|
321
|
-
message = message
|
|
424
|
+
message = message
|
|
425
|
+
?.replace(`{field}`, fieldLabel)
|
|
426
|
+
?.replace(`{value}`, rule.value[0]);
|
|
322
427
|
addError(
|
|
323
428
|
currentPath,
|
|
324
429
|
{ label: fieldLabel, fieldPath: currentPath, description: "", message },
|
|
@@ -358,10 +463,9 @@ const validateOperatorRule = (
|
|
|
358
463
|
const date = new Date(value);
|
|
359
464
|
date.setHours(0, 0, 0, 0);
|
|
360
465
|
if (forMessage) {
|
|
361
|
-
return `${date.getUTCFullYear()}-${String(
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
)}-${String(date.getUTCDate()).padStart(2, "0")}`;
|
|
466
|
+
return `${date.getUTCFullYear()}-${String(
|
|
467
|
+
date.getUTCMonth() + 1
|
|
468
|
+
).padStart(2, "0")}-${String(date.getUTCDate()).padStart(2, "0")}`;
|
|
365
469
|
}
|
|
366
470
|
return Math.floor(date.getTime() / 1000);
|
|
367
471
|
}
|
|
@@ -370,13 +474,13 @@ const validateOperatorRule = (
|
|
|
370
474
|
|
|
371
475
|
if (forMessage) {
|
|
372
476
|
return (
|
|
373
|
-
`${date.getUTCFullYear()}-${String(date.getUTCMonth() + 1).padStart(
|
|
374
|
-
date.getUTCDate()
|
|
375
|
-
).padStart(2, "0")} ` +
|
|
376
|
-
`${String(date.getUTCHours()).padStart(2, "0")}:${String(date.getUTCMinutes()).padStart(
|
|
477
|
+
`${date.getUTCFullYear()}-${String(date.getUTCMonth() + 1).padStart(
|
|
377
478
|
2,
|
|
378
479
|
"0"
|
|
379
|
-
)}
|
|
480
|
+
)}-${String(date.getUTCDate()).padStart(2, "0")} ` +
|
|
481
|
+
`${String(date.getUTCHours()).padStart(2, "0")}:${String(
|
|
482
|
+
date.getUTCMinutes()
|
|
483
|
+
).padStart(2, "0")}:${String(date.getUTCSeconds()).padStart(2, "0")}`
|
|
380
484
|
);
|
|
381
485
|
}
|
|
382
486
|
|
|
@@ -400,7 +504,9 @@ const validateOperatorRule = (
|
|
|
400
504
|
|
|
401
505
|
const fieldValueParsed = getComparableValue(fieldValue);
|
|
402
506
|
|
|
403
|
-
const messageValue = Array.isArray(rule.value)
|
|
507
|
+
const messageValue = Array.isArray(rule.value)
|
|
508
|
+
? rule.value.join(", ")
|
|
509
|
+
: String(rule.value);
|
|
404
510
|
let message = "";
|
|
405
511
|
|
|
406
512
|
switch (rule.options.operator) {
|
|
@@ -481,7 +587,9 @@ const validateOperatorRule = (
|
|
|
481
587
|
case constants.operatorTypes.EXISTS:
|
|
482
588
|
message = custom_message ?? error_messages.EXISTS;
|
|
483
589
|
message = message?.replace(`{field}`, formatLabel(field.display_label));
|
|
484
|
-
valid = rule.value[0]
|
|
590
|
+
valid = rule.value[0]
|
|
591
|
+
? fieldValue !== undefined
|
|
592
|
+
: fieldValue === undefined;
|
|
485
593
|
break;
|
|
486
594
|
case constants.operatorTypes.TYPE:
|
|
487
595
|
message = custom_message ?? error_messages.TYPE;
|
|
@@ -545,18 +653,33 @@ const generateErrorMessage = (
|
|
|
545
653
|
return message;
|
|
546
654
|
};
|
|
547
655
|
|
|
548
|
-
const handleRule = (
|
|
656
|
+
const handleRule = (
|
|
657
|
+
rule,
|
|
658
|
+
field,
|
|
659
|
+
value,
|
|
660
|
+
addError,
|
|
661
|
+
currentPath,
|
|
662
|
+
formData,
|
|
663
|
+
error_messages
|
|
664
|
+
) => {
|
|
549
665
|
const ruleValue = rule?.value;
|
|
550
|
-
let messageValue = Array.isArray(ruleValue)
|
|
666
|
+
let messageValue = Array.isArray(ruleValue)
|
|
667
|
+
? ruleValue.join(", ")
|
|
668
|
+
: String(ruleValue);
|
|
551
669
|
const fieldLabel = formatLabel(field.display_label);
|
|
552
670
|
const custom_message = rule?.custom_message;
|
|
553
671
|
|
|
554
|
-
if (
|
|
672
|
+
if (
|
|
673
|
+
rule.case === constants.rulesTypes.ONE_OF &&
|
|
674
|
+
choices.includes(field?.meta?.interface)
|
|
675
|
+
) {
|
|
555
676
|
const fieldChoices = field?.meta?.options?.choices || [];
|
|
556
677
|
const ruleValues = Array.isArray(ruleValue) ? ruleValue : [ruleValue];
|
|
557
678
|
|
|
558
679
|
const labelValuePairs = ruleValues.map((val) => {
|
|
559
|
-
const matched = fieldChoices.find(
|
|
680
|
+
const matched = fieldChoices.find(
|
|
681
|
+
(item) => String(item.value) === String(val)
|
|
682
|
+
);
|
|
560
683
|
return matched ? `${matched.label} (${matched.value})` : val;
|
|
561
684
|
});
|
|
562
685
|
|
|
@@ -586,7 +709,8 @@ const handleRule = (rule, field, value, addError, currentPath, formData, error_m
|
|
|
586
709
|
if (value) addValidationError("EMPTY", {}, custom_message);
|
|
587
710
|
break;
|
|
588
711
|
case constants.rulesTypes.NOT_EMPTY:
|
|
589
|
-
if (!value || value.length === 0)
|
|
712
|
+
if (!value || value.length === 0)
|
|
713
|
+
addValidationError("NOT_EMPTY", {}, custom_message);
|
|
590
714
|
break;
|
|
591
715
|
case constants.rulesTypes.ONE_OF:
|
|
592
716
|
if (!ruleValue?.includes(value))
|
|
@@ -594,10 +718,15 @@ const handleRule = (rule, field, value, addError, currentPath, formData, error_m
|
|
|
594
718
|
break;
|
|
595
719
|
case constants.rulesTypes.NOT_ONE_OF:
|
|
596
720
|
if (ruleValue?.includes(value))
|
|
597
|
-
addValidationError(
|
|
721
|
+
addValidationError(
|
|
722
|
+
"NOT_ONE_OF",
|
|
723
|
+
{ value: messageValue },
|
|
724
|
+
custom_message
|
|
725
|
+
);
|
|
598
726
|
break;
|
|
599
727
|
case constants.rulesTypes.NOT_ALLOWED:
|
|
600
|
-
if (ruleValue?.includes(value))
|
|
728
|
+
if (ruleValue?.includes(value))
|
|
729
|
+
addValidationError("NOT_ALLOWED", {}, custom_message);
|
|
601
730
|
break;
|
|
602
731
|
case constants.rulesTypes.MIN:
|
|
603
732
|
case constants.rulesTypes.MAX:
|
|
@@ -650,11 +779,15 @@ const validateField = (
|
|
|
650
779
|
error_messages,
|
|
651
780
|
onlyFormFields,
|
|
652
781
|
apiVersion,
|
|
653
|
-
fieldOptions
|
|
782
|
+
fieldOptions,
|
|
783
|
+
language_codes
|
|
654
784
|
) => {
|
|
655
785
|
if (onlyFormFields == true && (value === undefined || value === null)) return;
|
|
656
786
|
|
|
657
|
-
const
|
|
787
|
+
const { is_m2a_item } = field?.meta;
|
|
788
|
+
const currentPath = fieldPath
|
|
789
|
+
? `${fieldPath}.${field.key.split(".").pop()}`
|
|
790
|
+
: field.key;
|
|
658
791
|
const fieldLabel = formatLabel(field.display_label);
|
|
659
792
|
|
|
660
793
|
validateMetaRules(
|
|
@@ -667,7 +800,8 @@ const validateField = (
|
|
|
667
800
|
onlyFormFields,
|
|
668
801
|
apiVersion,
|
|
669
802
|
fieldOptions,
|
|
670
|
-
formData
|
|
803
|
+
formData,
|
|
804
|
+
language_codes
|
|
671
805
|
);
|
|
672
806
|
|
|
673
807
|
if (
|
|
@@ -676,29 +810,12 @@ const validateField = (
|
|
|
676
810
|
value &&
|
|
677
811
|
typeChecks[constants.types.OBJECT](value)
|
|
678
812
|
) {
|
|
679
|
-
const isManyToAnyItem =
|
|
680
|
-
field.display_label === "item" && field?.meta?.interface === constants.interfaces.MANY_TO_ANY;
|
|
681
|
-
|
|
682
|
-
const defaultKeys = new Set([
|
|
683
|
-
"_id",
|
|
684
|
-
"created_at",
|
|
685
|
-
"updated_at",
|
|
686
|
-
"__v",
|
|
687
|
-
"created_by",
|
|
688
|
-
"updated_by",
|
|
689
|
-
"nox_created_by",
|
|
690
|
-
"nox_updated_by",
|
|
691
|
-
"nox_created_at",
|
|
692
|
-
"nox_updated_at",
|
|
693
|
-
]);
|
|
694
|
-
|
|
695
813
|
let itemSchemaId = null;
|
|
696
814
|
|
|
697
|
-
if (
|
|
698
|
-
const
|
|
699
|
-
if (
|
|
700
|
-
|
|
701
|
-
itemSchemaId = childField?.schema_id ?? null;
|
|
815
|
+
if (is_m2a_item) {
|
|
816
|
+
const fieldPath = getM2AItemParentPath(currentPath);
|
|
817
|
+
if (fieldPath) {
|
|
818
|
+
itemSchemaId = getValue(formData, fieldPath)?.collection_id;
|
|
702
819
|
}
|
|
703
820
|
}
|
|
704
821
|
|
|
@@ -717,7 +834,8 @@ const validateField = (
|
|
|
717
834
|
error_messages,
|
|
718
835
|
onlyFormFields,
|
|
719
836
|
apiVersion,
|
|
720
|
-
fieldOptions
|
|
837
|
+
fieldOptions,
|
|
838
|
+
language_codes
|
|
721
839
|
)
|
|
722
840
|
);
|
|
723
841
|
} else if (field.type === constants.types.ARRAY && Array.isArray(value)) {
|
|
@@ -727,7 +845,14 @@ const validateField = (
|
|
|
727
845
|
const itemPath = `${currentPath}[${index}]`;
|
|
728
846
|
|
|
729
847
|
if (choices.includes(field?.meta?.interface) && !isEmpty(item)) {
|
|
730
|
-
applyValidations(
|
|
848
|
+
applyValidations(
|
|
849
|
+
field,
|
|
850
|
+
item,
|
|
851
|
+
addError,
|
|
852
|
+
itemPath,
|
|
853
|
+
formData,
|
|
854
|
+
error_messages
|
|
855
|
+
);
|
|
731
856
|
}
|
|
732
857
|
|
|
733
858
|
if (!typeChecks[itemType](item)) {
|
|
@@ -760,13 +885,23 @@ const validateField = (
|
|
|
760
885
|
error_messages,
|
|
761
886
|
onlyFormFields,
|
|
762
887
|
apiVersion,
|
|
763
|
-
fieldOptions
|
|
888
|
+
fieldOptions,
|
|
889
|
+
language_codes
|
|
764
890
|
)
|
|
765
891
|
);
|
|
766
892
|
});
|
|
767
893
|
}
|
|
768
894
|
} else {
|
|
769
|
-
if (
|
|
895
|
+
if (
|
|
896
|
+
!applyValidations(
|
|
897
|
+
field,
|
|
898
|
+
value,
|
|
899
|
+
addError,
|
|
900
|
+
currentPath,
|
|
901
|
+
formData,
|
|
902
|
+
error_messages
|
|
903
|
+
)
|
|
904
|
+
) {
|
|
770
905
|
addError(
|
|
771
906
|
currentPath,
|
|
772
907
|
{
|
|
@@ -781,11 +916,32 @@ const validateField = (
|
|
|
781
916
|
}
|
|
782
917
|
};
|
|
783
918
|
|
|
784
|
-
const applyValidations = (
|
|
785
|
-
|
|
919
|
+
const applyValidations = (
|
|
920
|
+
field,
|
|
921
|
+
value,
|
|
922
|
+
addError,
|
|
923
|
+
currentPath,
|
|
924
|
+
formData,
|
|
925
|
+
error_messages
|
|
926
|
+
) => {
|
|
927
|
+
if (
|
|
928
|
+
!field.validations ||
|
|
929
|
+
value === null ||
|
|
930
|
+
value === undefined ||
|
|
931
|
+
value === ""
|
|
932
|
+
)
|
|
933
|
+
return true;
|
|
786
934
|
return !field.validations.some(
|
|
787
935
|
(rule) =>
|
|
788
|
-
handleRule(
|
|
936
|
+
handleRule(
|
|
937
|
+
rule,
|
|
938
|
+
field,
|
|
939
|
+
value,
|
|
940
|
+
addError,
|
|
941
|
+
currentPath,
|
|
942
|
+
formData,
|
|
943
|
+
error_messages
|
|
944
|
+
) === false
|
|
789
945
|
);
|
|
790
946
|
};
|
|
791
947
|
|
|
@@ -808,6 +964,10 @@ const schema = {
|
|
|
808
964
|
language: { type: constants.types.STRING, array_type: null },
|
|
809
965
|
maxLevel: { type: constants.types.NUMBER, array_type: null },
|
|
810
966
|
onlyFormFields: { type: constants.types.BOOLEAN, array_type: null },
|
|
967
|
+
language_codes: {
|
|
968
|
+
type: constants.types.ARRAY,
|
|
969
|
+
array_type: constants.types.OBJECT_ID,
|
|
970
|
+
},
|
|
811
971
|
};
|
|
812
972
|
|
|
813
973
|
const validate = (data) => {
|
|
@@ -833,7 +993,8 @@ const validate = (data) => {
|
|
|
833
993
|
} = data;
|
|
834
994
|
|
|
835
995
|
const error_messages =
|
|
836
|
-
constants.LOCALE_MESSAGES[language] ??
|
|
996
|
+
constants.LOCALE_MESSAGES[language] ??
|
|
997
|
+
constants.LOCALE_MESSAGES[constants.LANGUAGES.en];
|
|
837
998
|
|
|
838
999
|
let result = { status: true, errors: {}, data: structuredClone(formData) };
|
|
839
1000
|
|
|
@@ -850,12 +1011,18 @@ const validate = (data) => {
|
|
|
850
1011
|
const secondParentField = fields.find((f) => f.path === secondParent);
|
|
851
1012
|
if (
|
|
852
1013
|
secondParentField &&
|
|
853
|
-
secondParentField?.meta?.interface ===
|
|
1014
|
+
secondParentField?.meta?.interface ===
|
|
1015
|
+
constants.interfaces.TRANSLATIONS
|
|
854
1016
|
) {
|
|
855
1017
|
const languageKey = secondParentField?.meta?.options?.language_field;
|
|
856
1018
|
const firstParentValue = getValue(formData, firstParent);
|
|
857
|
-
if (
|
|
858
|
-
|
|
1019
|
+
if (
|
|
1020
|
+
firstParentValue &&
|
|
1021
|
+
typeChecks[constants.types.OBJECT](firstParentValue)
|
|
1022
|
+
) {
|
|
1023
|
+
const codeKey = Object.keys(firstParentValue).find((key) =>
|
|
1024
|
+
key.includes(languageKey)
|
|
1025
|
+
);
|
|
859
1026
|
const codeValue = codeKey ? firstParentValue[codeKey] : null;
|
|
860
1027
|
if (codeValue) {
|
|
861
1028
|
const translation_key = fieldPath.replace(
|
|
@@ -881,7 +1048,10 @@ const validate = (data) => {
|
|
|
881
1048
|
// Validate Data
|
|
882
1049
|
const defaultField = { meta: { hidden: false } };
|
|
883
1050
|
if (!data) {
|
|
884
|
-
const message = error_messages.REQUIRED.replace(
|
|
1051
|
+
const message = error_messages.REQUIRED.replace(
|
|
1052
|
+
`{field}`,
|
|
1053
|
+
formatLabel("data")
|
|
1054
|
+
);
|
|
885
1055
|
addError(
|
|
886
1056
|
"data",
|
|
887
1057
|
{
|
|
@@ -897,10 +1067,10 @@ const validate = (data) => {
|
|
|
897
1067
|
|
|
898
1068
|
// validate data type
|
|
899
1069
|
if (!typeChecks[constants.types.OBJECT](data)) {
|
|
900
|
-
const message = error_messages.INVALID_TYPE.replace(
|
|
901
|
-
`{
|
|
902
|
-
|
|
903
|
-
);
|
|
1070
|
+
const message = error_messages.INVALID_TYPE.replace(
|
|
1071
|
+
`{field}`,
|
|
1072
|
+
formatLabel("data")
|
|
1073
|
+
).replace(`{type}`, constants.types.OBJECT);
|
|
904
1074
|
addError(
|
|
905
1075
|
"data",
|
|
906
1076
|
{
|
|
@@ -920,7 +1090,10 @@ const validate = (data) => {
|
|
|
920
1090
|
const fieldValue = data[key];
|
|
921
1091
|
// Skip empty values
|
|
922
1092
|
if (fieldValue == null || fieldValue == undefined) {
|
|
923
|
-
const message = error_messages.REQUIRED.replace(
|
|
1093
|
+
const message = error_messages.REQUIRED.replace(
|
|
1094
|
+
`{field}`,
|
|
1095
|
+
formatLabel(key)
|
|
1096
|
+
);
|
|
924
1097
|
addError(
|
|
925
1098
|
key,
|
|
926
1099
|
{
|
|
@@ -936,10 +1109,10 @@ const validate = (data) => {
|
|
|
936
1109
|
|
|
937
1110
|
// Validate field type
|
|
938
1111
|
if (!typeChecks[expectedType] || !typeChecks[expectedType](fieldValue)) {
|
|
939
|
-
const message = error_messages.INVALID_TYPE.replace(
|
|
940
|
-
`{
|
|
941
|
-
|
|
942
|
-
);
|
|
1112
|
+
const message = error_messages.INVALID_TYPE.replace(
|
|
1113
|
+
`{field}`,
|
|
1114
|
+
key
|
|
1115
|
+
).replace(`{type}`, expectedType);
|
|
943
1116
|
addError(
|
|
944
1117
|
key,
|
|
945
1118
|
{
|
|
@@ -959,7 +1132,11 @@ const validate = (data) => {
|
|
|
959
1132
|
// Determine the expected type of array items
|
|
960
1133
|
const arrayItemType = schema[key].array_type; // Define item types like "relations[]": "object"
|
|
961
1134
|
|
|
962
|
-
if (
|
|
1135
|
+
if (
|
|
1136
|
+
arrayItemType &&
|
|
1137
|
+
typeChecks[arrayItemType] &&
|
|
1138
|
+
!typeChecks[arrayItemType](item)
|
|
1139
|
+
) {
|
|
963
1140
|
const message = error_messages.INVALID_TYPE.replace(
|
|
964
1141
|
`{field}`,
|
|
965
1142
|
`${key}[${index}]`
|
|
@@ -981,10 +1158,10 @@ const validate = (data) => {
|
|
|
981
1158
|
|
|
982
1159
|
// Validate API Version
|
|
983
1160
|
if (!constants.API_VERSIONS.includes(apiVersion)) {
|
|
984
|
-
const message = error_messages.IN.replace(
|
|
985
|
-
`{
|
|
986
|
-
|
|
987
|
-
);
|
|
1161
|
+
const message = error_messages.IN.replace(
|
|
1162
|
+
`{field}`,
|
|
1163
|
+
formatLabel("apiVersion")
|
|
1164
|
+
).replace(`{value}`, constants.API_VERSIONS.join(", "));
|
|
988
1165
|
addError(
|
|
989
1166
|
"apiVersion",
|
|
990
1167
|
{
|
|
@@ -1008,7 +1185,9 @@ const validate = (data) => {
|
|
|
1008
1185
|
let allFields = isSeparatedFields ? [] : fields;
|
|
1009
1186
|
|
|
1010
1187
|
if (!isSeparatedFields) {
|
|
1011
|
-
schemaFields = fields.filter(
|
|
1188
|
+
schemaFields = fields.filter(
|
|
1189
|
+
(field) => field?.schema_id?.toString() === formId?.toString()
|
|
1190
|
+
);
|
|
1012
1191
|
}
|
|
1013
1192
|
|
|
1014
1193
|
let currentDepthMap = new Map();
|
|
@@ -1059,7 +1238,8 @@ const validate = (data) => {
|
|
|
1059
1238
|
error_messages,
|
|
1060
1239
|
onlyFormFields,
|
|
1061
1240
|
apiVersion,
|
|
1062
|
-
fieldOptions
|
|
1241
|
+
fieldOptions,
|
|
1242
|
+
data.language_codes
|
|
1063
1243
|
);
|
|
1064
1244
|
});
|
|
1065
1245
|
return result;
|