nox-validation 1.7.3 → 1.7.5

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/lib/helpers.js CHANGED
@@ -1604,6 +1604,23 @@ const rebuildFullPath = (nodePath, fullPath, modifierFn) => {
1604
1604
  : `${modifiedNodePath}`.replace(/\.+/g, ".");
1605
1605
  };
1606
1606
 
1607
+ const trimBasicPath = (basePath, staticType = "update") => {
1608
+ const regex = new RegExp(`${staticType}\\[\\d+\\]`, "g");
1609
+ const matches = [...basePath.matchAll(regex)];
1610
+
1611
+ if (matches.length === 0) return basePath; // no update/create/delete/etc found
1612
+
1613
+ // Last matched dynamic segment (e.g. "update[3]")
1614
+ const lastSegment = matches[matches.length - 1][0];
1615
+
1616
+ // Find starting index of this last segment
1617
+ const idx = basePath.lastIndexOf(lastSegment);
1618
+
1619
+ // Return everything BEFORE this segment (and remove trailing dot)
1620
+ const result = basePath.substring(0, idx).replace(/\.$/, "");
1621
+ return result;
1622
+ };
1623
+
1607
1624
  module.exports = {
1608
1625
  generateModifiedRules,
1609
1626
  getFieldsGroupBySchemaId,
@@ -1628,4 +1645,5 @@ module.exports = {
1628
1645
  remainingItems,
1629
1646
  formatDate,
1630
1647
  rebuildFullPath,
1648
+ trimBasicPath,
1631
1649
  };
package/lib/validate.js CHANGED
@@ -14,6 +14,7 @@ const {
14
14
  remainingItems,
15
15
  formatDate,
16
16
  rebuildFullPath,
17
+ trimBasicPath,
17
18
  } = require("./helpers");
18
19
 
19
20
  const choices = ["radio", "checkboxes", "dropdown_multiple", "dropdown"];
@@ -228,13 +229,13 @@ const isEmptyRelational = ({
228
229
  ? existingValue.length === 0
229
230
  ? []
230
231
  : typeof existingValue[0] === "string"
231
- ? existingValue
232
- : existingValue.map((item) => item._id)
232
+ ? existingValue
233
+ : existingValue.map((item) => item._id)
233
234
  : typeof existingValue === "string"
234
- ? [existingValue]
235
- : existingValue && typeof existingValue === "object"
236
- ? [existingValue._id]
237
- : [];
235
+ ? [existingValue]
236
+ : existingValue && typeof existingValue === "object"
237
+ ? [existingValue._id]
238
+ : [];
238
239
  all = Array.isArray(all) ? all?.filter(Boolean) : all;
239
240
  switch (interface) {
240
241
  case constants.interfaces.FILE:
@@ -359,21 +360,21 @@ const validateMetaRules = (
359
360
  const isValidRelational =
360
361
  required && isRelational
361
362
  ? isEmptyRelational({
362
- api_version: apiVersion,
363
- value: fieldValue,
364
- interface: field?.meta?.interface,
365
- onlyFormFields,
366
- existingValue:
367
- getValue(existingForm, currentPath) ||
368
- getValue(
369
- existingForm,
370
- currentPath
371
- ?.replace(".create", "")
372
- ?.replace(".update", "")
373
- ?.replace(".existing", "")
374
- ?.replace(".delete", "")
375
- ), // TODO: Need to Generate Form Path Without create, update, existing, delete
376
- })
363
+ api_version: apiVersion,
364
+ value: fieldValue,
365
+ interface: field?.meta?.interface,
366
+ onlyFormFields,
367
+ existingValue:
368
+ getValue(existingForm, currentPath) ||
369
+ getValue(
370
+ existingForm,
371
+ currentPath
372
+ ?.replace(".create", "")
373
+ ?.replace(".update", "")
374
+ ?.replace(".existing", "")
375
+ ?.replace(".delete", "")
376
+ ), // TODO: Need to Generate Form Path Without create, update, existing, delete
377
+ })
377
378
  : true;
378
379
 
379
380
  const isEmptyVal = is_m2a_item ? !fieldValue : isEmpty(fieldValue);
@@ -392,29 +393,30 @@ const validateMetaRules = (
392
393
  if (invalidRequire || !isValidRelational) {
393
394
  const message = field?.meta?.required
394
395
  ? error_messages.REQUIRED.replace(
395
- `{field}`,
396
- formatLabel(field.display_label)
397
- )
396
+ `{field}`,
397
+ formatLabel(field.display_label)
398
+ )
398
399
  : error_messages.NOT_EMPTY.replace(
399
- `{field}`,
400
- formatLabel(field.display_label)
401
- );
400
+ `{field}`,
401
+ formatLabel(field.display_label)
402
+ );
403
+
402
404
  addError(
403
405
  currentPath,
404
406
  translatedPath
405
407
  ? {
406
- label: formatLabel(field.display_label),
407
- fieldPath: currentPath,
408
- description: "",
409
- translation_path: translatedPath,
410
- message,
411
- }
408
+ label: formatLabel(field.display_label),
409
+ fieldPath: currentPath,
410
+ description: "",
411
+ translation_path: translatedPath,
412
+ message,
413
+ }
412
414
  : {
413
- label: formatLabel(field.display_label),
414
- fieldPath: currentPath,
415
- description: "",
416
- message,
417
- },
415
+ label: formatLabel(field.display_label),
416
+ fieldPath: currentPath,
417
+ description: "",
418
+ message,
419
+ },
418
420
  field
419
421
  );
420
422
  }
