@dwtechs/antity 0.9.2 → 0.11.0
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 +156 -15
- package/dist/antity.d.ts +6 -4
- package/dist/antity.js +68 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -120,6 +120,8 @@ const entity = new Entity("consumers", [
|
|
|
120
120
|
|
|
121
121
|
// add a consumer. Used when loggin in from user service
|
|
122
122
|
router.post("/", entity.normalize, entity.validate, ...);
|
|
123
|
+
// or use check method to both normalize and validate at once
|
|
124
|
+
router.put("/", entity.check, ...);
|
|
123
125
|
|
|
124
126
|
```
|
|
125
127
|
|
|
@@ -143,6 +145,7 @@ type Type =
|
|
|
143
145
|
"array" |
|
|
144
146
|
"jwt" |
|
|
145
147
|
"symbol" |
|
|
148
|
+
"password" |
|
|
146
149
|
"email" |
|
|
147
150
|
"regex" |
|
|
148
151
|
"json" |
|
|
@@ -162,8 +165,8 @@ type Method = "GET" | "PATCH" | "PUT" | "POST" | "DELETE";
|
|
|
162
165
|
class Property {
|
|
163
166
|
key: string;
|
|
164
167
|
type: Type;
|
|
165
|
-
min: number | Date;
|
|
166
|
-
max: number | Date;
|
|
168
|
+
min: number | Date | null;
|
|
169
|
+
max: number | Date | null;
|
|
167
170
|
required: boolean;
|
|
168
171
|
safe: boolean;
|
|
169
172
|
typeCheck: boolean;
|
|
@@ -184,34 +187,143 @@ class Entity {
|
|
|
184
187
|
set name(name: string);
|
|
185
188
|
|
|
186
189
|
/**
|
|
187
|
-
*
|
|
190
|
+
* Returns a single property object matching the given key.
|
|
188
191
|
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
192
|
+
* - Searches the entity's properties for a property with the specified key
|
|
193
|
+
* - Useful for dynamic validation, normalization, or documentation
|
|
194
|
+
*
|
|
195
|
+
* @param {string} key - The property key to look up
|
|
196
|
+
* @returns {Property | undefined} The Property object if found, otherwise undefined
|
|
197
|
+
*
|
|
198
|
+
* **Input Properties Required:**
|
|
199
|
+
* - `key` (string) - Property key to look up
|
|
200
|
+
*
|
|
201
|
+
* **Output Properties:**
|
|
202
|
+
* - Property object matching the key, or undefined if not found
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* ```typescript
|
|
206
|
+
* const prop = entity.getProp('firstName');
|
|
207
|
+
* // prop contains the Property object for 'firstName' or undefined
|
|
208
|
+
* ```
|
|
191
209
|
*/
|
|
192
210
|
getProp(key: string): Property | undefined;
|
|
193
211
|
|
|
194
212
|
/**
|
|
195
|
-
*
|
|
213
|
+
* Returns all properties configured for a given REST method.
|
|
214
|
+
*
|
|
215
|
+
* - Filters the entity's properties by the specified method (e.g., 'POST', 'GET')
|
|
216
|
+
* - Useful for dynamic validation, normalization, or documentation
|
|
196
217
|
*
|
|
197
|
-
* @param {Method} method - The method to filter properties by.
|
|
198
|
-
* @returns {Property[]}
|
|
218
|
+
* @param {Method} method - The REST method to filter properties by (e.g., 'POST', 'GET')
|
|
219
|
+
* @returns {Property[]} Array of Property objects associated with the method
|
|
220
|
+
*
|
|
221
|
+
* **Input Properties Required:**
|
|
222
|
+
* - `method` (string) - REST method to filter by
|
|
223
|
+
*
|
|
224
|
+
* **Output Properties:**
|
|
225
|
+
* - Array of Property objects matching the method
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* ```typescript
|
|
229
|
+
* const postProps = entity.getPropsByMethod('POST');
|
|
230
|
+
* // postProps contains all properties relevant for POST requests
|
|
231
|
+
* ```
|
|
199
232
|
*/
|
|
200
233
|
getPropsByMethod(method: Method): Property[];
|
|
201
234
|
|
|
202
235
|
/**
|
|
203
|
-
* Normalizes
|
|
204
|
-
*
|
|
236
|
+
* Normalizes each row in req.body.rows according to property config and HTTP method.
|
|
237
|
+
*
|
|
238
|
+
* - Applies sanitization if `sanitize: true` and method matches
|
|
239
|
+
* - Applies normalization if `normalize: true` and method matches
|
|
240
|
+
* - Mutates req.body.rows with sanitized/normalized values
|
|
241
|
+
* - Calls next(error) on failure, next() on success
|
|
242
|
+
*
|
|
243
|
+
* @param {Request} req - Express request object containing rows
|
|
244
|
+
* @param {Response} _res - Express response object (not used)
|
|
245
|
+
* @param {NextFunction} next - Express next function
|
|
246
|
+
*
|
|
247
|
+
* @returns {void}
|
|
248
|
+
*
|
|
249
|
+
* **Input Properties Required:**
|
|
250
|
+
* - `req.body.rows` (array) - Array of objects to normalize
|
|
251
|
+
* - Each property config can specify sanitize, normalize, etc.
|
|
252
|
+
*
|
|
253
|
+
* **Output Properties:**
|
|
254
|
+
* - Mutates `req.body.rows` with sanitized/normalized values
|
|
255
|
+
* - Calls next(error) if any row fails normalization, next() if all pass
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* ```typescript
|
|
259
|
+
* router.post('/entity', entity.normalize, (req, res) => {
|
|
260
|
+
* // req.body.rows are now sanitized and normalized
|
|
261
|
+
* res.json({ success: true });
|
|
262
|
+
* });
|
|
263
|
+
* ```
|
|
205
264
|
*/
|
|
206
|
-
normalize(req: Request, _res: Response, next: NextFunction)
|
|
265
|
+
normalize: (req: Request, _res: Response, next: NextFunction) => void;
|
|
207
266
|
|
|
208
267
|
/**
|
|
209
|
-
* Validates
|
|
268
|
+
* Validates each row in req.body.rows according to property config and HTTP method.
|
|
269
|
+
*
|
|
270
|
+
* - Checks required properties and validates values
|
|
271
|
+
* - Calls next(error) on failure, next() on success
|
|
272
|
+
*
|
|
273
|
+
* @param {Request} req - Express request object containing rows
|
|
274
|
+
* @param {Response} _res - Express response object (not used)
|
|
275
|
+
* @param {NextFunction} next - Express next function
|
|
276
|
+
*
|
|
277
|
+
* @returns {void}
|
|
278
|
+
*
|
|
279
|
+
* **Input Properties Required:**
|
|
280
|
+
* - `req.body.rows` (array) - Array of objects to validate
|
|
281
|
+
* - Each property config can specify validate, required, etc.
|
|
282
|
+
*
|
|
283
|
+
* **Output Properties:**
|
|
284
|
+
* - Calls next(error) if any row fails validation, next() if all pass
|
|
285
|
+
*
|
|
286
|
+
* @example
|
|
287
|
+
* ```typescript
|
|
288
|
+
* router.post('/entity', entity.validate, (req, res) => {
|
|
289
|
+
* // req.body.rows are now validated
|
|
290
|
+
* res.json({ success: true });
|
|
291
|
+
* });
|
|
292
|
+
* ```
|
|
293
|
+
*/
|
|
294
|
+
validate: (req: Request, _res: Response, next: NextFunction) => void;
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Checks, sanitizes, normalizes, and validates each row in req.body.rows according to property config and HTTP method.
|
|
298
|
+
*
|
|
299
|
+
* - Applies sanitization if `sanitize: true` and method matches
|
|
300
|
+
* - Applies normalization if `normalize: true` and method matches
|
|
301
|
+
* - Checks required properties and validates values
|
|
302
|
+
* - Calls next(error) on failure, next() on success
|
|
303
|
+
*
|
|
304
|
+
* @param {Request} req - Express request object containing rows
|
|
305
|
+
* @param {Response} _res - Express response object (not used)
|
|
306
|
+
* @param {NextFunction} next - Express next function
|
|
210
307
|
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
308
|
+
* @returns {void}
|
|
309
|
+
*
|
|
310
|
+
* **Input Properties Required:**
|
|
311
|
+
* - `req.body.rows` (array) - Array of objects to check
|
|
312
|
+
* - Each property config can specify sanitize, normalize, validate, required, etc.
|
|
313
|
+
*
|
|
314
|
+
* **Output Properties:**
|
|
315
|
+
* - Mutates `req.body.rows` with sanitized/normalized values
|
|
316
|
+
* - Calls next(error) if any row fails checks, next() if all pass
|
|
317
|
+
*
|
|
318
|
+
* @example
|
|
319
|
+
* ```typescript
|
|
320
|
+
* router.post('/entity', entity.check, (req, res) => {
|
|
321
|
+
* // req.body.rows are now sanitized, normalized, and validated
|
|
322
|
+
* res.json({ success: true });
|
|
323
|
+
* });
|
|
324
|
+
* ```
|
|
213
325
|
*/
|
|
214
|
-
|
|
326
|
+
check: (req: Request, _res: Response, next: NextFunction) => void;
|
|
215
327
|
}
|
|
216
328
|
|
|
217
329
|
```
|
|
@@ -219,6 +331,35 @@ normalize() and validate() methods are made to be used as Express.js middlewares
|
|
|
219
331
|
Each method will look for data to work on in the **req.body.rows** parameter.
|
|
220
332
|
|
|
221
333
|
|
|
334
|
+
### Password validation
|
|
335
|
+
|
|
336
|
+
Password validation will have the following options by default :
|
|
337
|
+
|
|
338
|
+
```javascript
|
|
339
|
+
const PWD_MIN_LENGTH = 9;
|
|
340
|
+
const PWD_MAX_LENGTH = 20;
|
|
341
|
+
const PWD_NUMBERS = true; // password must contain a number
|
|
342
|
+
const PWD_UPPERCASE = true; // password must contain an uppercase letter
|
|
343
|
+
const PWD_LOWERCASE = true; // password must contain a lowercase letter
|
|
344
|
+
const PWD_SYMBOLS = true; // password must contain at least one of the following symbol character : !@#%*_-+=:?><./()
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
#### Environment variables
|
|
348
|
+
|
|
349
|
+
You can update password default validator by setting the following environment variables :
|
|
350
|
+
|
|
351
|
+
```javascript
|
|
352
|
+
PWD_MIN_LENGTH_POLICY,
|
|
353
|
+
PWD_MAX_LENGTH_POLICY,
|
|
354
|
+
PWD_NUMBERS_POLICY,
|
|
355
|
+
PWD_UPPERCASE_POLICY,
|
|
356
|
+
PWD_LOWERCASE_POLICY,
|
|
357
|
+
PWD_SYMBOLS_POLICY
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Properties **min** and **max** of the password properties will override default and environement variable if set.
|
|
361
|
+
|
|
362
|
+
|
|
222
363
|
### Available options for a property
|
|
223
364
|
|
|
224
365
|
Any of these can be passed into the options object for each function.
|
package/dist/antity.d.ts
CHANGED
|
@@ -42,6 +42,7 @@ type Type =
|
|
|
42
42
|
"array" |
|
|
43
43
|
"jwt" |
|
|
44
44
|
"symbol" |
|
|
45
|
+
"password" |
|
|
45
46
|
"email" |
|
|
46
47
|
"regex" |
|
|
47
48
|
"json" |
|
|
@@ -69,13 +70,14 @@ declare class Entity {
|
|
|
69
70
|
getPropsByMethod(method: Method): Property[];
|
|
70
71
|
normalize: (req: Request, _res: Response, next: NextFunction) => void;
|
|
71
72
|
validate: (req: Request, _res: Response, next: NextFunction) => void;
|
|
73
|
+
check: (req: Request, _res: Response, next: NextFunction) => void;
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
declare class Property {
|
|
75
77
|
key: string;
|
|
76
78
|
type: Type;
|
|
77
|
-
min: number | Date;
|
|
78
|
-
max: number | Date;
|
|
79
|
+
min: number | Date | null;
|
|
80
|
+
max: number | Date | null;
|
|
79
81
|
required: boolean;
|
|
80
82
|
safe: boolean;
|
|
81
83
|
typeCheck: boolean;
|
|
@@ -89,8 +91,8 @@ declare class Property {
|
|
|
89
91
|
constructor(
|
|
90
92
|
key: string,
|
|
91
93
|
type: Type,
|
|
92
|
-
min: number | Date,
|
|
93
|
-
max: number | Date,
|
|
94
|
+
min: number | Date | null,
|
|
95
|
+
max: number | Date | null,
|
|
94
96
|
required: boolean,
|
|
95
97
|
safe: boolean,
|
|
96
98
|
typeCheck: boolean,
|
package/dist/antity.js
CHANGED
|
@@ -24,11 +24,18 @@ SOFTWARE.
|
|
|
24
24
|
https://github.com/DWTechs/Antity.js
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
|
-
import { isBoolean, isStringOfLength, isValidNumber, isValidInteger, isValidFloat, isEven, isOdd, isPositive, isNegative, isPowerOfTwo, isAscii, isArrayOfLength, isEmail, isRegex, isJson, isJWT, isSymbol, isIpAddress, isSlug, isHexadecimal, isValidDate, isValidTimestamp, isFunction, isHtmlElement, isHtmlEventAttribute, isNode, isObject, isString, isProperty, isArray, isIn, isDate, isNumber, isInteger, isNil } from '@dwtechs/checkard';
|
|
27
|
+
import { isBoolean, isStringOfLength, isValidNumber, isValidInteger, isValidFloat, isEven, isOdd, isPositive, isNegative, isPowerOfTwo, isAscii, isArrayOfLength, isValidPassword, isEmail, isRegex, isJson, isJWT, isSymbol, isIpAddress, isSlug, isHexadecimal, isValidDate, isValidTimestamp, isFunction, isHtmlElement, isHtmlEventAttribute, isNode, isObject, isString, isProperty, isArray, isIn, isDate, isNumber, isInteger, isNil } from '@dwtechs/checkard';
|
|
28
28
|
import { log } from '@dwtechs/winstan';
|
|
29
29
|
|
|
30
30
|
const Methods = ["GET", "PATCH", "PUT", "POST", "DELETE"];
|
|
31
31
|
|
|
32
|
+
const { PWD_MIN_LENGTH_POLICY, PWD_MAX_LENGTH_POLICY, PWD_NUMBERS_POLICY, PWD_UPPERCASE_POLICY, PWD_LOWERCASE_POLICY, PWD_SYMBOLS_POLICY } = process.env;
|
|
33
|
+
const PWD_MIN_LENGTH = PWD_MIN_LENGTH_POLICY ? +PWD_MIN_LENGTH_POLICY : 9;
|
|
34
|
+
const PWD_MAX_LENGTH = PWD_MAX_LENGTH_POLICY ? +PWD_MAX_LENGTH_POLICY : 20;
|
|
35
|
+
const PWD_NUMBERS = PWD_NUMBERS_POLICY ? true : false;
|
|
36
|
+
const PWD_UPPERCASE = PWD_UPPERCASE_POLICY ? true : false;
|
|
37
|
+
const PWD_LOWERCASE = PWD_LOWERCASE_POLICY ? true : false;
|
|
38
|
+
const PWD_SYMBOLS = PWD_SYMBOLS_POLICY ? true : false;
|
|
32
39
|
const Types = {
|
|
33
40
|
boolean: {
|
|
34
41
|
validate: (v, _min, _max, _typeCheck) => isBoolean(v)
|
|
@@ -66,6 +73,19 @@ const Types = {
|
|
|
66
73
|
array: {
|
|
67
74
|
validate: (v, min, max, _typeCheck) => isArrayOfLength(v, min || undefined, max || undefined)
|
|
68
75
|
},
|
|
76
|
+
password: {
|
|
77
|
+
validate: (v, min, max, _typeCheck) => {
|
|
78
|
+
const o = {
|
|
79
|
+
minLength: min || PWD_MIN_LENGTH,
|
|
80
|
+
maxLength: max || PWD_MAX_LENGTH,
|
|
81
|
+
lowerCase: PWD_LOWERCASE,
|
|
82
|
+
upperCase: PWD_UPPERCASE,
|
|
83
|
+
number: PWD_NUMBERS,
|
|
84
|
+
specialCharacter: PWD_SYMBOLS,
|
|
85
|
+
};
|
|
86
|
+
return isValidPassword(v, o);
|
|
87
|
+
}
|
|
88
|
+
},
|
|
69
89
|
email: {
|
|
70
90
|
validate: (v, _min, _max, _typeCheck) => isEmail(v)
|
|
71
91
|
},
|
|
@@ -185,12 +205,12 @@ function control(v, key, type, min, max, typeCheck, cb) {
|
|
|
185
205
|
c += ` and >= ${min}`;
|
|
186
206
|
if (!isNil(max))
|
|
187
207
|
c += ` and <= ${max}`;
|
|
188
|
-
return val ? null : {
|
|
208
|
+
return val ? null : { statusCode: 400, message: `Invalid ${key}, must be of type ${type}${c}` };
|
|
189
209
|
}
|
|
190
210
|
|
|
191
211
|
function require(v, key, type) {
|
|
192
212
|
log.debug(`require ${key}: ${type} = ${v}`);
|
|
193
|
-
return isNil(v) ? {
|
|
213
|
+
return isNil(v) ? { statusCode: 400, message: `Missing ${key} of type ${type}` } : null;
|
|
194
214
|
}
|
|
195
215
|
|
|
196
216
|
class Entity {
|
|
@@ -199,7 +219,7 @@ class Entity {
|
|
|
199
219
|
var _a;
|
|
200
220
|
const rows = (_a = req.body) === null || _a === void 0 ? void 0 : _a.rows;
|
|
201
221
|
if (!isArray(rows, "!0"))
|
|
202
|
-
return next({
|
|
222
|
+
return next({ statusCode: 400, message: "Normalize: no rows found in request body" });
|
|
203
223
|
for (const r of rows) {
|
|
204
224
|
for (const { key, type, sanitize: sanitize$1, normalize, sanitizer, normalizer, } of this._properties) {
|
|
205
225
|
let v = r[key];
|
|
@@ -223,11 +243,11 @@ class Entity {
|
|
|
223
243
|
const rows = (_a = req.body) === null || _a === void 0 ? void 0 : _a.rows;
|
|
224
244
|
const method = req.method;
|
|
225
245
|
if (!isArray(rows, "!0"))
|
|
226
|
-
return next({
|
|
246
|
+
return next({ statusCode: 400, message: "Validate: no rows found in request body" });
|
|
227
247
|
if (!isIn(Methods, method))
|
|
228
248
|
return next({
|
|
229
|
-
|
|
230
|
-
|
|
249
|
+
statusCode: 400,
|
|
250
|
+
message: `Invalid REST method. Received: ${method}. Must be one of: ${Methods.toString()}`
|
|
231
251
|
});
|
|
232
252
|
for (const r of rows) {
|
|
233
253
|
for (const { key, type, min, max, required, typeCheck, methods, validate, validator } of this._properties) {
|
|
@@ -248,6 +268,47 @@ class Entity {
|
|
|
248
268
|
}
|
|
249
269
|
next();
|
|
250
270
|
};
|
|
271
|
+
this.check = (req, _res, next) => {
|
|
272
|
+
var _a;
|
|
273
|
+
const rows = (_a = req.body) === null || _a === void 0 ? void 0 : _a.rows;
|
|
274
|
+
const method = req.method;
|
|
275
|
+
if (!isArray(rows, "!0"))
|
|
276
|
+
return next({ statusCode: 400, message: "Check: no rows found in request body" });
|
|
277
|
+
if (!isIn(Methods, method))
|
|
278
|
+
return next({
|
|
279
|
+
statusCode: 400,
|
|
280
|
+
message: `Invalid REST method. Received: ${method}. Must be one of: ${Methods.toString()}`
|
|
281
|
+
});
|
|
282
|
+
for (const r of rows) {
|
|
283
|
+
for (const { key, type, min, max, required, typeCheck, methods, validate, sanitize: sanitize$1, normalize, sanitizer, normalizer, validator } of this._properties) {
|
|
284
|
+
let v = r[key];
|
|
285
|
+
if (isIn(methods, method)) {
|
|
286
|
+
if (v) {
|
|
287
|
+
if (sanitize$1) {
|
|
288
|
+
log.debug(`sanitize ${key}: ${type} = ${v}`);
|
|
289
|
+
v = sanitize(v, sanitizer);
|
|
290
|
+
}
|
|
291
|
+
if (normalize && isFunction(normalizer)) {
|
|
292
|
+
log.debug(`normalize ${key}: ${type} = ${v}`);
|
|
293
|
+
v = normalizer(v);
|
|
294
|
+
}
|
|
295
|
+
r[key] = v;
|
|
296
|
+
if (validate) {
|
|
297
|
+
const ct = control(v, key, type, min, max, typeCheck, validator);
|
|
298
|
+
if (ct)
|
|
299
|
+
return next(ct);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (required) {
|
|
303
|
+
const rq = require(v, key, type);
|
|
304
|
+
if (rq)
|
|
305
|
+
return next(rq);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
next();
|
|
311
|
+
};
|
|
251
312
|
this._name = name;
|
|
252
313
|
this._properties = [];
|
|
253
314
|
this._unsafeProps = [];
|