perfect-payload 1.0.1 → 1.0.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 +349 -3
- package/index.js +623 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,350 @@
|
|
|
1
|
-
|
|
1
|
+
# Data Validation Module
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
This module provides a robust framework for validating data objects based on defined rules. It includes various validation attributes, types, custom error messages, and default values to ensure data integrity and consistency.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
1. [Quick Sight](#default-values)
|
|
8
|
+
2. [Available Validation Attributes](#available-validation-attributes)
|
|
9
|
+
3. [Available Types](#available-types)
|
|
10
|
+
4. [Custom Error Message Attributes](#custom-error-message-attributes)
|
|
11
|
+
5. [Default Values](#default-values)
|
|
12
|
+
6. [Examples and Usage](#default-values)
|
|
13
|
+
|
|
14
|
+
## 1. Quick Signht
|
|
15
|
+
|
|
16
|
+
usage:
|
|
17
|
+
|
|
18
|
+
```javascript
|
|
19
|
+
import { perfectPayloadV1 } from"perfect-payload";
|
|
20
|
+
const {statucCode=400, ...result}=perfectPayloadV1(data,dataValidationRule,validPayloadResponse,inValidPayloadResponse)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
input:
|
|
24
|
+
|
|
25
|
+
1. **data :** your payload object (required *)
|
|
26
|
+
2. **dataValidationRule:** validation rule object (required *)
|
|
27
|
+
3. **validPayloadResponse:** response you want if all attribute validation passed (Optional)
|
|
28
|
+
4. **inValidPayloadResponse:** response you want any validation fails (Optional)
|
|
29
|
+
|
|
30
|
+
**Default Valid Payload Response:**
|
|
31
|
+
|
|
32
|
+
```javascript
|
|
33
|
+
{
|
|
34
|
+
statusCode:200,
|
|
35
|
+
valid:true
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
**Default In-Valid Payload Response:**
|
|
41
|
+
|
|
42
|
+
```javascript
|
|
43
|
+
{
|
|
44
|
+
statusCode:400,
|
|
45
|
+
valid:false,
|
|
46
|
+
message:"One or more attribute values are invalid",
|
|
47
|
+
errors:["array of each attribute error messages"]
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
## 2. Available Validation Attributes
|
|
53
|
+
|
|
54
|
+
These attributes define the rules that can be applied to each field in the data object:
|
|
55
|
+
|
|
56
|
+
- **mandatory**: Specifies whether the data is required.
|
|
57
|
+
- **allowNull**: Allows `null` values.
|
|
58
|
+
- **allowEmptyObject**: Allows empty objects `{}`.
|
|
59
|
+
- **allowEmptyArray**: Allows empty arrays `[]`.
|
|
60
|
+
- **elementType**: Defines the type of elements in an array (refer to [Available Types](#available-types) section).
|
|
61
|
+
- **regex**: Applies custom regular expression validation.
|
|
62
|
+
- **type**: Specifies the type of the field (refer to [Available Types](#available-types) section).
|
|
63
|
+
- **minLength**: Minimum length for string values.
|
|
64
|
+
- **maxLength**: Maximum length for string values.
|
|
65
|
+
- **preventDecimal**: Disallows decimal or fractional values.
|
|
66
|
+
- **min**: Specifies the minimum number value.
|
|
67
|
+
- **max**: Specifies the maximum number value.
|
|
68
|
+
- **range**: Specifies a numeric range for the value.
|
|
69
|
+
- **objectAttr**: Validates nested objects.
|
|
70
|
+
- **dependency**: Validates fields that depend on the values of other fields (e.g., min and max salary).
|
|
71
|
+
|
|
72
|
+
## 3. Available Types
|
|
73
|
+
|
|
74
|
+
These types can be used in the `type` attribute to specify the expected data type:
|
|
75
|
+
|
|
76
|
+
- **number**: Numeric values.
|
|
77
|
+
- **string**: String values.
|
|
78
|
+
- **boolean**: Boolean values (`true`/`false`).
|
|
79
|
+
- **email**: Valid email addresses.
|
|
80
|
+
- **url**: Valid URL formats.
|
|
81
|
+
- **enum**: A specific set of allowed values (supports heterogeneous arrays).
|
|
82
|
+
- **uuid**: Valid UUIDs (supports all versions).
|
|
83
|
+
- **uuidv1/uuidv3/uuidv4/uuidv5**: Version-specific UUID validation.
|
|
84
|
+
- **objectId**: Valid MongoDB ObjectIds.
|
|
85
|
+
- **array**: Validates arrays, with support for element type validation using `elementType`.
|
|
86
|
+
- **object**: Validates objects, including nested objects using `objectAttr`.
|
|
87
|
+
|
|
88
|
+
## 4. Custom Error Message Attributes
|
|
89
|
+
|
|
90
|
+
You can define custom error messages for various validation failures using these attributes:
|
|
91
|
+
|
|
92
|
+
- **mandatoryError**: Custom message when a mandatory field is missing.
|
|
93
|
+
- **allowNullError**: Custom message when a `null` value is not allowed.
|
|
94
|
+
- **emptyObjectError**: Custom message when an empty object `{}` is not allowed.
|
|
95
|
+
- **emptyArrayError**: Custom message when an empty array `[]` is not allowed.
|
|
96
|
+
- **elementTypeError**: Custom message when array elements do not match the specified type.
|
|
97
|
+
- **regexError**: Custom message when a value does not match the specified regex pattern.
|
|
98
|
+
- **typeError**: Custom message when a value does not match the specified type.
|
|
99
|
+
- **minLengthError**: Custom message when a string value is shorter than the minimum length.
|
|
100
|
+
- **maxLengthError**: Custom message when a string value exceeds the maximum length.
|
|
101
|
+
- **preventDecimalError**: Custom message when a decimal value is not allowed.
|
|
102
|
+
- **minError**: Custom message when a value is less than the minimum allowed.
|
|
103
|
+
- **maxError**: Custom message when a value exceeds the maximum allowed.
|
|
104
|
+
- **rangeError**: Custom message when a value is outside the specified range.
|
|
105
|
+
|
|
106
|
+
## 5. Default Values
|
|
107
|
+
|
|
108
|
+
If not explicitly specified, the following default values are applied:
|
|
109
|
+
|
|
110
|
+
- **mandatory**: `false` (field is not required).
|
|
111
|
+
- **allowNull**: `true` (allows `null` values).
|
|
112
|
+
- **allowEmptyObject**: `true` (allows empty objects `{}`).
|
|
113
|
+
- **allowEmptyArray**: `true` (allows empty arrays `[]`).
|
|
114
|
+
- **elementType**: Ignored.
|
|
115
|
+
- **regex**: Ignored.
|
|
116
|
+
- **type**: Ignored.
|
|
117
|
+
- **minLength**: Ignored.
|
|
118
|
+
- **maxLength**: Ignored.
|
|
119
|
+
- **preventDecimal**: `false` (allows both decimal and integer values).
|
|
120
|
+
- **min**: Ignored.
|
|
121
|
+
- **max**: Ignored.
|
|
122
|
+
- **range**: Ignored.
|
|
123
|
+
- **objectAttr**: Ignored.
|
|
124
|
+
- **dependency**: Ignored.
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
## 6. Examples And Usage
|
|
128
|
+
|
|
129
|
+
### 1. Sample Validation Rule
|
|
130
|
+
|
|
131
|
+
sample-1
|
|
132
|
+
|
|
133
|
+
```javascript
|
|
134
|
+
{
|
|
135
|
+
firstName: {
|
|
136
|
+
mandatory: true,
|
|
137
|
+
allowNull: false,
|
|
138
|
+
type: "string",
|
|
139
|
+
minLength: 3,
|
|
140
|
+
},
|
|
141
|
+
lastName: {
|
|
142
|
+
mandatory: false,
|
|
143
|
+
allowNull: true,
|
|
144
|
+
type: "string",
|
|
145
|
+
},
|
|
146
|
+
email: {
|
|
147
|
+
mandatory: true,
|
|
148
|
+
allowNull: false,
|
|
149
|
+
type: "email",
|
|
150
|
+
},
|
|
151
|
+
phone: {
|
|
152
|
+
mandatory: true,
|
|
153
|
+
allowNull: false,
|
|
154
|
+
type: "string",
|
|
155
|
+
},
|
|
156
|
+
age: {
|
|
157
|
+
mandatory: false,
|
|
158
|
+
type: "number",
|
|
159
|
+
min: 1,
|
|
160
|
+
max: 120,
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
sample-2
|
|
168
|
+
|
|
169
|
+
```javascript
|
|
170
|
+
{
|
|
171
|
+
id: {
|
|
172
|
+
mandatory: true,
|
|
173
|
+
allowNull: true,
|
|
174
|
+
type: "uuidv4",
|
|
175
|
+
},
|
|
176
|
+
batchId: {
|
|
177
|
+
mandatory: true,
|
|
178
|
+
allowNull: true,
|
|
179
|
+
type: "objectId",
|
|
180
|
+
},
|
|
181
|
+
firstName: {
|
|
182
|
+
mandatory: true,
|
|
183
|
+
allowNull: false,
|
|
184
|
+
type: "string",
|
|
185
|
+
minLength: 3,
|
|
186
|
+
maxLength: 6,
|
|
187
|
+
},
|
|
188
|
+
lastName: {
|
|
189
|
+
mandatory: false,
|
|
190
|
+
allowNull: true,
|
|
191
|
+
type: "string",
|
|
192
|
+
},
|
|
193
|
+
age: {
|
|
194
|
+
type: "number",
|
|
195
|
+
min: 0.1,
|
|
196
|
+
max: 120,
|
|
197
|
+
},
|
|
198
|
+
isAdult: {
|
|
199
|
+
type: "boolean",
|
|
200
|
+
},
|
|
201
|
+
totalWins: {
|
|
202
|
+
type: "number",
|
|
203
|
+
min: 0,
|
|
204
|
+
preventDecimal: true,
|
|
205
|
+
},
|
|
206
|
+
email: {
|
|
207
|
+
type: "email",
|
|
208
|
+
regex: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
|
|
209
|
+
},
|
|
210
|
+
githubLink: {
|
|
211
|
+
type: "url",
|
|
212
|
+
},
|
|
213
|
+
accountStatus: {
|
|
214
|
+
type: "enum",
|
|
215
|
+
enumValues: ["Active", "Inactive", 200],
|
|
216
|
+
},
|
|
217
|
+
marks: {
|
|
218
|
+
range: "0-100",
|
|
219
|
+
},
|
|
220
|
+
allMarks: {
|
|
221
|
+
type: "array",
|
|
222
|
+
elementType: "string",
|
|
223
|
+
allowEmptyArray: false,
|
|
224
|
+
},
|
|
225
|
+
totalScore: {
|
|
226
|
+
type: "number",
|
|
227
|
+
dependency: {
|
|
228
|
+
result: {
|
|
229
|
+
setDependencyRule: (totalScore, result) => {
|
|
230
|
+
return { mandatory: true, allowNull: false, type: "string" };
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
result: {
|
|
236
|
+
type: "string",
|
|
237
|
+
dependency: {
|
|
238
|
+
totalScore: {
|
|
239
|
+
setDependencyRule: (result, totalScore) => {
|
|
240
|
+
return { mandatory: true, allowNull: false, type: "number" };
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
minSalary: {
|
|
246
|
+
mandatory: true,
|
|
247
|
+
min: 1,
|
|
248
|
+
type: "number",
|
|
249
|
+
dependency: {
|
|
250
|
+
maxSalary: {
|
|
251
|
+
setDependencyRule: (minSalary, maxSalary) => {
|
|
252
|
+
return {
|
|
253
|
+
mandatory: true,
|
|
254
|
+
min: minSalary + 1,
|
|
255
|
+
minError: "maxSalary must be more than minSalary",
|
|
256
|
+
};
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
maxSalary: {
|
|
262
|
+
dependency: {
|
|
263
|
+
minSalary: {
|
|
264
|
+
setDependencyRule: (maxSalary, minSalary) => {
|
|
265
|
+
return {
|
|
266
|
+
mandatory: true,
|
|
267
|
+
max: maxSalary - 1,
|
|
268
|
+
maxError: "minSalary must be less than maxSalary",
|
|
269
|
+
};
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
address: {
|
|
275
|
+
mandatory: true,
|
|
276
|
+
type: "object",
|
|
277
|
+
allowEmptyObject: false,
|
|
278
|
+
objectAttr: {
|
|
279
|
+
country: { mandatory: true, type: "string" },
|
|
280
|
+
state: {
|
|
281
|
+
mandatory: true,
|
|
282
|
+
type: "string",
|
|
283
|
+
},
|
|
284
|
+
city: {},
|
|
285
|
+
zip: {
|
|
286
|
+
mandatory: true,
|
|
287
|
+
type: "string",
|
|
288
|
+
},
|
|
289
|
+
position: {
|
|
290
|
+
mandatory: true,
|
|
291
|
+
type: "object",
|
|
292
|
+
allowEmptyObject: false,
|
|
293
|
+
objectAttr: {
|
|
294
|
+
lattitude: { mandatory: true, type: "number" },
|
|
295
|
+
longitude: {
|
|
296
|
+
mandatory: true,
|
|
297
|
+
type: "number",
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
### 2. Usage
|
|
309
|
+
|
|
310
|
+
#### 1. creating your route with middleware and validation rule:
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
```javascript
|
|
314
|
+
//Here validatePayload is your middleware function where you're invoking perfect payload
|
|
315
|
+
router.post(
|
|
316
|
+
"/payload-validation",
|
|
317
|
+
validatePayload({ rule: <your validation rule json object> }),
|
|
318
|
+
(req, res) => res.send("OK")
|
|
319
|
+
);
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
#### 2. Use perfect-payload in your middleware like below
|
|
323
|
+
|
|
324
|
+
```javascript
|
|
325
|
+
import { perfectPayloadV1 } from "perfect-payload";
|
|
326
|
+
|
|
327
|
+
export const validatePayload = ({ rule }) => {
|
|
328
|
+
return (req, res, next) => {
|
|
329
|
+
const { statusCode, ...response } = perfectPayloadV1(req?.body, rule);
|
|
330
|
+
if (+statusCode >= 200 && +statusCode <= 299) {
|
|
331
|
+
next();
|
|
332
|
+
} else res.status(statusCode).json(response);
|
|
333
|
+
};
|
|
334
|
+
};
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
#### Usage in venila js code
|
|
339
|
+
|
|
340
|
+
```javascript
|
|
341
|
+
import { perfectPayloadV1 } from "perfect-payload";
|
|
342
|
+
const result = perfectPayloadV1(dataObject, ruleObject);
|
|
343
|
+
console.log(result?.statusCode)
|
|
344
|
+
console.log(result?.isValid)
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
This documentation provides a comprehensive guide to using the data validation module effectively. Ensure to define your validation rules clearly to maintain data quality and consistency in your applications.
|
package/index.js
CHANGED
|
@@ -1,3 +1,624 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
//-----------AVAILABLE VALIATION ATTRIBUTES-----------
|
|
2
|
+
// mandatory : required data
|
|
3
|
+
// allowNull : allow null
|
|
4
|
+
// allowEmptyObject : allow {}
|
|
5
|
+
// allowEmptyArray : allow []
|
|
6
|
+
// elementType : define array elements type(values: AVAILABLE TYPE Section)
|
|
7
|
+
// regex : custom regex validation
|
|
8
|
+
// type : refer AVAILABLE TYPE Section
|
|
9
|
+
// minLength : for string length
|
|
10
|
+
// maxLength : for string length
|
|
11
|
+
// preventDecimal : decimal/fraction value nor allowed
|
|
12
|
+
// min : for minimum number value
|
|
13
|
+
// max : for maximum number value
|
|
14
|
+
// range : for number, between 2 range
|
|
15
|
+
// objectAttr : for nested object validation
|
|
16
|
+
// dependency : for dependent field validation(ex: min and max salary,etc. customizable)
|
|
17
|
+
//
|
|
18
|
+
//
|
|
19
|
+
//--------AVAILABLE TYPE-----------
|
|
20
|
+
// number :
|
|
21
|
+
// string :
|
|
22
|
+
// boolean :
|
|
23
|
+
// email :
|
|
24
|
+
// url :
|
|
25
|
+
// enum : array of values(heterogeneous values supported)
|
|
26
|
+
// uuid/uuidv1/uuidv3/uuidv4/uuidv5 : "uuid" for all version and version specific type for specific version
|
|
27
|
+
// objectId :
|
|
28
|
+
// array : can validate element tyoe with elementType attribute(PENDING)
|
|
29
|
+
// object : supports nested object validation as well with objectAttr attribute
|
|
30
|
+
//
|
|
31
|
+
//
|
|
32
|
+
// ---------Custom Error Message Attributes-----------
|
|
33
|
+
// mandatoryError : string
|
|
34
|
+
// allowNullError : string
|
|
35
|
+
// emptyObjectError : string
|
|
36
|
+
// emptyArrayError : string
|
|
37
|
+
// elementTypeError : string
|
|
38
|
+
// regexError : string
|
|
39
|
+
// typeError : string
|
|
40
|
+
// minLengthError : string
|
|
41
|
+
// maxLengthError : string
|
|
42
|
+
// preventDecimalError : string
|
|
43
|
+
// minError : string
|
|
44
|
+
// maxError : string
|
|
45
|
+
// rangeError : string
|
|
46
|
+
//
|
|
47
|
+
//
|
|
48
|
+
//--------DEFAULT VALUES IF NOT SPECIFIED-----------
|
|
49
|
+
// mandatory : false
|
|
50
|
+
// allowNull : true
|
|
51
|
+
// allowEmptyObject : true
|
|
52
|
+
// allowEmptyArray : true
|
|
53
|
+
// elementType : ignored
|
|
54
|
+
// regex : ignored
|
|
55
|
+
// type : ignored
|
|
56
|
+
// minLength : ignored
|
|
57
|
+
// maxLength : ignored
|
|
58
|
+
// preventDecimal : false(allows both decimal and integer)
|
|
59
|
+
// min : ignored
|
|
60
|
+
// max : ignored
|
|
61
|
+
// range : ignored
|
|
62
|
+
// objectAttr : ignored
|
|
63
|
+
// dependency : ignored
|
|
64
|
+
//
|
|
65
|
+
//
|
|
66
|
+
//
|
|
67
|
+
//
|
|
68
|
+
//
|
|
69
|
+
|
|
70
|
+
export function perfectPayloadV1(
|
|
71
|
+
data = {},
|
|
72
|
+
dataValidationRule = {},
|
|
73
|
+
validPayloadResponse = { statusCode: 200, valid: true },
|
|
74
|
+
inValidPayloadResponse = {
|
|
75
|
+
statusCode: 400,
|
|
76
|
+
valid: false,
|
|
77
|
+
message: "One or more attribute values are invalid",
|
|
78
|
+
}
|
|
79
|
+
) {
|
|
80
|
+
let rowErrors = [];
|
|
81
|
+
for (const attributeName in dataValidationRule) {
|
|
82
|
+
let addNextError = true;
|
|
83
|
+
const attributeRules = dataValidationRule?.[attributeName];
|
|
84
|
+
const attrPath = dataValidationRule?.[attributeName]?.path || null;
|
|
85
|
+
for (const ruleName in attributeRules) {
|
|
86
|
+
const attributePath = `${attrPath ? attrPath + "." : ""}${attributeName}`;
|
|
87
|
+
const attributeValue = data?.[attributeName] ?? null;
|
|
88
|
+
const nullAllowed =
|
|
89
|
+
dataValidationRule?.[attributeName]?.["allowNull"] ?? true;
|
|
90
|
+
const isMandatoryField = attributeRules?.["mandatory"] ?? false;
|
|
91
|
+
const attrExist = Object.keys(data)?.includes(attributeName); //Mandatory value check
|
|
92
|
+
|
|
93
|
+
switch (ruleName) {
|
|
94
|
+
case "mandatory":
|
|
95
|
+
//mandatory value check
|
|
96
|
+
if (isMandatoryField && !attrExist) {
|
|
97
|
+
addNextError = false;
|
|
98
|
+
rowErrors.push(
|
|
99
|
+
attributeRules?.["mandatoryError"] ||
|
|
100
|
+
`${attributePath} is mandatory`
|
|
101
|
+
);
|
|
102
|
+
} else if (!attrExist) {
|
|
103
|
+
addNextError = false;
|
|
104
|
+
}
|
|
105
|
+
break;
|
|
106
|
+
case "allowNull":
|
|
107
|
+
//allowNull value check
|
|
108
|
+
if (
|
|
109
|
+
addNextError &&
|
|
110
|
+
ruleName === "allowNull" &&
|
|
111
|
+
attributeValue == null
|
|
112
|
+
) {
|
|
113
|
+
if (!dataValidationRule?.[attributeName]?.[ruleName]) {
|
|
114
|
+
addNextError = false;
|
|
115
|
+
rowErrors.push(
|
|
116
|
+
attributeRules?.["allowNullError"] ||
|
|
117
|
+
`value null/'' not valid for attribute ${attributePath}`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
break;
|
|
122
|
+
case "allowEmptyObject":
|
|
123
|
+
//allowEmptyObject check
|
|
124
|
+
if (addNextError && ruleName === "allowEmptyObject") {
|
|
125
|
+
if (
|
|
126
|
+
!attributeRules?.["allowEmptyObject"] &&
|
|
127
|
+
Object.keys(attributeValue).length == 0
|
|
128
|
+
) {
|
|
129
|
+
rowErrors.push(
|
|
130
|
+
attributeRules?.["emptyObjectError"] ||
|
|
131
|
+
`value {} not valid for attribute ${attributePath}`
|
|
132
|
+
);
|
|
133
|
+
addNextError = false;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
break;
|
|
138
|
+
case "allowEmptyArray":
|
|
139
|
+
//allowEmptyArray check
|
|
140
|
+
if (addNextError && ruleName === "allowEmptyArray") {
|
|
141
|
+
if (
|
|
142
|
+
!attributeRules?.["allowEmptyArray"] &&
|
|
143
|
+
isArray(attributeValue) &&
|
|
144
|
+
attributeValue?.length == 0
|
|
145
|
+
) {
|
|
146
|
+
rowErrors.push(
|
|
147
|
+
attributeRules?.["emptyArrayError"] ||
|
|
148
|
+
`${attributePath} cannot be an empty array`
|
|
149
|
+
);
|
|
150
|
+
addNextError = false;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
break;
|
|
155
|
+
case "elementType":
|
|
156
|
+
//check array ele type
|
|
157
|
+
if (addNextError && ruleName === "elementType") {
|
|
158
|
+
if (isArray(attributeValue) && attributeValue?.length > 0) {
|
|
159
|
+
const hasInvalid = false;
|
|
160
|
+
if (hasInvalid) {
|
|
161
|
+
rowErrors.push(
|
|
162
|
+
attributeRules?.["elementTypeError"] ||
|
|
163
|
+
`invalid type values in ${attributePath}`
|
|
164
|
+
);
|
|
165
|
+
addNextError = false;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
break;
|
|
171
|
+
case "regex":
|
|
172
|
+
//custom regex check
|
|
173
|
+
if (addNextError && ruleName === "regex") {
|
|
174
|
+
if (!isPassedRegex(attributeRules[ruleName], attributeValue)) {
|
|
175
|
+
rowErrors.push(
|
|
176
|
+
attributeRules?.["regexError"] ||
|
|
177
|
+
`${attributePath} failed to pass the regex ${attributeRules[ruleName]}`
|
|
178
|
+
);
|
|
179
|
+
addNextError = false;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
break;
|
|
183
|
+
case "type":
|
|
184
|
+
//value type check
|
|
185
|
+
if (addNextError && ruleName === "type") {
|
|
186
|
+
if (attributeValue == null && nullAllowed) {
|
|
187
|
+
addNextError = true;
|
|
188
|
+
} else {
|
|
189
|
+
switch (attributeRules[ruleName]) {
|
|
190
|
+
case "number":
|
|
191
|
+
if (!isNumber(attributeValue)) {
|
|
192
|
+
rowErrors.push(
|
|
193
|
+
attributeRules?.["typeError"] ||
|
|
194
|
+
`Invalid ${typeof attributeValue} value(${attributeValue}) found in attribute ${attributePath}, required ${
|
|
195
|
+
attributeRules[ruleName]
|
|
196
|
+
} value`
|
|
197
|
+
);
|
|
198
|
+
addNextError = false;
|
|
199
|
+
}
|
|
200
|
+
break;
|
|
201
|
+
|
|
202
|
+
case "string":
|
|
203
|
+
if (!isString(attributeValue)) {
|
|
204
|
+
rowErrors.push(
|
|
205
|
+
attributeRules?.["typeError"] ||
|
|
206
|
+
`Invalid ${typeof attributeValue} value(${attributeValue}) found in attribute ${attributePath}, required ${
|
|
207
|
+
attributeRules[ruleName]
|
|
208
|
+
} value`
|
|
209
|
+
);
|
|
210
|
+
addNextError = false;
|
|
211
|
+
}
|
|
212
|
+
break;
|
|
213
|
+
case "boolean":
|
|
214
|
+
if (!isBoolean(attributeValue)) {
|
|
215
|
+
rowErrors.push(
|
|
216
|
+
attributeRules?.["typeError"] ||
|
|
217
|
+
`Invalid ${typeof attributeValue} value(${attributeValue}) found in attribute ${attributePath}, required ${
|
|
218
|
+
attributeRules[ruleName]
|
|
219
|
+
} value`
|
|
220
|
+
);
|
|
221
|
+
addNextError = false;
|
|
222
|
+
}
|
|
223
|
+
break;
|
|
224
|
+
case "email":
|
|
225
|
+
if (!isValidEmail(attributeValue)) {
|
|
226
|
+
rowErrors.push(
|
|
227
|
+
attributeRules?.["typeError"] ||
|
|
228
|
+
`Invalid email ID(${attributeValue}) found in attribute ${attributePath}`
|
|
229
|
+
);
|
|
230
|
+
addNextError = false;
|
|
231
|
+
}
|
|
232
|
+
break;
|
|
233
|
+
|
|
234
|
+
case "url":
|
|
235
|
+
if (!isValidUrl(attributeValue)) {
|
|
236
|
+
rowErrors.push(
|
|
237
|
+
attributeRules?.["typeError"] ||
|
|
238
|
+
`Invalid URL format(${attributeValue}) found in attribute ${attributePath}`
|
|
239
|
+
);
|
|
240
|
+
addNextError = false;
|
|
241
|
+
}
|
|
242
|
+
break;
|
|
243
|
+
case "enum":
|
|
244
|
+
if (attributeValue == null && nullAllowed) {
|
|
245
|
+
addNextError = true;
|
|
246
|
+
} else {
|
|
247
|
+
const allEnumValues = attributeRules["enumValues"];
|
|
248
|
+
if (!allEnumValues?.includes(attributeValue)) {
|
|
249
|
+
rowErrors.push(
|
|
250
|
+
attributeRules?.["typeError"] ||
|
|
251
|
+
`Invalid value(${attributeValue}) found in attribute ${attributePath}, valid values are ${allEnumValues?.join(
|
|
252
|
+
", "
|
|
253
|
+
)}`
|
|
254
|
+
);
|
|
255
|
+
addNextError = false;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
break;
|
|
259
|
+
case "uuid":
|
|
260
|
+
if (attributeValue == null && nullAllowed) {
|
|
261
|
+
addNextError = true;
|
|
262
|
+
} else if (!isUUID(attributeValue)) {
|
|
263
|
+
rowErrors.push(
|
|
264
|
+
attributeRules?.["typeError"] ||
|
|
265
|
+
`Invalid UUID(${attributeValue}) found in attribute ${attributePath}`
|
|
266
|
+
);
|
|
267
|
+
addNextError = false;
|
|
268
|
+
}
|
|
269
|
+
break;
|
|
270
|
+
case "uuidv1":
|
|
271
|
+
if (attributeValue == null && nullAllowed) {
|
|
272
|
+
addNextError = true;
|
|
273
|
+
} else if (!isUUIDv1(attributeValue)) {
|
|
274
|
+
rowErrors.push(
|
|
275
|
+
attributeRules?.["typeError"] ||
|
|
276
|
+
`Invalid v1 UUID(${attributeValue}) found in attribute ${attributePath}`
|
|
277
|
+
);
|
|
278
|
+
addNextError = false;
|
|
279
|
+
}
|
|
280
|
+
break;
|
|
281
|
+
case "uuidv3":
|
|
282
|
+
if (attributeValue == null && nullAllowed) {
|
|
283
|
+
addNextError = true;
|
|
284
|
+
} else if (!isUUIDv3(attributeValue)) {
|
|
285
|
+
rowErrors.push(
|
|
286
|
+
attributeRules?.["typeError"] ||
|
|
287
|
+
`Invalid v3 UUID(${attributeValue}) found in attribute ${attributePath}`
|
|
288
|
+
);
|
|
289
|
+
addNextError = false;
|
|
290
|
+
}
|
|
291
|
+
break;
|
|
292
|
+
case "uuidv4":
|
|
293
|
+
if (attributeValue == null && nullAllowed) {
|
|
294
|
+
addNextError = true;
|
|
295
|
+
} else if (!isUUIDv4(attributeValue)) {
|
|
296
|
+
rowErrors.push(
|
|
297
|
+
attributeRules?.["typeError"] ||
|
|
298
|
+
`Invalid v4 UUID(${attributeValue}) found in attribute ${attributePath}`
|
|
299
|
+
);
|
|
300
|
+
addNextError = false;
|
|
301
|
+
}
|
|
302
|
+
break;
|
|
303
|
+
case "uuidv5":
|
|
304
|
+
if (attributeValue == null && nullAllowed) {
|
|
305
|
+
addNextError = true;
|
|
306
|
+
} else if (!isUUIDv5(attributeValue)) {
|
|
307
|
+
rowErrors.push(
|
|
308
|
+
attributeRules?.["typeError"] ||
|
|
309
|
+
`Invalid v5 UUID(${attributeValue}) found in attribute ${attributePath}`
|
|
310
|
+
);
|
|
311
|
+
addNextError = false;
|
|
312
|
+
}
|
|
313
|
+
break;
|
|
314
|
+
case "objectId":
|
|
315
|
+
if (attributeValue == null && nullAllowed) {
|
|
316
|
+
addNextError = true;
|
|
317
|
+
} else {
|
|
318
|
+
if (!isObjectId(attributeValue)) {
|
|
319
|
+
rowErrors.push(
|
|
320
|
+
attributeRules?.["typeError"] ||
|
|
321
|
+
`Invalid ObjectId(${attributeValue}) found in attribute ${attributePath}`
|
|
322
|
+
);
|
|
323
|
+
addNextError = false;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
break;
|
|
327
|
+
|
|
328
|
+
case "array":
|
|
329
|
+
if (!isArray(attributeValue)) {
|
|
330
|
+
rowErrors.push(
|
|
331
|
+
attributeRules?.["typeError"] ||
|
|
332
|
+
`Invalid ${typeof attributeValue} value(${attributeValue}) found in attribute ${attributePath}, required ${
|
|
333
|
+
attributeRules[ruleName]
|
|
334
|
+
} value`
|
|
335
|
+
);
|
|
336
|
+
addNextError = false;
|
|
337
|
+
}
|
|
338
|
+
break;
|
|
339
|
+
|
|
340
|
+
case "object":
|
|
341
|
+
if (!isObject(attributeValue)) {
|
|
342
|
+
rowErrors.push(
|
|
343
|
+
attributeRules?.["typeError"] ||
|
|
344
|
+
`Invalid ${typeof attributeValue} value(${attributeValue}) found in attribute ${attributePath}, required ${
|
|
345
|
+
attributeRules[ruleName]
|
|
346
|
+
} value`
|
|
347
|
+
);
|
|
348
|
+
addNextError = false;
|
|
349
|
+
}
|
|
350
|
+
break;
|
|
351
|
+
|
|
352
|
+
default:
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
break;
|
|
359
|
+
case "minLength":
|
|
360
|
+
//minimum string length check
|
|
361
|
+
if (addNextError && ruleName === "minLength") {
|
|
362
|
+
if (attributeValue == null && nullAllowed) {
|
|
363
|
+
addNextError = true;
|
|
364
|
+
} else if (typeof attributeValue == "string") {
|
|
365
|
+
if (attributeValue.length < +attributeRules[ruleName])
|
|
366
|
+
rowErrors.push(
|
|
367
|
+
attributeRules?.["minLengthError"] ||
|
|
368
|
+
`${attributePath} should be minimum of ${attributeRules[ruleName]} character`
|
|
369
|
+
);
|
|
370
|
+
addNextError = false;
|
|
371
|
+
} else {
|
|
372
|
+
throw new Error(
|
|
373
|
+
`perfect-payload:- minLength is applied only on string type values, found ${typeof attributeValue} type`
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
break;
|
|
378
|
+
case "maxLength":
|
|
379
|
+
//max string length check
|
|
380
|
+
if (addNextError && ruleName === "maxLength") {
|
|
381
|
+
if (attributeValue == null && nullAllowed) {
|
|
382
|
+
addNextError = true;
|
|
383
|
+
} else if (typeof attributeValue == "string") {
|
|
384
|
+
if (attributeValue.length > +attributeRules[ruleName])
|
|
385
|
+
rowErrors.push(
|
|
386
|
+
attributeRules?.["maxLengthError"] ||
|
|
387
|
+
`${attributePath} can have maximum of ${attributeRules[ruleName]} character`
|
|
388
|
+
);
|
|
389
|
+
addNextError = false;
|
|
390
|
+
} else {
|
|
391
|
+
throw new Error(
|
|
392
|
+
`perfect-payload:- maxLength is applied only on string type values, found ${typeof attributeValue} type`
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
break;
|
|
397
|
+
case "preventDecimal":
|
|
398
|
+
//preventFraction
|
|
399
|
+
if (addNextError && ruleName === "preventDecimal") {
|
|
400
|
+
if (attributeValue == null && nullAllowed) {
|
|
401
|
+
addNextError = true;
|
|
402
|
+
} else if (!isNumber(attributeValue)) {
|
|
403
|
+
rowErrors.push(
|
|
404
|
+
`Invalid ${typeof attributeValue} value(${attributeValue}) found in attribute ${attributePath}, required number value`
|
|
405
|
+
);
|
|
406
|
+
addNextError = false;
|
|
407
|
+
} else if (+attributeValue % 1 !== 0) {
|
|
408
|
+
rowErrors.push(
|
|
409
|
+
attributeRules?.["preventDecimalError"] ||
|
|
410
|
+
`Decimal value not allowed in attribute ${attributePath}(${attributeValue})`
|
|
411
|
+
);
|
|
412
|
+
addNextError = false;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
break;
|
|
416
|
+
case "min":
|
|
417
|
+
//minimum value check
|
|
418
|
+
if (addNextError && ruleName === "min") {
|
|
419
|
+
if (attributeValue == null && nullAllowed) {
|
|
420
|
+
addNextError = true;
|
|
421
|
+
} else if (!isNumber(attributeValue)) {
|
|
422
|
+
rowErrors.push(
|
|
423
|
+
`Invalid ${typeof attributeValue} value(${attributeValue}) found in attribute ${attributePath}, required number value`
|
|
424
|
+
);
|
|
425
|
+
addNextError = false;
|
|
426
|
+
} else if (+attributeValue < +attributeRules[ruleName]) {
|
|
427
|
+
rowErrors.push(
|
|
428
|
+
attributeRules?.["minError"] ||
|
|
429
|
+
`minimum value ${
|
|
430
|
+
attributeRules[ruleName]
|
|
431
|
+
} is allowed in attribute ${attributePath}, found value ${
|
|
432
|
+
data[attributeName] ?? "Nil"
|
|
433
|
+
}`
|
|
434
|
+
);
|
|
435
|
+
addNextError = false;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
break;
|
|
439
|
+
case "max":
|
|
440
|
+
//maximum value check
|
|
441
|
+
if (addNextError && ruleName === "max") {
|
|
442
|
+
if (attributeValue == null && nullAllowed) {
|
|
443
|
+
addNextError = true;
|
|
444
|
+
} else if (!isNumber(attributeValue)) {
|
|
445
|
+
rowErrors.push(
|
|
446
|
+
`Invalid ${typeof attributeValue} value(${attributeValue}) found in attribute ${attributePath}, required number value`
|
|
447
|
+
);
|
|
448
|
+
addNextError = false;
|
|
449
|
+
} else if (+attributeValue > +attributeRules[ruleName]) {
|
|
450
|
+
rowErrors.push(
|
|
451
|
+
attributeRules?.["maxError"] ||
|
|
452
|
+
`maximum value ${
|
|
453
|
+
attributeRules[ruleName]
|
|
454
|
+
} is allowed in attribute ${attributePath}, found value ${
|
|
455
|
+
data[attributeName] ?? "Nil"
|
|
456
|
+
}`
|
|
457
|
+
);
|
|
458
|
+
addNextError = false;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
break;
|
|
462
|
+
case "range":
|
|
463
|
+
//value range check
|
|
464
|
+
if (addNextError && ruleName === "range") {
|
|
465
|
+
if (attributeValue == null && nullAllowed) {
|
|
466
|
+
addNextError = true;
|
|
467
|
+
} else if (!isNumber(attributeValue)) {
|
|
468
|
+
rowErrors.push(
|
|
469
|
+
`Invalid ${typeof attributeValue} value(${attributeValue}) found in attribute ${attributePath}, required number value`
|
|
470
|
+
);
|
|
471
|
+
addNextError = false;
|
|
472
|
+
} else {
|
|
473
|
+
const [min, max] = attributeRules[ruleName].split("-");
|
|
474
|
+
if (
|
|
475
|
+
!isNumber(attributeValue) ||
|
|
476
|
+
+attributeValue < +min ||
|
|
477
|
+
+attributeValue > +max
|
|
478
|
+
) {
|
|
479
|
+
rowErrors.push(
|
|
480
|
+
attributeRules?.["rangeError"] ||
|
|
481
|
+
`Invalid values(${attributeValue}) found in attribute ${attributePath}, value should be between ${min} and ${max} `
|
|
482
|
+
);
|
|
483
|
+
addNextError = false;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
break;
|
|
488
|
+
case "objectAttr":
|
|
489
|
+
//nested object attributes check
|
|
490
|
+
if (addNextError && ruleName === "objectAttr") {
|
|
491
|
+
const allObjectAttr = Object.keys(attributeRules?.[ruleName]);
|
|
492
|
+
const objectAttrRules = {};
|
|
493
|
+
for (const attr of allObjectAttr) {
|
|
494
|
+
objectAttrRules[attr] = {
|
|
495
|
+
...attributeRules?.[ruleName]?.[attr],
|
|
496
|
+
path: attributeRules?.[ruleName]?.[attr]?.path
|
|
497
|
+
? attributeRules?.[ruleName]?.[attr]?.path + `.${attr}`
|
|
498
|
+
: `${attributePath}`,
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const { errors = [] } = dataValidatorV1(
|
|
503
|
+
attributeValue,
|
|
504
|
+
objectAttrRules
|
|
505
|
+
);
|
|
506
|
+
rowErrors = [...rowErrors, ...errors];
|
|
507
|
+
addNextError = true;
|
|
508
|
+
}
|
|
509
|
+
break;
|
|
510
|
+
case "dependency":
|
|
511
|
+
//dependency check
|
|
512
|
+
if (addNextError && ruleName === "dependency") {
|
|
513
|
+
const allDependencyAttr = Object.keys(attributeRules?.[ruleName]);
|
|
514
|
+
for (const attr of allDependencyAttr) {
|
|
515
|
+
if (
|
|
516
|
+
typeof attributeRules?.[ruleName]?.[attr]?.setDependencyRule ==
|
|
517
|
+
"function"
|
|
518
|
+
) {
|
|
519
|
+
const newRule = attributeRules?.[ruleName]?.[
|
|
520
|
+
attr
|
|
521
|
+
]?.setDependencyRule(attributeValue, data?.[attr]);
|
|
522
|
+
let newData = { [attributeName]: attributeValue };
|
|
523
|
+
if (Object.keys(data).includes(attr)) {
|
|
524
|
+
newData = { ...newData, [attr]: data?.[attr] };
|
|
525
|
+
}
|
|
526
|
+
const { errors = [] } = dataValidatorV1(newData, {
|
|
527
|
+
[attr]: newRule,
|
|
528
|
+
});
|
|
529
|
+
rowErrors = [...rowErrors, ...errors];
|
|
530
|
+
addNextError = true;
|
|
531
|
+
} else {
|
|
532
|
+
throw new Error(
|
|
533
|
+
`perfect-payload:- function setDependencyRule not found in ${attr} dependency `
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
break;
|
|
539
|
+
default:
|
|
540
|
+
break;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
if (rowErrors?.length) {
|
|
546
|
+
return { ...inValidPayloadResponse, errors: rowErrors };
|
|
547
|
+
} else {
|
|
548
|
+
return validPayloadResponse;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
function isNumber(value) {
|
|
553
|
+
return typeof value == "number";
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
function isString(value) {
|
|
557
|
+
return typeof value == "string";
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
function isBoolean(value) {
|
|
561
|
+
return typeof value == "boolean";
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
function isArray(value) {
|
|
565
|
+
return Array.isArray(value);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function isObject(value) {
|
|
569
|
+
return typeof value == "object";
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
function isValidEmail(email) {
|
|
573
|
+
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
|
574
|
+
return emailRegex.test(email);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function isValidUrl(url) {
|
|
578
|
+
const urlRegex =
|
|
579
|
+
/^(https?:\/\/)([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})(:[0-9]+)?(\/[^\s]*)?(\?[^\s]*)?$/;
|
|
580
|
+
return urlRegex.test(url);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
function isUUID(uuid) {
|
|
584
|
+
const uuidRegex =
|
|
585
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
586
|
+
|
|
587
|
+
return uuidRegex.test(uuid);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
function isUUIDv1(uuid) {
|
|
591
|
+
const uuidRegex =
|
|
592
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-1[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
593
|
+
|
|
594
|
+
return uuidRegex.test(uuid);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
function isUUIDv3(uuid) {
|
|
598
|
+
const uuidRegex =
|
|
599
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
600
|
+
|
|
601
|
+
return uuidRegex.test(uuid);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
function isUUIDv4(uuid) {
|
|
605
|
+
const uuidRegex =
|
|
606
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
607
|
+
|
|
608
|
+
return uuidRegex.test(uuid);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
function isUUIDv5(uuid) {
|
|
612
|
+
const uuidRegex =
|
|
613
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
614
|
+
|
|
615
|
+
return uuidRegex.test(uuid);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
function isObjectId(id) {
|
|
619
|
+
return /^[0-9a-fA-F]{24}$/.test(id);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
function isPassedRegex(reExpression, value) {
|
|
623
|
+
return reExpression.test(value);
|
|
3
624
|
}
|