@@ -472,16 +474,20 @@ const validateMetaRules = (
472
474
  node: field,
473
475
  skipFn: (node) => {
474
476
  let status = false;
477
+ if (node.meta?.interface === constants.interfaces.MANY_TO_ANY)
478
+ status = true;
479
+
475
480
  if (
476
- node.meta?.interface === constants.interfaces.MANY_TO_ANY
477
- ) status = true
478
-
479
- if ([constants.interfaces.MANY_TO_MANY,
480
- constants.interfaces.ONE_TO_MANY,
481
- constants.interfaces.MANY_TO_ONE,
482
- constants.interfaces.SEO,
483
- ].includes(node.meta?.interface) && (!node.meta?.required || !node.meta?.hidden)) status = true
484
- return status
481
+ [
482
+ constants.interfaces.MANY_TO_MANY,
483
+ constants.interfaces.ONE_TO_MANY,
484
+ constants.interfaces.MANY_TO_ONE,
485
+ constants.interfaces.SEO,
486
+ ].includes(node.meta?.interface) &&
487
+ (!node.meta?.required || !node.meta?.hidden)
488
+ )
489
+ status = true;
490
+ return status;
485
491
  },
486
492
  conditionFn: (node) =>
487
493
  node.meta?.required === true && !node.isRelationalUpdate,
@@ -490,6 +496,24 @@ const validateMetaRules = (
490
496
  label: node.display_label,
491
497
  }),
