nox-validation 1.2.1 → 1.2.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/lib/constant.js CHANGED
@@ -161,6 +161,7 @@ const LANGUAGES = Object.freeze({
161
161
  });
162
162
 
163
163
  const LOCALE_NL_MESSAGES = Object.freeze({
164
+ ADD_CHOICE: `Voeg minstens één optie toe voor {field}.`,
164
165
  INVALID_VALUE: `{field} bevat een ongeldige waarde.`,
165
166
  EMPTY: `{field} moet leeg zijn.`,
166
167
  NOT_EMPTY: `{field} mag niet leeg zijn.`,
@@ -205,6 +206,7 @@ const LOCALE_NL_MESSAGES = Object.freeze({
205
206
  });
206
207
 
207
208
  const LOCALE_EN_MESSAGES = Object.freeze({
209
+ ADD_CHOICE:`Please add at least one choice for {field}.`,
208
210
  INVALID_VALUE: `{field} contains invalid value.`,
209
211
  EMPTY: `{field} should be empty.`,
210
212
  NOT_EMPTY: `{field} should not be empty.`,
@@ -249,6 +251,7 @@ const LOCALE_EN_MESSAGES = Object.freeze({
249
251
  });
250
252
 
251
253
  const LOCALE_LV_MESSAGES = Object.freeze({
254
+ ADD_CHOICE: `Lūdzu, pievienojiet vismaz vienu izvēli laukam {field}.`,
252
255
  INVALID_VALUE: `{field} satur nederīgu vērtību.`,
253
256
  EMPTY: `{field} jābūt tukšam.`,
254
257
  NOT_EMPTY: `{field} nedrīkst būt tukšs.`,
@@ -293,6 +296,7 @@ const LOCALE_LV_MESSAGES = Object.freeze({
293
296
  });
294
297
 
295
298
  const LOCALE_PL_MESSAGES = Object.freeze({
299
+ ADD_CHOICE: `Proszę dodać co najmniej jedną opcję dla {field}.`,
296
300
  INVALID_VALUE: `{field} zawiera nieprawidłową wartość.`,
297
301
  EMPTY: `{field} powinno być puste.`,
298
302
  NOT_EMPTY: `{field} nie powinno być puste.`,
@@ -337,6 +341,7 @@ const LOCALE_PL_MESSAGES = Object.freeze({
337
341
  });
338
342
 
339
343
  const LOCALE_RO_MESSAGES = Object.freeze({
344
+ ADD_CHOICE: `Vă rugăm să adăugați cel puțin o opțiune pentru {field}.`,
340
345
  INVALID_VALUE: `{field} conține o valoare invalidă.`,
341
346
  EMPTY: `{field} trebuie să fie gol.`,
342
347
  NOT_EMPTY: `{field} nu trebuie să fie gol.`,
@@ -381,6 +386,7 @@ const LOCALE_RO_MESSAGES = Object.freeze({
381
386
  });
382
387
 
383
388
  const LOCALE_RU_MESSAGES = Object.freeze({
389
+ ADD_CHOICE: `Пожалуйста, добавьте хотя бы один вариант для {field}.`,
384
390
  INVALID_VALUE: `{field} содержит недопустимое значение.`,
385
391
  EMPTY: `{field} должно быть пустым.`,
386
392
  NOT_EMPTY: `{field} не должно быть пустым.`,
package/lib/helpers.js CHANGED
@@ -355,7 +355,14 @@ const convertTypesV1 = (field) => {
355
355
  return { type, array_type, find_relations };
356
356
  };
357
357
 
358
- const generateField = (name, path, schema_definition_type, type, childrenFields = [],relationType="none") => {
358
+ const generateField = (
359
+ name,
360
+ path,
361
+ schema_definition_type,
362
+ type,
363
+ childrenFields = [],
364
+ relationType = "none"
365
+ ) => {
359
366
  childrenFields = childrenFields?.map((child) => {
360
367
  return {
361
368
  ...child,
@@ -375,7 +382,7 @@ const generateField = (name, path, schema_definition_type, type, childrenFields
375
382
  required: false,
376
383
  nullable: false,
377
384
  hidden: false,
378
- interface:relationType
385
+ interface: relationType,
379
386
  },
380
387
  validations: [],
381
388
  schema_definition: {
@@ -398,7 +405,7 @@ const createChildrenFieldsFiles = (key) => {
398
405
  constants.types.ARRAY
399
406
  );
400
407
  return [existingField, deleteField];
401
- }
408
+ };
402
409
 
403
410
  const generateRelationalFieldV1 = (key = "", collectionFields = [], relationType) => {
404
411
  if (relationType === constants.interfaces.MANY_TO_ANY) {
@@ -581,7 +588,7 @@ const getForeignCollectionDetails = ({
581
588
  this_collection: mainTable.one_collection_id,
582
589
  this_field: "_id",
583
590
  foreign_collection:
584
- iFace === constants.interfaces.MANY_TO_MANY
591
+ iFace === constants.interfaces.MANY_TO_MANY
585
592
  ? relational?.one_collection_id
586
593
  : iFace === constants.interfaces.MANY_TO_ANY
587
594
  ? relational?.one_allowed_collections_id
@@ -667,8 +674,7 @@ const getCachedOrFetchFields = (schemaId, allFields, relational_fields) => {
667
674
  return fields;
668
675
  };
669
676
 
670
- const getChildFields = (relationDetail, allFields, relational_fields, isTranslation,name) => {
671
-
677
+ const getChildFields = (relationDetail, allFields, relational_fields, isTranslation, name) => {
672
678
  let key = isTranslation
673
679
  ? [
674
680
  ...(Array.isArray(relationDetail.junction_collection)
@@ -723,11 +729,13 @@ const buildNestedStructure = ({
723
729
  schemaFields.forEach((item) => {
724
730
  const pathParts = item.path.split(".");
725
731
  const key = pathParts.join(".");
726
- const isV2File = apiVersion === constants.API_VERSION.V2 && [
727
- constants.interfaces.FILES,
728
- constants.interfaces.FILE,
729
- constants.interfaces.FILE_IMAGE,
730
- ].includes(item?.meta?.interface);
732
+ const isV2File =
733
+ apiVersion === constants.API_VERSION.V2 &&
734
+ [
735
+ constants.interfaces.FILES,
736
+ constants.interfaces.FILE,
737
+ constants.interfaces.FILE_IMAGE,
738
+ ].includes(item?.meta?.interface);
731
739
 
732
740
  const currentDepth = currentDepthMap.get(isRoot ? item.path : rootPath) || 0;
733
741
 
@@ -750,7 +758,8 @@ const buildNestedStructure = ({
750
758
  relationDetail,
751
759
  allFields,
752
760
  relational_fields,
753
- item?.meta?.interface === constants.interfaces.TRANSLATIONS,key
761
+ item?.meta?.interface === constants.interfaces.TRANSLATIONS,
762
+ key
754
763
  );
755
764
  } else {
756
765
  childFields = getCachedFields(relationDetail, relational_fields);
@@ -774,7 +783,7 @@ const buildNestedStructure = ({
774
783
  }
775
784
  }
776
785
 
777
- if(isV2File){
786
+ if (isV2File) {
778
787
  definedType.array_type = constants.types.OBJECT;
779
788
  definedType.type = constants.types.OBJECT;
780
789
  childFields = createChildrenFieldsFiles(key);
@@ -791,16 +800,16 @@ const buildNestedStructure = ({
791
800
  required: item.meta?.required || false,
792
801
  nullable: item.meta?.nullable || false,
793
802
  hidden: item.meta?.hidden || false,
803
+ options: item.meta?.options || {},
794
804
  },
795
- validations:
796
- item?.meta?.validations?.rules?.length > 0
797
- ? generateModifiedRules(item?.meta, item?.meta?.validations?.validation_msg)
798
- : [],
805
+ validations: generateModifiedRules(item?.meta, item?.meta?.validations?.validation_msg),
799
806
  custom_error_message: item?.meta?.validations?.validation_msg,
800
807
  children:
801
808
  childFields?.length > 0
802
- ? isV2File ? childFields:apiVersion === constants.API_VERSION.V1 &&
803
- item.meta?.interface !== constants.interfaces.TRANSLATIONS
809
+ ? isV2File
810
+ ? childFields
811
+ : apiVersion === constants.API_VERSION.V1 &&
812
+ item.meta?.interface !== constants.interfaces.TRANSLATIONS
804
813
  ? generateRelationalFieldV1(key, childFields, item.meta?.interface)
805
814
  : generateRelationalField(key, childFields, item.meta?.interface)
806
815
  : [],
@@ -979,129 +988,145 @@ const generateFieldCompareRules = (rule) => {
979
988
  };
980
989
 
981
990
  const generateModifiedRules = (meta, custom_message) => {
982
- return meta?.validations?.rules?.map((rule, index) => {
991
+ let rules = [];
992
+ if (meta?.validations?.rules?.length > 0) {
993
+ rules = meta?.validations?.rules?.map((rule, index) => {
994
+ let modifiedRule = {
995
+ identifier: String(index),
996
+ case: constants.rulesTypes.RULES_COMPARE,
997
+ value: [],
998
+ options: {},
999
+ custom_message,
1000
+ };
1001
+ switch (rule.rule) {
1002
+ case "contains":
1003
+ modifiedRule.case = constants.rulesTypes.REGEX;
1004
+ modifiedRule.value = [rule[rule.rule].value];
1005
+ modifiedRule.options = {
1006
+ type: constants.regexTypes.CONTAINS,
1007
+ case_sensitive: !rule[rule.rule].insensitive,
1008
+ multiline: false,
1009
+ global: false,
1010
+ };
1011
+ break;
1012
+ case "doesNotContain":
1013
+ modifiedRule.case = constants.rulesTypes.REGEX;
1014
+ modifiedRule.value = [rule[rule.rule].value];
1015
+ modifiedRule.options = {
1016
+ type: constants.regexTypes.NOT_CONTAINS,
1017
+ case_sensitive: !rule[rule.rule].insensitive,
1018
+ multiline: false,
1019
+ global: false,
1020
+ };
1021
+ break;
1022
+ case "startsWith":
1023
+ modifiedRule.case = constants.rulesTypes.REGEX;
1024
+ modifiedRule.value = [rule[rule.rule].value];
1025
+ modifiedRule.options = {
1026
+ type: constants.regexTypes.START_WITH,
1027
+ case_sensitive: !rule[rule.rule].insensitive,
1028
+ };
1029
+ break;
1030
+ case "doesNotStartWith":
1031
+ modifiedRule.case = constants.rulesTypes.REGEX;
1032
+ modifiedRule.value = [rule[rule.rule].value];
1033
+ modifiedRule.options = {
1034
+ type: constants.regexTypes.NOT_START_WITH,
1035
+ case_sensitive: !rule[rule.rule].insensitive,
1036
+ };
1037
+ break;
1038
+ case "endsWith":
1039
+ modifiedRule.case = constants.rulesTypes.REGEX;
1040
+ modifiedRule.value = [rule[rule.rule].value];
1041
+ modifiedRule.options = {
1042
+ type: constants.regexTypes.ENDS_WITH,
1043
+ case_sensitive: !rule[rule.rule].insensitive,
1044
+ };
1045
+ break;
1046
+ case "doesNotEndWith":
1047
+ modifiedRule.case = constants.rulesTypes.REGEX;
1048
+ modifiedRule.value = [rule[rule.rule].value];
1049
+ modifiedRule.options = {
1050
+ type: constants.regexTypes.NOT_ENDS_WITH,
1051
+ case_sensitive: !rule[rule.rule].insensitive,
1052
+ };
1053
+ break;
1054
+ case "matchesRegExp":
1055
+ modifiedRule.case = constants.rulesTypes.REGEX;
1056
+ modifiedRule.value = [rule.matchesRegExp.value];
1057
+ modifiedRule.options.type = constants.regexTypes.MATCH;
1058
+ break;
1059
+ case "equals":
1060
+ modifiedRule.case = constants.rulesTypes.OPERATOR;
1061
+ modifiedRule.options.operator = constants.operatorTypes.EQUAL;
1062
+ modifiedRule.value = [rule[rule.rule].value];
1063
+ break;
1064
+ case "doesNotEqual":
1065
+ modifiedRule.case = constants.rulesTypes.OPERATOR;
1066
+ modifiedRule.options.operator = constants.operatorTypes.NOT_EQUAL;
1067
+ modifiedRule.value = [rule[rule.rule].value];
1068
+ break;
1069
+ case "lessThan":
1070
+ modifiedRule.case = constants.rulesTypes.OPERATOR;
1071
+ modifiedRule.options.operator = constants.operatorTypes.LESS_THAN;
1072
+ modifiedRule.value = [rule[rule.rule].value];
1073
+ break;
1074
+ case "lessThanOrEqualTo":
1075
+ modifiedRule.case = constants.rulesTypes.OPERATOR;
1076
+ modifiedRule.options.operator = constants.operatorTypes.LESS_THAN_EQUAL;
1077
+ modifiedRule.value = [rule[rule.rule].value];
1078
+ break;
1079
+ case "greaterThan":
1080
+ modifiedRule.case = constants.rulesTypes.OPERATOR;
1081
+ modifiedRule.options.operator = constants.operatorTypes.GREATER_THAN;
1082
+ modifiedRule.value = [rule[rule.rule].value];
1083
+ break;
1084
+ case "greaterThanOrEqualTo":
1085
+ modifiedRule.case = constants.rulesTypes.OPERATOR;
1086
+ modifiedRule.options.operator = constants.operatorTypes.GREATER_THAN_EQUAL;
1087
+ modifiedRule.value = [rule[rule.rule].value];
1088
+ break;
1089
+ case "isEmpty":
1090
+ modifiedRule.case = constants.rulesTypes.EMPTY;
1091
+ modifiedRule.value = [];
1092
+ break;
1093
+ case "isNotEmpty":
1094
+ modifiedRule.case = constants.rulesTypes.NOT_EMPTY;
1095
+ modifiedRule.value = [];
1096
+ break;
1097
+ case "isOneOf":
1098
+ modifiedRule.case = constants.rulesTypes.ONE_OF;
1099
+ modifiedRule.value = rule[rule.rule].value;
1100
+ break;
1101
+ case "isNotOneOf":
1102
+ modifiedRule.case = constants.rulesTypes.NOT_ONE_OF;
1103
+ modifiedRule.value = [rule[rule.rule].value];
1104
+ break;
1105
+ case constants.rulesTypes.FIELD_COMPARE: {
1106
+ const fieldRule = generateFieldCompareRules(rule);
1107
+ modifiedRule.case = fieldRule.case;
1108
+ modifiedRule.options = fieldRule.options;
1109
+ modifiedRule.value = fieldRule.value;
1110
+ break;
1111
+ }
1112
+ }
1113
+
1114
+ return modifiedRule;
1115
+ });
1116
+ }
1117
+ const { options } = meta;
1118
+ if (options?.choices?.length > 0) {
1119
+ const choices = options?.choices?.map((item) => item?.value);
983
1120
  let modifiedRule = {
984
- identifier: String(index),
985
- case: constants.rulesTypes.RULES_COMPARE,
986
- value: [],
1121
+ identifier: String(rules?.length + 1),
1122
+ case: constants.rulesTypes.ONE_OF,
1123
+ value: choices,
987
1124
  options: {},
988
1125
  custom_message,
989
1126
  };
990
- switch (rule.rule) {
991
- case "contains":
992
- modifiedRule.case = constants.rulesTypes.REGEX;
993
- modifiedRule.value = [rule[rule.rule].value];
994
- modifiedRule.options = {
995
- type: constants.regexTypes.CONTAINS,
996
- case_sensitive: !rule[rule.rule].insensitive,
997
- multiline: false,
998
- global: false,
999
- };
1000
- break;
1001
- case "doesNotContain":
1002
- modifiedRule.case = constants.rulesTypes.REGEX;
1003
- modifiedRule.value = [rule[rule.rule].value];
1004
- modifiedRule.options = {
1005
- type: constants.regexTypes.NOT_CONTAINS,
1006
- case_sensitive: !rule[rule.rule].insensitive,
1007
- multiline: false,
1008
- global: false,
1009
- };
1010
- break;
1011
- case "startsWith":
1012
- modifiedRule.case = constants.rulesTypes.REGEX;
1013
- modifiedRule.value = [rule[rule.rule].value];
1014
- modifiedRule.options = {
1015
- type: constants.regexTypes.START_WITH,
1016
- case_sensitive: !rule[rule.rule].insensitive,
1017
- };
1018
- break;
1019
- case "doesNotStartWith":
1020
- modifiedRule.case = constants.rulesTypes.REGEX;
1021
- modifiedRule.value = [rule[rule.rule].value];
1022
- modifiedRule.options = {
1023
- type: constants.regexTypes.NOT_START_WITH,
1024
- case_sensitive: !rule[rule.rule].insensitive,
1025
- };
1026
- break;
1027
- case "endsWith":
1028
- modifiedRule.case = constants.rulesTypes.REGEX;
1029
- modifiedRule.value = [rule[rule.rule].value];
1030
- modifiedRule.options = {
1031
- type: constants.regexTypes.ENDS_WITH,
1032
- case_sensitive: !rule[rule.rule].insensitive,
1033
- };
1034
- break;
1035
- case "doesNotEndWith":
1036
- modifiedRule.case = constants.rulesTypes.REGEX;
1037
- modifiedRule.value = [rule[rule.rule].value];
1038
- modifiedRule.options = {
1039
- type: constants.regexTypes.NOT_ENDS_WITH,
1040
- case_sensitive: !rule[rule.rule].insensitive,
1041
- };
1042
- break;
1043
- case "matchesRegExp":
1044
- modifiedRule.case = constants.rulesTypes.REGEX;
1045
- modifiedRule.value = [rule.matchesRegExp.value];
1046
- modifiedRule.options.type = constants.regexTypes.MATCH;
1047
- break;
1048
- case "equals":
1049
- modifiedRule.case = constants.rulesTypes.OPERATOR;
1050
- modifiedRule.options.operator = constants.operatorTypes.EQUAL;
1051
- modifiedRule.value = [rule[rule.rule].value];
1052
- break;
1053
- case "doesNotEqual":
1054
- modifiedRule.case = constants.rulesTypes.OPERATOR;
1055
- modifiedRule.options.operator = constants.operatorTypes.NOT_EQUAL;
1056
- modifiedRule.value = [rule[rule.rule].value];
1057
- break;
1058
- case "lessThan":
1059
- modifiedRule.case = constants.rulesTypes.OPERATOR;
1060
- modifiedRule.options.operator = constants.operatorTypes.LESS_THAN;
1061
- modifiedRule.value = [rule[rule.rule].value];
1062
- break;
1063
- case "lessThanOrEqualTo":
1064
- modifiedRule.case = constants.rulesTypes.OPERATOR;
1065
- modifiedRule.options.operator = constants.operatorTypes.LESS_THAN_EQUAL;
1066
- modifiedRule.value = [rule[rule.rule].value];
1067
- break;
1068
- case "greaterThan":
1069
- modifiedRule.case = constants.rulesTypes.OPERATOR;
1070
- modifiedRule.options.operator = constants.operatorTypes.GREATER_THAN;
1071
- modifiedRule.value = [rule[rule.rule].value];
1072
- break;
1073
- case "greaterThanOrEqualTo":
1074
- modifiedRule.case = constants.rulesTypes.OPERATOR;
1075
- modifiedRule.options.operator = constants.operatorTypes.GREATER_THAN_EQUAL;
1076
- modifiedRule.value = [rule[rule.rule].value];
1077
- break;
1078
- case "isEmpty":
1079
- modifiedRule.case = constants.rulesTypes.EMPTY;
1080
- modifiedRule.value = [];
1081
- break;
1082
- case "isNotEmpty":
1083
- modifiedRule.case = constants.rulesTypes.NOT_EMPTY;
1084
- modifiedRule.value = [];
1085
- break;
1086
- case "isOneOf":
1087
- modifiedRule.case = constants.rulesTypes.ONE_OF;
1088
- modifiedRule.value = rule[rule.rule].value;
1089
- break;
1090
- case "isNotOneOf":
1091
- modifiedRule.case = constants.rulesTypes.NOT_ONE_OF;
1092
- modifiedRule.value = [rule[rule.rule].value];
1093
- break;
1094
- case constants.rulesTypes.FIELD_COMPARE: {
1095
- const fieldRule = generateFieldCompareRules(rule);
1096
- modifiedRule.case = fieldRule.case;
1097
- modifiedRule.options = fieldRule.options;
1098
- modifiedRule.value = fieldRule.value;
1099
- break;
1100
- }
1101
- }
1102
-
1103
- return modifiedRule;
1104
- });
1127
+ rules.push(modifiedRule);
1128
+ }
1129
+ return rules;
1105
1130
  };
1106
1131
 
1107
1132
  const getFieldsGroupBySchemaId = (arr) => {
package/lib/validate.js CHANGED
@@ -10,6 +10,8 @@ const {
10
10
  getParentKey,
11
11
  } = require("./helpers");
12
12
 
13
+ const choices = ["radio", "checkboxes", "dropdown_multiple", "dropdown"];
14
+
13
15
  const typeChecks = {
14
16
  date: (val, data) => {
15
17
  if (val instanceof Date && !isNaN(val)) return true;
@@ -139,7 +141,7 @@ const isEmptyRelational = ({ api_version, value, interface }) => {
139
141
  if (interface === constants.interfaces.MANY_TO_ANY) {
140
142
  return (
141
143
  value &&
142
- typeChecks[constants.types.OBJECT](value) &&
144
+ typeChecks[constants.types.OBJECT](value) &&
143
145
  value.hasOwnProperty("collection") &&
144
146
  value.hasOwnProperty("sort") &&
145
147
  value.hasOwnProperty("item") &&
@@ -170,7 +172,7 @@ const validateMetaRules = (
170
172
  apiVersion
171
173
  ) => {
172
174
  const fieldValue = providedValue;
173
- const { required = false, nullable = false } = field?.meta ?? {};
175
+ const { required = false, nullable = false, options } = field?.meta ?? {};
174
176
  const isRelational = [
175
177
  constants.interfaces.FILES,
176
178
  constants.interfaces.FILE,
@@ -182,6 +184,23 @@ const validateMetaRules = (
182
184
  constants.interfaces.TRANSLATIONS,
183
185
  ].includes(field?.meta?.interface);
184
186
 
187
+ if (
188
+ choices.includes(field?.meta?.interface) &&
189
+ (!options?.choices || !options?.choices?.length > 0)
190
+ ) {
191
+ const message = error_messages.ADD_CHOICE.replace(`{field}`, formatLabel(field.display_label));
192
+ addError(
193
+ currentPath,
194
+ {
195
+ label: formatLabel(field.display_label),
196
+ fieldPath: currentPath,
197
+ description: "",
198
+ message,
199
+ },
200
+ field
201
+ );
202
+ }
203
+
185
204
  const isValidRelational =
186
205
  required && isRelational && !onlyFormFields
187
206
  ? isEmptyRelational({
@@ -191,7 +210,7 @@ const validateMetaRules = (
191
210
  })
192
211
  : true;
193
212
 
194
- if ((required && isEmpty(fieldValue) && fieldValue !== null) || !isValidRelational) {
213
+ if ((required && isEmpty(fieldValue)) || !isValidRelational) {
195
214
  const message = error_messages.REQUIRED.replace(`{field}`, formatLabel(field.display_label));
196
215
  addError(
197
216
  currentPath,
@@ -682,8 +701,14 @@ const validateField = (
682
701
  const itemType = field?.array_type || field?.schema_definition?.type;
683
702
  if (itemType) {
684
703
  value.forEach((item, index) => {
704
+ const itemPath = `${currentPath}[${index}]`;
705
+
706
+ if (choices.includes(field?.meta?.interface)) {
707
+ applyValidations(field, item, addError, itemPath, formData, error_messages);
708
+ }
709
+
685
710
  if (!typeChecks[itemType](item)) {
686
- addError(`${currentPath}[${index}]`, {
711
+ addError(itemPath, {
687
712
  label: fieldLabel,
688
713
  fieldPath: `${currentPath}[${index}]`,
689
714
  description: "",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nox-validation",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "validate dynamic schema",
5
5
  "main": "index.js",
6
6
  "scripts": {