nox-validation 1.3.1 → 1.3.2
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 +278 -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,78 @@ const validateMetaRules = (
|
|
|
244
273
|
);
|
|
245
274
|
}
|
|
246
275
|
|
|
276
|
+
if (
|
|
277
|
+
!fieldValue &&
|
|
278
|
+
![
|
|
279
|
+
constants.interfaces.FILES,
|
|
280
|
+
constants.interfaces.FILE,
|
|
281
|
+
constants.interfaces.FILE_IMAGE,
|
|
282
|
+
constants.interfaces.MANY_TO_MANY,
|
|
283
|
+
constants.interfaces.ONE_TO_MANY,
|
|
284
|
+
constants.interfaces.MANY_TO_ONE,
|
|
285
|
+
constants.interfaces.MANY_TO_ANY,
|
|
286
|
+
].includes(field?.meta?.interface)
|
|
287
|
+
) {
|
|
288
|
+
const isTranslationChild =
|
|
289
|
+
field?.meta?.interface === constants.interfaces.TRANSLATIONS;
|
|
290
|
+
|
|
291
|
+
getSelectedNodes({
|
|
292
|
+
node: field,
|
|
293
|
+
skipFn: (node) =>
|
|
294
|
+
node.meta?.interface === constants.interfaces.MANY_TO_ANY,
|
|
295
|
+
conditionFn: (node) => node.meta?.required === true,
|
|
296
|
+
mapFn: (node) => ({ key: node.key, label: node.display_label }),
|
|
297
|
+
actionFn: (node) => {
|
|
298
|
+
let fPath = node.key;
|
|
299
|
+
|
|
300
|
+
if (fPath.includes("update.")) return;
|
|
301
|
+
|
|
302
|
+
const label = formatLabel(node.display_label);
|
|
303
|
+
const message = error_messages.REQUIRED.replace("{field}", label);
|
|
304
|
+
|
|
305
|
+
const buildError = (path, translated) => {
|
|
306
|
+
let obj = {
|
|
307
|
+
label,
|
|
308
|
+
fieldPath: path,
|
|
309
|
+
description: "",
|
|
310
|
+
message,
|
|
311
|
+
};
|
|
312
|
+
if (translated) {
|
|
313
|
+
obj.translation_path = translated;
|
|
314
|
+
}
|
|
315
|
+
addError(path, obj, node);
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
const isMultiLang =
|
|
319
|
+
isTranslationChild &&
|
|
320
|
+
Array.isArray(language_codes) &&
|
|
321
|
+
language_codes.length > 1;
|
|
322
|
+
|
|
323
|
+
if (isMultiLang) {
|
|
324
|
+
language_codes.forEach((lang, index) => {
|
|
325
|
+
const langPath = fPath.replace("create.", `create[${index}].`);
|
|
326
|
+
const transformedPath = fPath.replace("create.", `${lang}.`);
|
|
327
|
+
buildError(langPath, transformedPath);
|
|
328
|
+
});
|
|
329
|
+
} else {
|
|
330
|
+
const singlePath = fPath.replace(
|
|
331
|
+
"create.",
|
|
332
|
+
isTranslationChild ? "create[lang_code]." : "create[0]."
|
|
333
|
+
);
|
|
334
|
+
buildError(singlePath);
|
|
335
|
+
}
|
|
336
|
+
},
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
247
340
|
const validType =
|
|
248
341
|
field?.alternateType?.length > 0
|
|
249
|
-
? [field.type, ...field?.alternateType].some((type) =>
|
|
250
|
-
|
|
342
|
+
? [field.type, ...field?.alternateType].some((type) =>
|
|
343
|
+
typeChecks[type](fieldValue)
|
|
344
|
+
)
|
|
345
|
+
: typeChecks[field.type](fieldValue, { key: currentPath, updateValue });
|
|
251
346
|
|
|
252
|
-
if (
|
|
253
|
-
!isEmpty(fieldValue) &&
|
|
254
|
-
!validType
|
|
255
|
-
) {
|
|
347
|
+
if (!isEmpty(fieldValue) && !validType) {
|
|
256
348
|
const message = error_messages.INVALID_TYPE.replace(
|
|
257
349
|
`{field}`,
|
|
258
350
|
formatLabel(field.display_label)
|
|
@@ -281,9 +373,9 @@ const handleRegexValidation = (
|
|
|
281
373
|
) => {
|
|
282
374
|
let message = "";
|
|
283
375
|
const fieldLabel = formatLabel(field.display_label);
|
|
284
|
-
const flags = `${rule.options.case_sensitive ? "" : "i"}${
|
|
285
|
-
rule.options.
|
|
286
|
-
}`;
|
|
376
|
+
const flags = `${rule.options.case_sensitive ? "" : "i"}${
|
|
377
|
+
rule.options.multiline ? "m" : ""
|
|
378
|
+
}${rule.options.global ? "g" : ""}`;
|
|
287
379
|
const regex = new RegExp(rule.value[0], flags);
|
|
288
380
|
|
|
289
381
|
const isValid = (() => {
|
|
@@ -293,22 +385,32 @@ const handleRegexValidation = (
|
|
|
293
385
|
return regex.test(fieldValue);
|
|
294
386
|
case constants.regexTypes.START_WITH:
|
|
295
387
|
message = custom_message ?? error_messages.REGEX_START_WITH;
|
|
296
|
-
return fieldValue?.startsWith(
|
|
388
|
+
return fieldValue?.startsWith(
|
|
389
|
+
rule.value[0].replace(/^\//, "").replace(/\/$/, "")
|
|
390
|
+
);
|
|
297
391
|
case constants.regexTypes.ENDS_WITH:
|
|
298
392
|
message = custom_message ?? error_messages.REGEX_ENDS_WITH;
|
|
299
|
-
return fieldValue?.endsWith(
|
|
393
|
+
return fieldValue?.endsWith(
|
|
394
|
+
rule.value[0].replace(/^\//, "").replace(/\/$/, "")
|
|
395
|
+
);
|
|
300
396
|
case constants.regexTypes.CONTAINS:
|
|
301
397
|
message = custom_message ?? error_messages.REGEX_CONTAINS;
|
|
302
398
|
return regex.test(fieldValue);
|
|
303
399
|
case constants.regexTypes.EXACT:
|
|
304
400
|
message = custom_message ?? error_messages.REGEX_EXACT;
|
|
305
|
-
return
|
|
401
|
+
return (
|
|
402
|
+
fieldValue === rule.value[0].replace(/^\//, "").replace(/\/$/, "")
|
|
403
|
+
);
|
|
306
404
|
case constants.regexTypes.NOT_START_WITH:
|
|
307
405
|
message = custom_message ?? error_messages.REGEX_NOT_START_WITH;
|
|
308
|
-
return !fieldValue?.startsWith(
|
|
406
|
+
return !fieldValue?.startsWith(
|
|
407
|
+
rule.value[0].replace(/^\//, "").replace(/\/$/, "")
|
|
408
|
+
);
|
|
309
409
|
case constants.regexTypes.NOT_ENDS_WITH:
|
|
310
410
|
message = custom_message ?? error_messages.REGEX_NOT_ENDS_WITH;
|
|
311
|
-
return !fieldValue?.endsWith(
|
|
411
|
+
return !fieldValue?.endsWith(
|
|
412
|
+
rule.value[0].replace(/^\//, "").replace(/\/$/, "")
|
|
413
|
+
);
|
|
312
414
|
case constants.regexTypes.NOT_CONTAINS:
|
|
313
415
|
message = custom_message ?? error_messages.REGEX_NOT_CONTAINS;
|
|
314
416
|
return !regex.test(fieldValue);
|
|
@@ -318,7 +420,9 @@ const handleRegexValidation = (
|
|
|
318
420
|
})();
|
|
319
421
|
|
|
320
422
|
if (!isValid) {
|
|
321
|
-
message = message
|
|
423
|
+
message = message
|
|
424
|
+
?.replace(`{field}`, fieldLabel)
|
|
425
|
+
?.replace(`{value}`, rule.value[0]);
|
|
322
426
|
addError(
|
|
323
427
|
currentPath,
|
|
324
428
|
{ label: fieldLabel, fieldPath: currentPath, description: "", message },
|
|
@@ -358,10 +462,9 @@ const validateOperatorRule = (
|
|
|
358
462
|
const date = new Date(value);
|
|
359
463
|
date.setHours(0, 0, 0, 0);
|
|
360
464
|
if (forMessage) {
|
|
361
|
-
return `${date.getUTCFullYear()}-${String(
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
)}-${String(date.getUTCDate()).padStart(2, "0")}`;
|
|
465
|
+
return `${date.getUTCFullYear()}-${String(
|
|
466
|
+
date.getUTCMonth() + 1
|
|
467
|
+
).padStart(2, "0")}-${String(date.getUTCDate()).padStart(2, "0")}`;
|
|
365
468
|
}
|
|
366
469
|
return Math.floor(date.getTime() / 1000);
|
|
367
470
|
}
|
|
@@ -370,13 +473,13 @@ const validateOperatorRule = (
|
|
|
370
473
|
|
|
371
474
|
if (forMessage) {
|
|
372
475
|
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(
|
|
476
|
+
`${date.getUTCFullYear()}-${String(date.getUTCMonth() + 1).padStart(
|
|
377
477
|
2,
|
|
378
478
|
"0"
|
|
379
|
-
)}
|
|
479
|
+
)}-${String(date.getUTCDate()).padStart(2, "0")} ` +
|
|
480
|
+
`${String(date.getUTCHours()).padStart(2, "0")}:${String(
|
|
481
|
+
date.getUTCMinutes()
|
|
482
|
+
).padStart(2, "0")}:${String(date.getUTCSeconds()).padStart(2, "0")}`
|
|
380
483
|
);
|
|
381
484
|
}
|
|
382
485
|
|
|
@@ -400,7 +503,9 @@ const validateOperatorRule = (
|
|
|
400
503
|
|
|
401
504
|
const fieldValueParsed = getComparableValue(fieldValue);
|
|
402
505
|
|
|
403
|
-
const messageValue = Array.isArray(rule.value)
|
|
506
|
+
const messageValue = Array.isArray(rule.value)
|
|
507
|
+
? rule.value.join(", ")
|
|
508
|
+
: String(rule.value);
|
|
404
509
|
let message = "";
|
|
405
510
|
|
|
406
511
|
switch (rule.options.operator) {
|
|
@@ -481,7 +586,9 @@ const validateOperatorRule = (
|
|
|
481
586
|
case constants.operatorTypes.EXISTS:
|
|
482
587
|
message = custom_message ?? error_messages.EXISTS;
|
|
483
588
|
message = message?.replace(`{field}`, formatLabel(field.display_label));
|
|
484
|
-
valid = rule.value[0]
|
|
589
|
+
valid = rule.value[0]
|
|
590
|
+
? fieldValue !== undefined
|
|
591
|
+
: fieldValue === undefined;
|
|
485
592
|
break;
|
|
486
593
|
case constants.operatorTypes.TYPE:
|
|
487
594
|
message = custom_message ?? error_messages.TYPE;
|
|
@@ -545,18 +652,33 @@ const generateErrorMessage = (
|
|
|
545
652
|
return message;
|
|
546
653
|
};
|
|
547
654
|
|
|
548
|
-
const handleRule = (
|
|
655
|
+
const handleRule = (
|
|
656
|
+
rule,
|
|
657
|
+
field,
|
|
658
|
+
value,
|
|
659
|
+
addError,
|
|
660
|
+
currentPath,
|
|
661
|
+
formData,
|
|
662
|
+
error_messages
|
|
663
|
+
) => {
|
|
549
664
|
const ruleValue = rule?.value;
|
|
550
|
-
let messageValue = Array.isArray(ruleValue)
|
|
665
|
+
let messageValue = Array.isArray(ruleValue)
|
|
666
|
+
? ruleValue.join(", ")
|
|
667
|
+
: String(ruleValue);
|
|
551
668
|
const fieldLabel = formatLabel(field.display_label);
|
|
552
669
|
const custom_message = rule?.custom_message;
|
|
553
670
|
|
|
554
|
-
if (
|
|
671
|
+
if (
|
|
672
|
+
rule.case === constants.rulesTypes.ONE_OF &&
|
|
673
|
+
choices.includes(field?.meta?.interface)
|
|
674
|
+
) {
|
|
555
675
|
const fieldChoices = field?.meta?.options?.choices || [];
|
|
556
676
|
const ruleValues = Array.isArray(ruleValue) ? ruleValue : [ruleValue];
|
|
557
677
|
|
|
558
678
|
const labelValuePairs = ruleValues.map((val) => {
|
|
559
|
-
const matched = fieldChoices.find(
|
|
679
|
+
const matched = fieldChoices.find(
|
|
680
|
+
(item) => String(item.value) === String(val)
|
|
681
|
+
);
|
|
560
682
|
return matched ? `${matched.label} (${matched.value})` : val;
|
|
561
683
|
});
|
|
562
684
|
|
|
@@ -586,7 +708,8 @@ const handleRule = (rule, field, value, addError, currentPath, formData, error_m
|
|
|
586
708
|
if (value) addValidationError("EMPTY", {}, custom_message);
|
|
587
709
|
break;
|
|
588
710
|
case constants.rulesTypes.NOT_EMPTY:
|
|
589
|
-
if (!value || value.length === 0)
|
|
711
|
+
if (!value || value.length === 0)
|
|
712
|
+
addValidationError("NOT_EMPTY", {}, custom_message);
|
|
590
713
|
break;
|
|
591
714
|
case constants.rulesTypes.ONE_OF:
|
|
592
715
|
if (!ruleValue?.includes(value))
|
|
@@ -594,10 +717,15 @@ const handleRule = (rule, field, value, addError, currentPath, formData, error_m
|
|
|
594
717
|
break;
|
|
595
718
|
case constants.rulesTypes.NOT_ONE_OF:
|
|
596
719
|
if (ruleValue?.includes(value))
|
|
597
|
-
addValidationError(
|
|
720
|
+
addValidationError(
|
|
721
|
+
"NOT_ONE_OF",
|
|
722
|
+
{ value: messageValue },
|
|
723
|
+
custom_message
|
|
724
|
+
);
|
|
598
725
|
break;
|
|
599
726
|
case constants.rulesTypes.NOT_ALLOWED:
|
|
600
|
-
if (ruleValue?.includes(value))
|
|
727
|
+
if (ruleValue?.includes(value))
|
|
728
|
+
addValidationError("NOT_ALLOWED", {}, custom_message);
|
|
601
729
|
break;
|
|
602
730
|
case constants.rulesTypes.MIN:
|
|
603
731
|
case constants.rulesTypes.MAX:
|
|
@@ -650,11 +778,15 @@ const validateField = (
|
|
|
650
778
|
error_messages,
|
|
651
779
|
onlyFormFields,
|
|
652
780
|
apiVersion,
|
|
653
|
-
fieldOptions
|
|
781
|
+
fieldOptions,
|
|
782
|
+
language_codes
|
|
654
783
|
) => {
|
|
655
784
|
if (onlyFormFields == true && (value === undefined || value === null)) return;
|
|
656
785
|
|
|
657
|
-
const
|
|
786
|
+
const { is_m2a_item } = field?.meta;
|
|
787
|
+
const currentPath = fieldPath
|
|
788
|
+
? `${fieldPath}.${field.key.split(".").pop()}`
|
|
789
|
+
: field.key;
|
|
658
790
|
const fieldLabel = formatLabel(field.display_label);
|
|
659
791
|
|
|
660
792
|
validateMetaRules(
|
|
@@ -667,7 +799,8 @@ const validateField = (
|
|
|
667
799
|
onlyFormFields,
|
|
668
800
|
apiVersion,
|
|
669
801
|
fieldOptions,
|
|
670
|
-
formData
|
|
802
|
+
formData,
|
|
803
|
+
language_codes
|
|
671
804
|
);
|
|
672
805
|
|
|
673
806
|
if (
|
|
@@ -676,29 +809,12 @@ const validateField = (
|
|
|
676
809
|
value &&
|
|
677
810
|
typeChecks[constants.types.OBJECT](value)
|
|
678
811
|
) {
|
|
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
812
|
let itemSchemaId = null;
|
|
696
813
|
|
|
697
|
-
if (
|
|
698
|
-
const
|
|
699
|
-
if (
|
|
700
|
-
|
|
701
|
-
itemSchemaId = childField?.schema_id ?? null;
|
|
814
|
+
if (is_m2a_item) {
|
|
815
|
+
const fieldPath = getM2AItemParentPath(currentPath);
|
|
816
|
+
if (fieldPath) {
|
|
817
|
+
itemSchemaId = getValue(formData, fieldPath)?.collection_id;
|
|
702
818
|
}
|
|
703
819
|
}
|
|
704
820
|
|
|
@@ -717,7 +833,8 @@ const validateField = (
|
|
|
717
833
|
error_messages,
|
|
718
834
|
onlyFormFields,
|
|
719
835
|
apiVersion,
|
|
720
|
-
fieldOptions
|
|
836
|
+
fieldOptions,
|
|
837
|
+
language_codes
|
|
721
838
|
)
|
|
722
839
|
);
|
|
723
840
|
} else if (field.type === constants.types.ARRAY && Array.isArray(value)) {
|
|
@@ -727,7 +844,14 @@ const validateField = (
|
|
|
727
844
|
const itemPath = `${currentPath}[${index}]`;
|
|
728
845
|
|
|
729
846
|
if (choices.includes(field?.meta?.interface) && !isEmpty(item)) {
|
|
730
|
-
applyValidations(
|
|
847
|
+
applyValidations(
|
|
848
|
+
field,
|
|
849
|
+
item,
|
|
850
|
+
addError,
|
|
851
|
+
itemPath,
|
|
852
|
+
formData,
|
|
853
|
+
error_messages
|
|
854
|
+
);
|
|
731
855
|
}
|
|
732
856
|
|
|
733
857
|
if (!typeChecks[itemType](item)) {
|
|
@@ -760,13 +884,23 @@ const validateField = (
|
|
|
760
884
|
error_messages,
|
|
761
885
|
onlyFormFields,
|
|
762
886
|
apiVersion,
|
|
763
|
-
fieldOptions
|
|
887
|
+
fieldOptions,
|
|
888
|
+
language_codes
|
|
764
889
|
)
|
|
765
890
|
);
|
|
766
891
|
});
|
|
767
892
|
}
|
|
768
893
|
} else {
|
|
769
|
-
if (
|
|
894
|
+
if (
|
|
895
|
+
!applyValidations(
|
|
896
|
+
field,
|
|
897
|
+
value,
|
|
898
|
+
addError,
|
|
899
|
+
currentPath,
|
|
900
|
+
formData,
|
|
901
|
+
error_messages
|
|
902
|
+
)
|
|
903
|
+
) {
|
|
770
904
|
addError(
|
|
771
905
|
currentPath,
|
|
772
906
|
{
|
|
@@ -781,11 +915,32 @@ const validateField = (
|
|
|
781
915
|
}
|
|
782
916
|
};
|
|
783
917
|
|
|
784
|
-
const applyValidations = (
|
|
785
|
-
|
|
918
|
+
const applyValidations = (
|
|
919
|
+
field,
|
|
920
|
+
value,
|
|
921
|
+
addError,
|
|
922
|
+
currentPath,
|
|
923
|
+
formData,
|
|
924
|
+
error_messages
|
|
925
|
+
) => {
|
|
926
|
+
if (
|
|
927
|
+
!field.validations ||
|
|
928
|
+
value === null ||
|
|
929
|
+
value === undefined ||
|
|
930
|
+
value === ""
|
|
931
|
+
)
|
|
932
|
+
return true;
|
|
786
933
|
return !field.validations.some(
|
|
787
934
|
(rule) =>
|
|
788
|
-
handleRule(
|
|
935
|
+
handleRule(
|
|
936
|
+
rule,
|
|
937
|
+
field,
|
|
938
|
+
value,
|
|
939
|
+
addError,
|
|
940
|
+
currentPath,
|
|
941
|
+
formData,
|
|
942
|
+
error_messages
|
|
943
|
+
) === false
|
|
789
944
|
);
|
|
790
945
|
};
|
|
791
946
|
|
|
@@ -808,6 +963,10 @@ const schema = {
|
|
|
808
963
|
language: { type: constants.types.STRING, array_type: null },
|
|
809
964
|
maxLevel: { type: constants.types.NUMBER, array_type: null },
|
|
810
965
|
onlyFormFields: { type: constants.types.BOOLEAN, array_type: null },
|
|
966
|
+
language_codes: {
|
|
967
|
+
type: constants.types.ARRAY,
|
|
968
|
+
array_type: constants.types.OBJECT_ID,
|
|
969
|
+
},
|
|
811
970
|
};
|
|
812
971
|
|
|
813
972
|
const validate = (data) => {
|
|
@@ -833,7 +992,8 @@ const validate = (data) => {
|
|
|
833
992
|
} = data;
|
|
834
993
|
|
|
835
994
|
const error_messages =
|
|
836
|
-
constants.LOCALE_MESSAGES[language] ??
|
|
995
|
+
constants.LOCALE_MESSAGES[language] ??
|
|
996
|
+
constants.LOCALE_MESSAGES[constants.LANGUAGES.en];
|
|
837
997
|
|
|
838
998
|
let result = { status: true, errors: {}, data: structuredClone(formData) };
|
|
839
999
|
|
|
@@ -850,12 +1010,18 @@ const validate = (data) => {
|
|
|
850
1010
|
const secondParentField = fields.find((f) => f.path === secondParent);
|
|
851
1011
|
if (
|
|
852
1012
|
secondParentField &&
|
|
853
|
-
secondParentField?.meta?.interface ===
|
|
1013
|
+
secondParentField?.meta?.interface ===
|
|
1014
|
+
constants.interfaces.TRANSLATIONS
|
|
854
1015
|
) {
|
|
855
1016
|
const languageKey = secondParentField?.meta?.options?.language_field;
|
|
856
1017
|
const firstParentValue = getValue(formData, firstParent);
|
|
857
|
-
if (
|
|
858
|
-
|
|
1018
|
+
if (
|
|
1019
|
+
firstParentValue &&
|
|
1020
|
+
typeChecks[constants.types.OBJECT](firstParentValue)
|
|
1021
|
+
) {
|
|
1022
|
+
const codeKey = Object.keys(firstParentValue).find((key) =>
|
|
1023
|
+
key.includes(languageKey)
|
|
1024
|
+
);
|
|
859
1025
|
const codeValue = codeKey ? firstParentValue[codeKey] : null;
|
|
860
1026
|
if (codeValue) {
|
|
861
1027
|
const translation_key = fieldPath.replace(
|
|
@@ -881,7 +1047,10 @@ const validate = (data) => {
|
|
|
881
1047
|
// Validate Data
|
|
882
1048
|
const defaultField = { meta: { hidden: false } };
|
|
883
1049
|
if (!data) {
|
|
884
|
-
const message = error_messages.REQUIRED.replace(
|
|
1050
|
+
const message = error_messages.REQUIRED.replace(
|
|
1051
|
+
`{field}`,
|
|
1052
|
+
formatLabel("data")
|
|
1053
|
+
);
|
|
885
1054
|
addError(
|
|
886
1055
|
"data",
|
|
887
1056
|
{
|
|
@@ -897,10 +1066,10 @@ const validate = (data) => {
|
|
|
897
1066
|
|
|
898
1067
|
// validate data type
|
|
899
1068
|
if (!typeChecks[constants.types.OBJECT](data)) {
|
|
900
|
-
const message = error_messages.INVALID_TYPE.replace(
|
|
901
|
-
`{
|
|
902
|
-
|
|
903
|
-
);
|
|
1069
|
+
const message = error_messages.INVALID_TYPE.replace(
|
|
1070
|
+
`{field}`,
|
|
1071
|
+
formatLabel("data")
|
|
1072
|
+
).replace(`{type}`, constants.types.OBJECT);
|
|
904
1073
|
addError(
|
|
905
1074
|
"data",
|
|
906
1075
|
{
|
|
@@ -920,7 +1089,10 @@ const validate = (data) => {
|
|
|
920
1089
|
const fieldValue = data[key];
|
|
921
1090
|
// Skip empty values
|
|
922
1091
|
if (fieldValue == null || fieldValue == undefined) {
|
|
923
|
-
const message = error_messages.REQUIRED.replace(
|
|
1092
|
+
const message = error_messages.REQUIRED.replace(
|
|
1093
|
+
`{field}`,
|
|
1094
|
+
formatLabel(key)
|
|
1095
|
+
);
|
|
924
1096
|
addError(
|
|
925
1097
|
key,
|
|
926
1098
|
{
|
|
@@ -936,10 +1108,10 @@ const validate = (data) => {
|
|
|
936
1108
|
|
|
937
1109
|
// Validate field type
|
|
938
1110
|
if (!typeChecks[expectedType] || !typeChecks[expectedType](fieldValue)) {
|
|
939
|
-
const message = error_messages.INVALID_TYPE.replace(
|
|
940
|
-
`{
|
|
941
|
-
|
|
942
|
-
);
|
|
1111
|
+
const message = error_messages.INVALID_TYPE.replace(
|
|
1112
|
+
`{field}`,
|
|
1113
|
+
key
|
|
1114
|
+
).replace(`{type}`, expectedType);
|
|
943
1115
|
addError(
|
|
944
1116
|
key,
|
|
945
1117
|
{
|
|
@@ -959,7 +1131,11 @@ const validate = (data) => {
|
|
|
959
1131
|
// Determine the expected type of array items
|
|
960
1132
|
const arrayItemType = schema[key].array_type; // Define item types like "relations[]": "object"
|
|
961
1133
|
|
|
962
|
-
if (
|
|
1134
|
+
if (
|
|
1135
|
+
arrayItemType &&
|
|
1136
|
+
typeChecks[arrayItemType] &&
|
|
1137
|
+
!typeChecks[arrayItemType](item)
|
|
1138
|
+
) {
|
|
963
1139
|
const message = error_messages.INVALID_TYPE.replace(
|
|
964
1140
|
`{field}`,
|
|
965
1141
|
`${key}[${index}]`
|
|
@@ -981,10 +1157,10 @@ const validate = (data) => {
|
|
|
981
1157
|
|
|
982
1158
|
// Validate API Version
|
|
983
1159
|
if (!constants.API_VERSIONS.includes(apiVersion)) {
|
|
984
|
-
const message = error_messages.IN.replace(
|
|
985
|
-
`{
|
|
986
|
-
|
|
987
|
-
);
|
|
1160
|
+
const message = error_messages.IN.replace(
|
|
1161
|
+
`{field}`,
|
|
1162
|
+
formatLabel("apiVersion")
|
|
1163
|
+
).replace(`{value}`, constants.API_VERSIONS.join(", "));
|
|
988
1164
|
addError(
|
|
989
1165
|
"apiVersion",
|
|
990
1166
|
{
|
|
@@ -1008,7 +1184,9 @@ const validate = (data) => {
|
|
|
1008
1184
|
let allFields = isSeparatedFields ? [] : fields;
|
|
1009
1185
|
|
|
1010
1186
|
if (!isSeparatedFields) {
|
|
1011
|
-
schemaFields = fields.filter(
|
|
1187
|
+
schemaFields = fields.filter(
|
|
1188
|
+
(field) => field?.schema_id?.toString() === formId?.toString()
|
|
1189
|
+
);
|
|
1012
1190
|
}
|
|
1013
1191
|
|
|
1014
1192
|
let currentDepthMap = new Map();
|
|
@@ -1059,7 +1237,8 @@ const validate = (data) => {
|
|
|
1059
1237
|
error_messages,
|
|
1060
1238
|
onlyFormFields,
|
|
1061
1239
|
apiVersion,
|
|
1062
|
-
fieldOptions
|
|
1240
|
+
fieldOptions,
|
|
1241
|
+
data.language_codes
|
|
1063
1242
|
);
|
|
1064
1243
|
});
|
|
1065
1244
|
return result;
|