492
498
  actionFn: (node) => {
499
+ const normalize = (path) => path.replace(/\[\d+\]/g, "");
500
+
501
+ const getRoot = (path) => normalize(path).split(".")[0];
502
+
503
+ const buildError = (path, translated) => {
504
+ if (!isEmpty(getValue(formData, path))) return;
505
+ let obj = {
506
+ label,
507
+ fieldPath: path,
508
+ description: "",
509
+ message,
510
+ };
511
+ if (translated) {
512
+ obj.translation_path = translated;
513
+ }
514
+ addError(path, obj, node);
515
+ };
516
+
493
517
  const isTranslationNode =
494
518
  node?.meta?.parentInterface === constants.interfaces.TRANSLATIONS ||
495
519
  field?.meta?.interface === constants.interfaces.TRANSLATIONS;
@@ -509,12 +533,27 @@ const validateMetaRules = (
509
533
 
510
534
  if (
511
535
  currentPath &&
512
- currentPath?.trim() !== "" &&
513
- !fPath.includes(currentPath)
536
+ currentPath.trim() !== "" &&
537
+ !normalize(fPath).startsWith(normalize(currentPath)) &&
538
+ getRoot(fPath) !== getRoot(currentPath)
514
539
  ) {
515
- fPath = `${currentPath}.${fPath}`;
540
+ if (!currentPath.includes(fPath)) {
541
+ fPath = `${currentPath}.${fPath}`;
542
+ }
543
+ } else if (fPath !== currentPath) {
544
+ fPath = currentPath;
516
545
  }
517
546
 
547
+ // if (
548
+ // currentPath &&
549
+ // currentPath?.trim() !== "" &&
550
+ // !fPath.includes(currentPath)
551
+ // // TODO: Need to Optimize This fPath => group.items.dropdown currentPath => group.items[0].dropdown final path becomes => group.items[0].dropdown.group.items.dropdown
552
+ // // also translations.create[0].meta_description <=fPath condition currentPath => seo.create[0].translations.create[0].meta_description then condition becomes true but parent is not same then need to merge
553
+ // ) {
554
+ // fPath = `${currentPath}.${fPath}`;
555
+ // }
556
+
518
557
  if (staticType === "update") return;
519
558
  if (staticType === "delete" && !isTranslationNode) return;
520
559
  if (staticType === "existing" && !isTranslationNode) return;
@@ -522,34 +561,21 @@ const validateMetaRules = (
522
561
  const label = formatLabel(node.display_label);
523
562
  const message = error_messages.REQUIRED.replace("{field}", label);
524
563
 
525
- const buildError = (path, translated) => {
526
- if (!isEmpty(getValue(formData, path))) return;
527
- let obj = {
528
- label,
529
- fieldPath: path,
530
- description: "",
531
- message,
532
- };
533
- if (translated) {
534
- obj.translation_path = translated;
535
- }
536
- addError(path, obj, node);
537
- };
538
-
539
564
  const isMultiLang =
540
565
  isTranslationNode &&
541
566
  Array.isArray(language_codes) &&
542
567
  language_codes.length > 1;
568
+
543
569
  if (isMultiLang) {
570
+ const fullPath = fPath;
571
+
544
572
  language_codes.forEach((lang, index) => {
545
573
  let langPath, transformedPath;
546
574
  let isAdded = false;
547
-
548
575
  // Check if current language entry exists in fieldValue.create
549
576
  if (
550
577
  Array.isArray(fieldValue?.create) &&
551
578
  fieldValue?.create.some((item, idx) => {
552
- // Check by languages_code or by index match
553
579
  return item.languages_code === lang || idx === index;
554
580
  })
555
581
  ) {
@@ -560,25 +586,21 @@ const validateMetaRules = (
560
586
  node?.value?.includes("create[0].") ||
561
587
  node?.value?.includes("create[0]")
562
588
  ) {
563
- langPath = rebuildFullPath(node.value, node.key, (path) =>
589
+ langPath = rebuildFullPath(node.value, fPath, (path) =>
564
590
  path.replace(/create\[0\]\./, `create[${index}].`)
565
591
  );
566
- transformedPath = rebuildFullPath(
567
- node.value,
568
- node.key,
569
- (path) => {
570
- const newPath = path
571
- .replace(/\[0\]\./, `.${lang}.`)
572
- ?.replace("create", "");
573
- return newPath;
574
- }
575
- );
592
+ transformedPath = rebuildFullPath(node.value, fPath, (path) => {
593
+ const newPath = path
594
+ .replace(/\[0\]\./, `.${lang}.`)
595
+ ?.replace("create", "");
596
+ return newPath;
597
+ });
576
598
  } else {
577
- langPath = rebuildFullPath(node.value, node.key, (path) => {
599
+ langPath = rebuildFullPath(node.value, fPath, (path) => {
578
600
  return path.replace(/create\./, `create[${index}].`);
579
601
  });
580
602
 
581
- transformedPath = rebuildFullPath(node.value, node.key, (path) =>
603
+ transformedPath = rebuildFullPath(node.value, fPath, (path) =>
582
604
  path.replace(/\[0\]\./, `.${lang}.`)?.replace("create", "")
583
605
  );
584
606
  }
@@ -606,8 +628,8 @@ const validateMetaRules = (
606
628
  const validType =
607
629
  field?.alternateType?.length > 0
608
630
  ? [field.type, ...field?.alternateType].some((type) =>
609
- typeChecks[type](fieldValue)
610
- )
631
+ typeChecks[type](fieldValue)
632
+ )
611
633
  : typeChecks[field.type](fieldValue, { key: currentPath, updateValue });
612
634
 
613
635
  if (!isEmpty(fieldValue) && !validType) {
@@ -645,8 +667,9 @@ const handleRegexValidation = (
645
667
  const fieldLabel = formatLabel(field.display_label);
646
668
 
647
669
  // Determine regex flags
648
- const flags = `${rule.options?.case_sensitive ? "" : "i"}${rule.options?.multiline ? "m" : ""
649
- }${rule.options?.global ? "g" : ""}`;
670
+ const flags = `${rule.options?.case_sensitive ? "" : "i"}${
671
+ rule.options?.multiline ? "m" : ""
672
+ }${rule.options?.global ? "g" : ""}`;
650
673
 
651
674
  // Build regex for MATCH/CONTAINS/NOT_CONTAINS
652
675
  let regex;
@@ -1138,16 +1161,7 @@ const validateField = (
1138
1161
  timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone,
1139
1162
  translatedPath = null
1140
1163
  ) => {
1141
- if (
1142
- onlyFormFields == true &&
1143
- value === undefined &&
1144
- ![constants.interfaces.TRANSLATIONS, constants.interfaces.SEO].includes(
1145
- field?.meta?.interface
1146
- )
1147
- )
1148
- return;
1149
-
1150
- const { is_m2a_item } = field?.meta;
1164
+ const { is_m2a_item, staticType = null, parentInterface } = field?.meta;
1151
1165
  const currentPath = fieldPath
1152
1166
  ? `${fieldPath}.${field.key.split(".").pop()}`
1153
1167
  : field.key;
@@ -1157,6 +1171,50 @@ const validateField = (
1157
1171
  ? `${translatedPath}.${field.key.split(".").pop()}`
1158
1172
  : field.key;
1159
1173
  }
1174
+
1175
+ if (
1176
+ onlyFormFields == true &&
1177
+ value === undefined &&
1178
+ ![constants.interfaces.TRANSLATIONS, constants.interfaces.SEO].includes(
1179
+ field?.meta?.interface
1180
+ )
1181
+ ) {
1182
+ if (
1183
+ onlyFormFields == true &&
1184
+ value === undefined &&
1185
+ staticType === "update" &&
1186
+ parentInterface === constants.interfaces.TRANSLATIONS &&
1187
+ field?.meta.required
1188
+ ) {
1189
+ const langCode = getValue(formData, fieldPath)?.languages_code;
1190
+ const translationFieldPath = trimBasicPath(currentPath, staticType);
1191
+ const existingItemValue = getValue(
1192
+ existingForm,
1193
+ translationFieldPath
1194
+ )?.find((item) => item.languages_code === langCode)?.[
1195
+ `${field.key.split(".").pop()}`
1196
+ ];
1197
+
1198
+ if (isEmpty(existingItemValue)) {
1199
+ const message = error_messages.REQUIRED.replace(
1200
+ `{field}`,
1201
+ formatLabel(`${field.key.split(".").pop()}`)
1202
+ );
1203
+ addError(
1204
+ currentPath,
1205
+ {
1206
+ label: formatLabel(`${field.key.split(".").pop()}`),
1207
+ fieldPath: currentPath,
1208
+ translation_path: translatedPath,
1209
+ description: "",
1210
+ message,
1211
+ },
1212
+ field
1213
+ );
1214
+ }
1215
+ } else return;
1216
+ }
1217
+
1160
1218
  const fieldLabel = formatLabel(field.display_label);
1161
1219
 
1162
1220
  validateMetaRules(
@@ -1273,6 +1331,13 @@ const validateField = (
1273
1331
  )
1274
1332
  trPath = trPath.replace(".update.", ".");
1275
1333
 
1334
+ if (
1335
+ trPath &&
1336
+ field.meta?.staticType === "create" &&
1337
+ trPath.includes(".create.")
1338
+ )
1339
+ trPath = trPath.replace(".create.", ".");
1340
+
1276
1341
  validateField(
1277
1342
  child,
1278
1343
  item[child.key.split(".").pop()],
@@ -1445,7 +1510,7 @@ const validate = (data) => {
1445
1510
  if (
1446
1511
  secondParentField &&
1447
1512
  secondParentField?.meta?.interface ===
1448
- constants.interfaces.TRANSLATIONS
1513
+ constants.interfaces.TRANSLATIONS
1449
1514
  ) {
1450
1515
  const languageKey = secondParentField?.meta?.options?.language_field;
1451
1516
  const firstParentValue = getValue(formData, firstParent);
@@ -1470,7 +1535,7 @@ const validate = (data) => {
1470
1535
  }
1471
1536
  }
1472
1537
 
1473
- result.errors[fieldPath] = obj;
1538
+ result.errors[fieldPath] = { ...obj, fieldId: field?.field_id };
1474
1539
  result.status = false;
1475
1540
  }
1476
1541
  if (abortEarly && !result.status) return result;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nox-validation",
3
- "version": "1.7.3",
3
+ "version": "1.7.5",
4
4
  "description": "validate dynamic schema",
5
5
  "main": "index.js",
6
6
  "scripts": {