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.
Files changed (3) hide show
  1. package/README.md +349 -3
  2. package/index.js +623 -2
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,4 +1,350 @@
1
- ### USAGE
1
+ # Data Validation Module
2
2
 
3
- const helloWorld = require('my-npm-package');
4
- console.log(helloWorld()); // Outputs: Hello, world!
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
- export function helloWorld(message, jsonData) {
2
- return JSON.stringify(jsonData) + " " + message;
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "perfect-payload",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "type": "module",
5
5
  "description": "JSON Data Validation",
6
6
  "main": "index.js",