@dwtechs/antity-pgsql 0.9.1 → 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 +78 -24
- package/dist/antity-pgsql.d.ts +6 -6
- package/dist/antity-pgsql.js +40 -19
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -62,11 +62,11 @@ const entity = new SQLEntity("consumers", [
|
|
|
62
62
|
type: "integer",
|
|
63
63
|
min: 0,
|
|
64
64
|
max: 120,
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
isTypeChecked: true,
|
|
66
|
+
isFilterable: true,
|
|
67
|
+
requiredFor: ["PUT"],
|
|
68
68
|
operations: ["SELECT", "UPDATE"],
|
|
69
|
-
|
|
69
|
+
isPrivate: false,
|
|
70
70
|
sanitizer: null,
|
|
71
71
|
normalizer: null,
|
|
72
72
|
validator: null,
|
|
@@ -76,11 +76,11 @@ const entity = new SQLEntity("consumers", [
|
|
|
76
76
|
type: "string",
|
|
77
77
|
min: 0,
|
|
78
78
|
max: 255,
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
isTypeChecked: true,
|
|
80
|
+
isFilterable: false,
|
|
81
|
+
requiredFor: ["POST", "PUT"],
|
|
82
82
|
operations: ["SELECT", "UPDATE"],
|
|
83
|
-
|
|
83
|
+
isPrivate: false,
|
|
84
84
|
sanitizer: null,
|
|
85
85
|
normalizer: normalizeName,
|
|
86
86
|
validator: null,
|
|
@@ -90,11 +90,11 @@ const entity = new SQLEntity("consumers", [
|
|
|
90
90
|
type: "string",
|
|
91
91
|
min: 0,
|
|
92
92
|
max: 255,
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
isTypeChecked: true,
|
|
94
|
+
isFilterable: false,
|
|
95
|
+
requiredFor: ["POST", "PUT"],
|
|
96
96
|
operations: ["SELECT", "UPDATE"],
|
|
97
|
-
|
|
97
|
+
isPrivate: false,
|
|
98
98
|
sanitizer: null,
|
|
99
99
|
normalizer: normalizeName,
|
|
100
100
|
validator: null,
|
|
@@ -104,11 +104,11 @@ const entity = new SQLEntity("consumers", [
|
|
|
104
104
|
type: "string",
|
|
105
105
|
min: 0,
|
|
106
106
|
max: 255,
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
isTypeChecked: true,
|
|
108
|
+
isFilterable: true,
|
|
109
|
+
requiredFor: ["POST", "PUT"],
|
|
110
110
|
operations: ["SELECT", "UPDATE"],
|
|
111
|
-
|
|
111
|
+
isPrivate: false,
|
|
112
112
|
sanitizer: null,
|
|
113
113
|
normalizer: normalizeNickname,
|
|
114
114
|
validator: null,
|
|
@@ -125,10 +125,10 @@ router.put("/", ...entity.updateArraySubstack);
|
|
|
125
125
|
router.post("/manual", entity.normalizeArray, entity.validateArray, ..., entity.add);
|
|
126
126
|
router.put("/manual", entity.normalizeArray, entity.validateArray, ..., entity.update);
|
|
127
127
|
|
|
128
|
-
router.
|
|
128
|
+
router.patch("/archive", ..., entity.archive);
|
|
129
129
|
router.delete("/", ..., entity.delete);
|
|
130
130
|
router.delete("/archived", ..., entity.deleteArchive);
|
|
131
|
-
router.get("/history", ..., entity.getHistory);
|
|
131
|
+
router.get("/:id/history", ..., entity.getHistory);
|
|
132
132
|
|
|
133
133
|
```
|
|
134
134
|
|
|
@@ -160,10 +160,14 @@ type MatchMode =
|
|
|
160
160
|
"st_dwithin";
|
|
161
161
|
|
|
162
162
|
|
|
163
|
+
type Filters = {
|
|
164
|
+
[key: string]: Filter | Filter[]; // Supports both simple (object) and complex (array) formats
|
|
165
|
+
}
|
|
166
|
+
|
|
163
167
|
type Filter = {
|
|
164
168
|
value: string | number | boolean | Date | number[];
|
|
165
|
-
subProps?: string[];
|
|
166
169
|
matchMode?: MatchMode;
|
|
170
|
+
operator?: string; // 'and' | 'or' - Used when multiple filters apply to the same property
|
|
167
171
|
}
|
|
168
172
|
|
|
169
173
|
type ExpressMiddleware = (req: Request, res: Response, next: NextFunction) => void;
|
|
@@ -175,7 +179,7 @@ class SQLEntity {
|
|
|
175
179
|
get name(): string;
|
|
176
180
|
get table(): string;
|
|
177
181
|
get schema(): string;
|
|
178
|
-
get
|
|
182
|
+
get privateProps(): string[];
|
|
179
183
|
get properties(): Property[];
|
|
180
184
|
set name(name: string);
|
|
181
185
|
set table(table: string);
|
|
@@ -278,6 +282,56 @@ Using substacks simplifies your route definitions and ensures consistent data pr
|
|
|
278
282
|
- **deleteArchive()**: Deletes archived rows that were archived before a specific date using a PostgreSQL SECURITY DEFINER function. Expects `req.body.date` to be a Date object.
|
|
279
283
|
- **getHistory()**: Retrieves modification history for rows from the `log.history` table. Expects `req.body.rows` to be an array of objects with `id` property. Returns all historical records for the specified entity IDs.
|
|
280
284
|
|
|
285
|
+
### Filters
|
|
286
|
+
|
|
287
|
+
Filters support two formats for maximum flexibility:
|
|
288
|
+
|
|
289
|
+
#### Simple Format (Single Filter per Property)
|
|
290
|
+
|
|
291
|
+
Backward-compatible format using a single filter object:
|
|
292
|
+
|
|
293
|
+
```javascript
|
|
294
|
+
const filters = {
|
|
295
|
+
name: { value: 'John', matchMode: 'contains' },
|
|
296
|
+
age: { value: 30, matchMode: 'equals' },
|
|
297
|
+
archived: { value: false, matchMode: 'equals' }
|
|
298
|
+
};
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
#### Complex Format (Multiple Filters per Property)
|
|
302
|
+
|
|
303
|
+
Array-based format supporting multiple filters with logical operators:
|
|
304
|
+
|
|
305
|
+
```javascript
|
|
306
|
+
const filters = {
|
|
307
|
+
// Multiple filters on the same property with OR operator
|
|
308
|
+
name: [
|
|
309
|
+
{ value: 'John', matchMode: 'contains', operator: 'or' },
|
|
310
|
+
{ value: 'Jane', matchMode: 'contains', operator: 'or' }
|
|
311
|
+
],
|
|
312
|
+
// Age range with AND operator
|
|
313
|
+
age: [
|
|
314
|
+
{ value: 18, matchMode: 'gte', operator: 'and' },
|
|
315
|
+
{ value: 65, matchMode: 'lte', operator: 'and' }
|
|
316
|
+
],
|
|
317
|
+
// Single filter in array format
|
|
318
|
+
archived: [{ value: false, matchMode: 'equals' }]
|
|
319
|
+
};
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
This generates SQL like:
|
|
323
|
+
```sql
|
|
324
|
+
WHERE (name LIKE '%John%' OR name LIKE '%Jane%')
|
|
325
|
+
AND (age >= 18 AND age <= 65)
|
|
326
|
+
AND archived = false
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
**Notes:**
|
|
330
|
+
- Both formats can be mixed in the same filters object
|
|
331
|
+
- When using arrays with a single filter, the operator is optional
|
|
332
|
+
- Default operator is 'AND' if not specified
|
|
333
|
+
- The operator field is case-insensitive
|
|
334
|
+
|
|
281
335
|
|
|
282
336
|
## Match modes
|
|
283
337
|
|
|
@@ -360,10 +414,10 @@ Any of these can be passed into the options object for each function.
|
|
|
360
414
|
| type | Type | Type of the property |
|
|
361
415
|
| min | number \| Date | Minimum value | 0 \| 1900-01-01
|
|
362
416
|
| max | number \| Date | Maximum value | 999999999 \| 2200-12-31
|
|
363
|
-
|
|
|
364
|
-
|
|
|
365
|
-
|
|
|
366
|
-
|
|
|
417
|
+
| requiredFor | Method[] | property is required for the listed methods only | ["PATCH", "PUT", "POST"]
|
|
418
|
+
| isPrivate | boolean | Property is unsafe to send in the response | true
|
|
419
|
+
| isTypeChecked | boolean | Type is checked during validation | false
|
|
420
|
+
| isFilterable | boolean | property is filterable in a SELECT operation | true
|
|
367
421
|
| operations | Operation[] | Property is used for the DML operations only | ["SELECT", "INSERT", "UPDATE"]
|
|
368
422
|
| sanitizer | ((v:any) => any) \| null | Custom sanitizer function if sanitize is true | null
|
|
369
423
|
| normalizer | ((v:any) => any) \| null | Custom Normalizer function if normalize is true | null
|
package/dist/antity-pgsql.d.ts
CHANGED
|
@@ -36,23 +36,23 @@ export type Filters = {
|
|
|
36
36
|
};
|
|
37
37
|
export type Filter = {
|
|
38
38
|
value: string | number | boolean | Date | number[];
|
|
39
|
-
subProps?: string[];
|
|
40
39
|
matchMode?: MatchMode;
|
|
40
|
+
operator?: string;
|
|
41
41
|
};
|
|
42
42
|
export type { Type };
|
|
43
43
|
|
|
44
44
|
export declare class Property extends BaseProperty {
|
|
45
|
-
|
|
45
|
+
isFilterable: boolean;
|
|
46
46
|
operations: Operation[];
|
|
47
47
|
constructor(
|
|
48
48
|
key: string,
|
|
49
49
|
type: Type,
|
|
50
50
|
min: number | Date | null,
|
|
51
51
|
max: number | Date | null,
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
requiredFor: Method[],
|
|
53
|
+
isPrivate: boolean,
|
|
54
|
+
isTypeChecked: boolean,
|
|
55
|
+
isFilterable: boolean,
|
|
56
56
|
operations: Operation[] | undefined,
|
|
57
57
|
sanitizer: ((v: unknown) => unknown) | null,
|
|
58
58
|
normalizer: ((v: unknown) => unknown) | null,
|
package/dist/antity-pgsql.js
CHANGED
|
@@ -160,20 +160,33 @@ function comparator(matchMode) {
|
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
function add(filters) {
|
|
163
|
+
var _a, _b;
|
|
163
164
|
const conditions = [];
|
|
164
165
|
const args = [];
|
|
165
166
|
if (filters) {
|
|
166
167
|
let i = 1;
|
|
167
168
|
for (const k in filters) {
|
|
168
|
-
const
|
|
169
|
-
const
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
169
|
+
const filterValue = filters[k];
|
|
170
|
+
const filterArray = isArray(filterValue) ? filterValue : [filterValue];
|
|
171
|
+
const groupConditions = [];
|
|
172
|
+
for (const filter of filterArray) {
|
|
173
|
+
const { value, matchMode } = filter;
|
|
174
|
+
const indexes = isArray(value) ? value.map(() => i++) : [i++];
|
|
175
|
+
const cond = addOne(k, indexes, matchMode);
|
|
176
|
+
if (cond) {
|
|
177
|
+
groupConditions.push(cond);
|
|
178
|
+
if (isArray(value))
|
|
179
|
+
args.push(...value);
|
|
180
|
+
else
|
|
181
|
+
args.push(value);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (groupConditions.length > 0) {
|
|
185
|
+
const operator = ((_b = (_a = filterArray[0]) === null || _a === void 0 ? void 0 : _a.operator) === null || _b === void 0 ? void 0 : _b.toUpperCase()) || 'AND';
|
|
186
|
+
const combined = groupConditions.length > 1
|
|
187
|
+
? `(${groupConditions.join(` ${operator} `)})`
|
|
188
|
+
: groupConditions[0];
|
|
189
|
+
conditions.push(combined);
|
|
177
190
|
}
|
|
178
191
|
}
|
|
179
192
|
}
|
|
@@ -459,18 +472,26 @@ function cleanFilters(filters, properties) {
|
|
|
459
472
|
delete filters[k];
|
|
460
473
|
continue;
|
|
461
474
|
}
|
|
462
|
-
if (!prop.
|
|
475
|
+
if (!prop.isFilterable) {
|
|
463
476
|
log.warn(`${LOGS_PREFIX}Filters: skipping unfilterable property: ${k}`);
|
|
464
477
|
delete filters[k];
|
|
465
478
|
continue;
|
|
466
479
|
}
|
|
467
480
|
const type$1 = type(prop.type);
|
|
468
|
-
const
|
|
469
|
-
|
|
470
|
-
|
|
481
|
+
const filterValue = filters[k];
|
|
482
|
+
const filterArray = isArray(filterValue) ? filterValue : [filterValue];
|
|
483
|
+
const validFilters = filterArray.filter((f) => {
|
|
484
|
+
const { matchMode: matchMode$1 } = f;
|
|
485
|
+
if (!matchMode$1 || !matchMode(type$1, matchMode$1)) {
|
|
486
|
+
log.warn(`${LOGS_PREFIX}Filters: skipping invalid match mode: "${matchMode$1}" for type: "${type$1}" at property: "${k}"`);
|
|
487
|
+
return false;
|
|
488
|
+
}
|
|
489
|
+
return true;
|
|
490
|
+
});
|
|
491
|
+
if (!validFilters.length)
|
|
471
492
|
delete filters[k];
|
|
472
|
-
|
|
473
|
-
|
|
493
|
+
else
|
|
494
|
+
filters[k] = validFilters;
|
|
474
495
|
}
|
|
475
496
|
}
|
|
476
497
|
return filters;
|
|
@@ -497,10 +518,10 @@ function generateSummary(name, table, properties) {
|
|
|
497
518
|
lines.push(`│ │ ├─ Type: ${p.type}`);
|
|
498
519
|
lines.push(`│ │ ├─ Min: ${p.min}`);
|
|
499
520
|
lines.push(`│ │ ├─ Max: ${p.max}`);
|
|
500
|
-
lines.push(`│ │ ├─
|
|
501
|
-
lines.push(`│ │ ├─
|
|
502
|
-
lines.push(`│ │ ├─
|
|
503
|
-
lines.push(`│ │ ├─
|
|
521
|
+
lines.push(`│ │ ├─ RequiredFor: ${p.requiredFor}`);
|
|
522
|
+
lines.push(`│ │ ├─ IsPrivate: ${p.isPrivate}`);
|
|
523
|
+
lines.push(`│ │ ├─ IsTypeChecked: ${p.isTypeChecked}`);
|
|
524
|
+
lines.push(`│ │ ├─ IsFilterable: ${p.isFilterable}`);
|
|
504
525
|
lines.push(`│ │ ├─ Operations: [${p.operations.join(', ')}]`);
|
|
505
526
|
lines.push(`│ │ ├─ Sanitize: ${p.sanitize}`);
|
|
506
527
|
lines.push(`│ │ ├─ Normalize: ${p.normalize}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dwtechs/antity-pgsql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Open source library to add PostgreSQL support to @dwtechs/Antity entities.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"entities"
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@dwtechs/checkard": "3.6.0",
|
|
40
40
|
"@dwtechs/winstan": "0.5.0",
|
|
41
|
-
"@dwtechs/antity": "0.
|
|
41
|
+
"@dwtechs/antity": "0.16.0",
|
|
42
42
|
"@dwtechs/sparray": "0.2.1",
|
|
43
43
|
"pg": "8.13.1"
|
|
44
44
|
},
|