nox-validation 1.7.3 → 1.7.4

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,25 @@ 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;
542
+ } else fPath = `${currentPath}.${fPath}`;
516
543
  }
517
544
 
545
+ // if (
546
+ // currentPath &&
547
+ // currentPath?.trim() !== "" &&
548
+ // !fPath.includes(currentPath)
549
+ // // TODO: Need to Optimize This fPath => group.items.dropdown currentPath => group.items[0].dropdown final path becomes => group.items[0].dropdown.group.items.dropdown
550
+ // // 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
551
+ // ) {
552
+ // fPath = `${currentPath}.${fPath}`;
553
+ // }
554
+
518
555
  if (staticType === "update") return;
519
556
  if (staticType === "delete" && !isTranslationNode) return;
520
557
  if (staticType === "existing" && !isTranslationNode) return;
@@ -522,34 +559,21 @@ const validateMetaRules = (
522
559
  const label = formatLabel(node.display_label);
523
560
  const message = error_messages.REQUIRED.replace("{field}", label);
524
561
 
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
562
  const isMultiLang =
540
563
  isTranslationNode &&
541
564
  Array.isArray(language_codes) &&
542
565
  language_codes.length > 1;
566
+
543
567
  if (isMultiLang) {
568
+ const fullPath = fPath;
569
+
544
570
  language_codes.forEach((lang, index) => {
545
571
  let langPath, transformedPath;
546
572
  let isAdded = false;
547
-
548
- // Check if current language entry exists in fieldValue.create
573
+ // // Check if current language entry exists in fieldValue.create
549
574
  if (
550
575
  Array.isArray(fieldValue?.create) &&
551
576
  fieldValue?.create.some((item, idx) => {
552
- // Check by languages_code or by index match
553
577
  return item.languages_code === lang || idx === index;
554
578
  })
555
579
  ) {
@@ -560,25 +584,21 @@ const validateMetaRules = (
560
584
  node?.value?.includes("create[0].") ||
561
585
  node?.value?.includes("create[0]")
562
586
  ) {
563
- langPath = rebuildFullPath(node.value, node.key, (path) =>
587
+ langPath = rebuildFullPath(node.value, fPath, (path) =>
564
588
  path.replace(/create\[0\]\./, `create[${index}].`)
565
589
  );
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
- );
590
+ transformedPath = rebuildFullPath(node.value, fPath, (path) => {
591
+ const newPath = path
592
+ .replace(/\[0\]\./, `.${lang}.`)
593
+ ?.replace("create", "");
594
+ return newPath;
595
+ });
576
596
  } else {
577
- langPath = rebuildFullPath(node.value, node.key, (path) => {
597
+ langPath = rebuildFullPath(node.value, fPath, (path) => {
578
598
  return path.replace(/create\./, `create[${index}].`);
579
599
  });
580
600
 
581
- transformedPath = rebuildFullPath(node.value, node.key, (path) =>
601
+ transformedPath = rebuildFullPath(node.value, fPath, (path) =>
582
602
  path.replace(/\[0\]\./, `.${lang}.`)?.replace("create", "")
583
603
  );
584
604
  }
@@ -606,8 +626,8 @@ const validateMetaRules = (
606
626
  const validType =
607
627
  field?.alternateType?.length > 0
608
628
  ? [field.type, ...field?.alternateType].some((type) =>
609
- typeChecks[type](fieldValue)
610
- )
629
+ typeChecks[type](fieldValue)
630
+ )
611
631
  : typeChecks[field.type](fieldValue, { key: currentPath, updateValue });
612
632
 
613
633
  if (!isEmpty(fieldValue) && !validType) {
@@ -645,8 +665,9 @@ const handleRegexValidation = (
645
665
  const fieldLabel = formatLabel(field.display_label);
646
666
 
647
667
  // Determine regex flags
648
- const flags = `${rule.options?.case_sensitive ? "" : "i"}${rule.options?.multiline ? "m" : ""
649
- }${rule.options?.global ? "g" : ""}`;
668
+ const flags = `${rule.options?.case_sensitive ? "" : "i"}${
669
+ rule.options?.multiline ? "m" : ""
670
+ }${rule.options?.global ? "g" : ""}`;
650
671
 
651
672
  // Build regex for MATCH/CONTAINS/NOT_CONTAINS
652
673
  let regex;
@@ -1138,16 +1159,7 @@ const validateField = (
1138
1159
  timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone,
1139
1160
  translatedPath = null
1140
1161
  ) => {
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;
1162
+ const { is_m2a_item, staticType = null, parentInterface } = field?.meta;
1151
1163
  const currentPath = fieldPath
1152
1164
  ? `${fieldPath}.${field.key.split(".").pop()}`
1153
1165
  : field.key;
@@ -1157,6 +1169,50 @@ const validateField = (
1157
1169
  ? `${translatedPath}.${field.key.split(".").pop()}`
1158
1170
  : field.key;
1159
1171
  }
1172
+
1173
+ if (
1174
+ onlyFormFields == true &&
1175
+ value === undefined &&
1176
+ ![constants.interfaces.TRANSLATIONS, constants.interfaces.SEO].includes(
1177
+ field?.meta?.interface
1178
+ )
1179
+ ) {
1180
+ if (
1181
+ onlyFormFields == true &&
1182
+ value === undefined &&
1183
+ staticType === "update" &&
1184
+ parentInterface === constants.interfaces.TRANSLATIONS &&
1185
+ field?.meta.required
1186
+ ) {
1187
+ const langCode = getValue(formData, fieldPath)?.languages_code;
1188
+ const translationFieldPath = trimBasicPath(currentPath, staticType);
1189
+ const existingItemValue = getValue(
1190
+ existingForm,
1191
+ translationFieldPath
1192
+ )?.find((item) => item.languages_code === langCode)?.[
1193
+ `${field.key.split(".").pop()}`
1194
+ ];
1195
+
1196
+ if (isEmpty(existingItemValue)) {
1197
+ const message = error_messages.REQUIRED.replace(
1198
+ `{field}`,
1199
+ formatLabel(`${field.key.split(".").pop()}`)
1200
+ );
1201
+ addError(
1202
+ currentPath,
1203
+ {
1204
+ label: formatLabel(`${field.key.split(".").pop()}`),
1205
+ fieldPath: currentPath,
1206
+ translation_path: translatedPath,
1207
+ description: "",
1208
+ message,
1209
+ },
1210
+ field
1211
+ );
1212
+ }
1213
+ } else return;
1214
+ }
1215
+
1160
1216
  const fieldLabel = formatLabel(field.display_label);
1161
1217
 
1162
1218
  validateMetaRules(
@@ -1273,6 +1329,13 @@ const validateField = (
1273
1329
  )
1274
1330
  trPath = trPath.replace(".update.", ".");
1275
1331
 
1332
+ if (
1333
+ trPath &&
1334
+ field.meta?.staticType === "create" &&
1335
+ trPath.includes(".create.")
1336
+ )
1337
+ trPath = trPath.replace(".create.", ".");
1338
+
1276
1339
  validateField(
1277
1340
  child,
1278
1341
  item[child.key.split(".").pop()],
@@ -1445,7 +1508,7 @@ const validate = (data) => {
1445
1508
  if (
1446
1509
  secondParentField &&
1447
1510
  secondParentField?.meta?.interface ===
1448
- constants.interfaces.TRANSLATIONS
1511
+ constants.interfaces.TRANSLATIONS
1449
1512
  ) {
1450
1513
  const languageKey = secondParentField?.meta?.options?.language_field;
1451
1514
  const firstParentValue = getValue(formData, firstParent);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nox-validation",
3
- "version": "1.7.3",
3
+ "version": "1.7.4",
4
4
  "description": "validate dynamic schema",
5
5
  "main": "index.js",
6
6
  "scripts": {