@dwtechs/antity-pgsql 0.15.2 → 0.17.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 +48 -40
- package/dist/antity-pgsql.d.ts +21 -15
- package/dist/antity-pgsql.js +65 -62
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -22,24 +22,14 @@
|
|
|
22
22
|
- 🚚 Shipped as EcmaScrypt module
|
|
23
23
|
- 📝 Written in Typescript
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
## Support
|
|
27
|
-
|
|
28
|
-
- node: 22
|
|
29
|
-
|
|
30
|
-
This is the oldest targeted versions. The library should work properly on older versions of Node.js but we do not support it officially.
|
|
31
|
-
|
|
32
|
-
|
|
33
25
|
## Installation
|
|
34
26
|
|
|
35
27
|
```bash
|
|
36
28
|
$ npm i @dwtechs/antity-pgsql
|
|
37
29
|
```
|
|
38
30
|
|
|
39
|
-
|
|
40
31
|
## Usage
|
|
41
32
|
|
|
42
|
-
|
|
43
33
|
```javascript
|
|
44
34
|
|
|
45
35
|
import { SQLEntity } from "@dwtechs/antity-pgsql";
|
|
@@ -140,6 +130,8 @@ router.get("/:id/history", ..., entity.getHistory);
|
|
|
140
130
|
|
|
141
131
|
type Operation = "SELECT" | "INSERT" | "UPDATE";
|
|
142
132
|
|
|
133
|
+
type Row = Record<string, string | number | boolean | Date | number[]>;
|
|
134
|
+
|
|
143
135
|
type MatchMode =
|
|
144
136
|
"startsWith" |
|
|
145
137
|
"endsWith" |
|
|
@@ -149,6 +141,7 @@ type MatchMode =
|
|
|
149
141
|
"notEquals" |
|
|
150
142
|
"between" |
|
|
151
143
|
"in" |
|
|
144
|
+
"notIn" |
|
|
152
145
|
"lt" |
|
|
153
146
|
"lte" |
|
|
154
147
|
"gt" |
|
|
@@ -171,6 +164,21 @@ type Filter = {
|
|
|
171
164
|
operator?: string; // 'and' | 'or' - Used when multiple filters apply to the same property
|
|
172
165
|
}
|
|
173
166
|
|
|
167
|
+
type PGClient = {
|
|
168
|
+
query(text: string, values?: unknown[]): Promise<PGResponse>;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
type PGResponse = {
|
|
172
|
+
rows: Record<string, unknown>[];
|
|
173
|
+
rowCount: number | null;
|
|
174
|
+
total?: number;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
type SelectResponse = {
|
|
178
|
+
rows: Record<string, unknown>[];
|
|
179
|
+
total?: number;
|
|
180
|
+
};
|
|
181
|
+
|
|
174
182
|
type ExpressMiddleware = (req: Request, res: Response, next: NextFunction) => void;
|
|
175
183
|
type ExpressMiddlewareAsync = (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
176
184
|
type SubstackTuple = [ExpressMiddleware, ExpressMiddleware, ExpressMiddlewareAsync];
|
|
@@ -206,32 +214,28 @@ class SQLEntity {
|
|
|
206
214
|
args: (Filter["value"])[];
|
|
207
215
|
};
|
|
208
216
|
update: (
|
|
209
|
-
rows:
|
|
210
|
-
|
|
211
|
-
consumerName?: string) => {
|
|
217
|
+
rows: Row[],
|
|
218
|
+
consumer?: { id?: number | string, nickname?: string }) => {
|
|
212
219
|
query: string;
|
|
213
220
|
args: unknown[];
|
|
214
221
|
};
|
|
215
222
|
archive: (
|
|
216
|
-
rows:
|
|
217
|
-
|
|
218
|
-
consumerName?: string) => {
|
|
223
|
+
rows: Row[],
|
|
224
|
+
consumer?: { id?: number | string, nickname?: string }) => {
|
|
219
225
|
query: string;
|
|
220
226
|
args: unknown[];
|
|
221
227
|
};
|
|
222
228
|
insert: (
|
|
223
|
-
rows:
|
|
224
|
-
|
|
225
|
-
consumerName?: string,
|
|
229
|
+
rows: Row[],
|
|
230
|
+
consumer?: { id?: number | string, nickname?: string },
|
|
226
231
|
rtn?: string) => {
|
|
227
232
|
query: string;
|
|
228
233
|
args: unknown[];
|
|
229
234
|
};
|
|
230
235
|
upsert: (
|
|
231
|
-
rows:
|
|
236
|
+
rows: Row[],
|
|
232
237
|
conflictTarget: string | string[],
|
|
233
|
-
|
|
234
|
-
consumerName?: string,
|
|
238
|
+
consumer?: { id?: number | string, nickname?: string },
|
|
235
239
|
rtn?: string) => {
|
|
236
240
|
query: string;
|
|
237
241
|
args: unknown[];
|
|
@@ -266,7 +270,7 @@ function filter(
|
|
|
266
270
|
function execute(
|
|
267
271
|
query: string,
|
|
268
272
|
args: (string | number | boolean | Date | number[])[],
|
|
269
|
-
client:
|
|
273
|
+
client: PGClient | null,
|
|
270
274
|
): Promise<PGResponse>;
|
|
271
275
|
|
|
272
276
|
|
|
@@ -307,10 +311,10 @@ Using substacks simplifies your route definitions and ensures consistent data pr
|
|
|
307
311
|
### Query Methods
|
|
308
312
|
|
|
309
313
|
- **query.select()**: Generates a SELECT query. When the `rows` parameter is provided (not null), pagination is automatically enabled and the query includes `COUNT(*) OVER () AS total` to return the total number of rows. The total count is extracted from results and returned separately from the row data.
|
|
310
|
-
- **query.insert()**: Generates an INSERT query. Accepts an array of objects with properties matching the entity definition. Optionally appends `
|
|
311
|
-
- **query.update()**: Generates an UPDATE query using CASE statements. Accepts an array of objects with `id` property. Optionally appends `
|
|
312
|
-
- **query.upsert()**: Generates an INSERT ... ON CONFLICT ... DO UPDATE query. (See [Upsert](#upsert-insert-or-update) section below.) Accepts an array of objects and a `conflictTarget` (single column name or array of column names) that defines uniqueness. If a conflict occurs on the specified column(s), the row is updated; otherwise, it is inserted. Properties are automatically included if they have both INSERT and UPDATE operations. Optionally appends `
|
|
313
|
-
- **query.archive()**: Generates a simplified `UPDATE ... SET archived = true WHERE id IN (...)` query. Accepts an array of objects with `id` property. Optionally appends `
|
|
314
|
+
- **query.insert()**: Generates an INSERT query. Accepts an array of `Row` objects with properties matching the entity definition. Consumer fields are appended directly to the query arguments — row objects are **not mutated**. Optionally appends `consumer.id` as `creatorid` and `consumer.nickname` as `name` for audit tracking. Supports `RETURNING` clause via the `rtn` parameter.
|
|
315
|
+
- **query.update()**: Generates an UPDATE query using CASE statements. Accepts an array of `Row` objects with `id` property. Optionally appends `consumer.id` as `updaterid` and `consumer.nickname` as `name` for audit tracking.
|
|
316
|
+
- **query.upsert()**: Generates an INSERT ... ON CONFLICT ... DO UPDATE query. (See [Upsert](#upsert-insert-or-update) section below.) Accepts an array of `Row` objects and a `conflictTarget` (single column name or array of column names) that defines uniqueness. If a conflict occurs on the specified column(s), the row is updated; otherwise, it is inserted. Properties are automatically included if they have both INSERT and UPDATE operations. Consumer fields are appended directly to the query arguments — row objects are **not mutated**. Optionally appends `consumer.id` as `creatorid` and `consumer.nickname` as `name` for audit tracking. Supports `RETURNING` clause via the `rtn` parameter.
|
|
317
|
+
- **query.archive()**: Generates a simplified `UPDATE ... SET archived = true WHERE id IN (...)` query. Accepts an array of `Row` objects with `id` property. Optionally appends `consumer.id` as `updaterid` and `consumer.nickname` as `name` for audit tracking. Does not require an `archived` field in the rows — it is set directly in the SQL.
|
|
314
318
|
- **sync()**: Atomically synchronises the table with the provided rows inside a single PostgreSQL transaction. Missing rows are inserted, existing rows are updated, and rows absent from the list are deleted. Accepts optional `idField` (default `'id'`) and `filters` to restrict the scope of managed rows. Stores the result in `res.locals.rows` and a summary `{ inserted, updated, deleted }` in `res.locals.sync`.
|
|
315
319
|
- **delete()**: Deletes rows by their IDs. Expects `req.body.rows` to be an array of objects with `id` property: `[{id: 1}, {id: 2}]`
|
|
316
320
|
- **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.
|
|
@@ -372,7 +376,7 @@ res.locals.sync // { inserted: 1, updated: 1, deleted: 1 }
|
|
|
372
376
|
- **Atomic**: All insert / update / delete operations are wrapped in a single transaction.
|
|
373
377
|
- **Filter scope**: When `filters` are provided, only rows matching the filter are considered "managed". Rows outside the filter are never touched.
|
|
374
378
|
- **Property selection**: Insert uses `INSERT` properties; update uses `UPDATE` properties — same as the standalone `add` and `update` middlewares.
|
|
375
|
-
- **Consumer tracking**: `
|
|
379
|
+
- **Consumer tracking**: `consumer.id` and `consumer.nickname` from `res.locals.consumer` are forwarded to inserts as `creatorid`/`name` and to updates as `updaterid`/`name` for audit tracking.
|
|
376
380
|
|
|
377
381
|
### Upsert (Insert or Update)
|
|
378
382
|
|
|
@@ -432,18 +436,17 @@ router.post('/users/upsert', ...entity.upsertArraySubstack);
|
|
|
432
436
|
const { query, args } = entity.query.upsert(
|
|
433
437
|
[{ id: 1, name: 'John', email: 'john@example.com' }],
|
|
434
438
|
'id',
|
|
435
|
-
1, //
|
|
436
|
-
'admin', // consumerName (optional)
|
|
439
|
+
{ id: 1, nickname: 'admin' }, // consumer (optional)
|
|
437
440
|
'RETURNING id' // return clause (optional)
|
|
438
441
|
);
|
|
439
442
|
// Generates:
|
|
440
|
-
// INSERT INTO public.users (name, email,
|
|
443
|
+
// INSERT INTO public.users (name, email, creatorid, name)
|
|
441
444
|
// VALUES ($1, $2, $3, $4)
|
|
442
445
|
// ON CONFLICT (id) DO UPDATE SET
|
|
443
446
|
// name = EXCLUDED.name,
|
|
444
447
|
// email = EXCLUDED.email,
|
|
445
|
-
//
|
|
446
|
-
//
|
|
448
|
+
// creatorid = EXCLUDED.creatorid,
|
|
449
|
+
// name = EXCLUDED.name
|
|
447
450
|
// RETURNING id
|
|
448
451
|
```
|
|
449
452
|
|
|
@@ -538,7 +541,8 @@ List of possible match modes :
|
|
|
538
541
|
| notContains | | string | Whether the value does not contain filter value |
|
|
539
542
|
| equals | | string \| number | Whether the value equals the filter value |
|
|
540
543
|
| notEquals | | string \| number | Whether the value does not equal the filter value |
|
|
541
|
-
| in | | string[] \| number[] | Whether the value
|
|
544
|
+
| in | | string[] \| number[] | Whether the value is included in the list |
|
|
545
|
+
| notIn | | string[] \| number[] | Whether the value is not included in the list |
|
|
542
546
|
| lt | | string \| number | Whether the value is less than the filter value |
|
|
543
547
|
| lte | | string \| number | Whether the value is less than or equals to the filter value |
|
|
544
548
|
| gt | | string \| number | Whether the value is greater than the filter value |
|
|
@@ -557,8 +561,8 @@ List of compatible match modes for each property types
|
|
|
557
561
|
|
|
558
562
|
| Name | Match modes |
|
|
559
563
|
| :---------- | :---------------------- |
|
|
560
|
-
| string | startsWith,<br>contains,<br>endsWith,<br>notContains,<br>equals,<br>notEquals,<br>lt,<br>lte,<br>gt,<br>gte |
|
|
561
|
-
| number | equals,<br>notEquals,<br>lt,<br>lte,<br>gt,<br>gte |
|
|
564
|
+
| string | startsWith,<br>contains,<br>endsWith,<br>notContains,<br>equals,<br>notEquals,<br>in,<br>notIn,<br>lt,<br>lte,<br>gt,<br>gte |
|
|
565
|
+
| number | equals,<br>notEquals,<br>in,<br>notIn,<br>lt,<br>lte,<br>gt,<br>gte |
|
|
562
566
|
| date | is,<br>isNot,<br>before,<br>after |
|
|
563
567
|
| boolean | is,<br>isNot |
|
|
564
568
|
| string[] | in |
|
|
@@ -612,21 +616,25 @@ Any of these can be passed into the options object for each function.
|
|
|
612
616
|
| isTypeChecked | boolean | Type is checked during validation | false
|
|
613
617
|
| isFilterable | boolean | property is filterable in a SELECT operation | true
|
|
614
618
|
| operations | Operation[] | Property is used for the DML operations only | ["SELECT", "INSERT", "UPDATE"]
|
|
615
|
-
| sanitizer | ((v:
|
|
616
|
-
| normalizer | ((v:
|
|
617
|
-
| validator | ((v:
|
|
619
|
+
| sanitizer | ((v: unknown) => unknown) \| null | Custom sanitizer function if sanitize is true | null
|
|
620
|
+
| normalizer | ((v: unknown) => unknown) \| null | Custom Normalizer function if normalize is true | null
|
|
621
|
+
| validator | ((v: unknown) => unknown) \| null | validator function if validate is true | null
|
|
618
622
|
|
|
619
623
|
|
|
620
624
|
* *Min and max parameters are not used for boolean type*
|
|
621
625
|
* *TypeCheck Parameter is not used for boolean, string and array types*
|
|
622
626
|
|
|
627
|
+
## Support
|
|
628
|
+
|
|
629
|
+
| Environment | Version |
|
|
630
|
+
| :---------- | :-----: |
|
|
631
|
+
| Node.js | >= 22 |
|
|
623
632
|
|
|
624
633
|
## Contributors
|
|
625
634
|
|
|
626
635
|
Antity.js is still in development and we would be glad to get all the help you can provide.
|
|
627
636
|
To contribute please read **[contributor.md](https://github.com/DWTechs/Antity.js/blob/main/contributor.md)** for detailed installation guide.
|
|
628
637
|
|
|
629
|
-
|
|
630
638
|
## Stack
|
|
631
639
|
|
|
632
640
|
| Purpose | Choice | Motivation |
|
package/dist/antity-pgsql.d.ts
CHANGED
|
@@ -27,7 +27,6 @@ https://github.com/DWTechs/Antity-pgsql.js
|
|
|
27
27
|
import { Entity, Property as BaseProperty } from "@dwtechs/antity";
|
|
28
28
|
import type { Type, Method } from "@dwtechs/antity";
|
|
29
29
|
import type { Request, Response, NextFunction } from 'express';
|
|
30
|
-
import type { Pool, PoolClient } from 'pg';
|
|
31
30
|
|
|
32
31
|
export type Operation = "SELECT" | "INSERT" | "UPDATE";
|
|
33
32
|
export type Sort = "ASC" | "DESC";
|
|
@@ -75,9 +74,20 @@ export type Geometry = {
|
|
|
75
74
|
maxLat: number;
|
|
76
75
|
};
|
|
77
76
|
};
|
|
77
|
+
export type Row = Record<string, Filter["value"]>;
|
|
78
|
+
|
|
79
|
+
export type PGClient = {
|
|
80
|
+
query(text: string, values?: unknown[]): Promise<PGResponse>;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export type SelectResponse = {
|
|
84
|
+
rows: Record<string, unknown>[];
|
|
85
|
+
total?: number;
|
|
86
|
+
};
|
|
87
|
+
|
|
78
88
|
export type PGResponse = {
|
|
79
89
|
rows: Record<string, unknown>[];
|
|
80
|
-
rowCount: number;
|
|
90
|
+
rowCount: number | null;
|
|
81
91
|
total?: number;
|
|
82
92
|
command?: string;
|
|
83
93
|
oid?: number;
|
|
@@ -132,27 +142,24 @@ export declare class SQLEntity extends Entity {
|
|
|
132
142
|
};
|
|
133
143
|
|
|
134
144
|
update: (
|
|
135
|
-
rows:
|
|
136
|
-
|
|
137
|
-
consumerName?: string
|
|
145
|
+
rows: Row[],
|
|
146
|
+
consumer?: { id?: number | string, nickname?: string }
|
|
138
147
|
) => {
|
|
139
148
|
query: string;
|
|
140
149
|
args: unknown[];
|
|
141
150
|
};
|
|
142
151
|
|
|
143
152
|
archive: (
|
|
144
|
-
rows:
|
|
145
|
-
|
|
146
|
-
consumerName?: string
|
|
153
|
+
rows: Row[],
|
|
154
|
+
consumer?: { id?: number | string, nickname?: string }
|
|
147
155
|
) => {
|
|
148
156
|
query: string;
|
|
149
157
|
args: unknown[];
|
|
150
158
|
};
|
|
151
159
|
|
|
152
160
|
insert: (
|
|
153
|
-
rows:
|
|
154
|
-
|
|
155
|
-
consumerName?: string,
|
|
161
|
+
rows: Row[],
|
|
162
|
+
consumer?: { id?: number | string, nickname?: string },
|
|
156
163
|
rtn?: string
|
|
157
164
|
) => {
|
|
158
165
|
query: string;
|
|
@@ -160,10 +167,9 @@ export declare class SQLEntity extends Entity {
|
|
|
160
167
|
};
|
|
161
168
|
|
|
162
169
|
upsert: (
|
|
163
|
-
rows:
|
|
170
|
+
rows: Row[],
|
|
164
171
|
conflictTarget: string | string[],
|
|
165
|
-
|
|
166
|
-
consumerName?: string,
|
|
172
|
+
consumer?: { id?: number | string, nickname?: string },
|
|
167
173
|
rtn?: string
|
|
168
174
|
) => {
|
|
169
175
|
query: string;
|
|
@@ -202,6 +208,6 @@ export declare function filter(
|
|
|
202
208
|
export declare function execute(
|
|
203
209
|
query: string,
|
|
204
210
|
args: (string | number | boolean | Date | number[])[],
|
|
205
|
-
client:
|
|
211
|
+
client: PGClient | null
|
|
206
212
|
): Promise<PGResponse>;
|
|
207
213
|
|
package/dist/antity-pgsql.js
CHANGED
|
@@ -43,15 +43,15 @@ var pool = new Pool({
|
|
|
43
43
|
const LOGS_PREFIX = '[Antity-PGSQL] ';
|
|
44
44
|
|
|
45
45
|
function start(query, args) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
log.debug(() => {
|
|
47
|
+
const a = JSON.stringify(args);
|
|
48
|
+
const q = query.replace(/[\n\r]+/g, "").replace(/\s{2,}/g, " ");
|
|
49
|
+
return `${LOGS_PREFIX}Pgsql: { Query : '${q}', Args : '${a}' }`;
|
|
50
|
+
});
|
|
49
51
|
return Date.now();
|
|
50
52
|
}
|
|
51
53
|
function end(res, time) {
|
|
52
|
-
|
|
53
|
-
const t = Date.now() - time;
|
|
54
|
-
log.debug(`Pgsql response in ${t}ms : ${r}`);
|
|
54
|
+
log.debug(() => `Pgsql response in ${Date.now() - time}ms : ${JSON.stringify(res)}`);
|
|
55
55
|
}
|
|
56
56
|
var perf = {
|
|
57
57
|
start,
|
|
@@ -305,19 +305,20 @@ class Insert {
|
|
|
305
305
|
if (consumerId !== undefined && consumerName !== undefined) {
|
|
306
306
|
propsToUse.push("consumerId", "consumerName");
|
|
307
307
|
nbProps += 2;
|
|
308
|
-
cols += `,
|
|
308
|
+
cols += `, creatorid, name`;
|
|
309
309
|
}
|
|
310
310
|
let query = `INSERT INTO ${quoteIfUppercase(schema)}.${quoteIfUppercase(table)} (${cols}) VALUES `;
|
|
311
311
|
const args = [];
|
|
312
312
|
let i = 0;
|
|
313
313
|
for (const row of rows) {
|
|
314
|
-
if (consumerId !== undefined && consumerName !== undefined) {
|
|
315
|
-
row.consumerId = consumerId;
|
|
316
|
-
row.consumerName = consumerName;
|
|
317
|
-
}
|
|
318
314
|
query += `${$i(nbProps, i)}, `;
|
|
319
315
|
for (const prop of propsToUse) {
|
|
320
|
-
|
|
316
|
+
if (prop === "consumerId")
|
|
317
|
+
args.push(consumerId);
|
|
318
|
+
else if (prop === "consumerName")
|
|
319
|
+
args.push(consumerName);
|
|
320
|
+
else
|
|
321
|
+
args.push(row[prop]);
|
|
321
322
|
}
|
|
322
323
|
i += nbProps;
|
|
323
324
|
}
|
|
@@ -356,7 +357,7 @@ class Update {
|
|
|
356
357
|
const propsToUse = [...this._props];
|
|
357
358
|
if (consumerId !== undefined && consumerName !== undefined)
|
|
358
359
|
propsToUse.push("consumerId", "consumerName");
|
|
359
|
-
log.debug(`${LOGS_PREFIX}Update query input rows: ${JSON.stringify(rows, null, 2)}`);
|
|
360
|
+
log.debug(() => `${LOGS_PREFIX}Update query input rows: ${JSON.stringify(rows, null, 2)}`);
|
|
360
361
|
const l = rows.length;
|
|
361
362
|
const args = rows.map(row => row.id);
|
|
362
363
|
let query = `UPDATE ${quoteIfUppercase(schema)}.${quoteIfUppercase(table)} SET `;
|
|
@@ -364,13 +365,14 @@ class Update {
|
|
|
364
365
|
for (const p of propsToUse) {
|
|
365
366
|
if (rows[0][p] === undefined)
|
|
366
367
|
continue;
|
|
367
|
-
|
|
368
|
+
const colName = p === "consumerId" ? "updaterid" : p === "consumerName" ? "name" : quoteIfUppercase(p);
|
|
369
|
+
query += `${colName} = CASE `;
|
|
368
370
|
for (let j = 0; j < l; j++) {
|
|
369
371
|
const row = rows[j];
|
|
370
372
|
query += `WHEN id = $${j + 1} THEN $${i++} `;
|
|
371
373
|
args.push(row[p]);
|
|
372
374
|
}
|
|
373
|
-
query += `ELSE ${
|
|
375
|
+
query += `ELSE ${colName} END, `;
|
|
374
376
|
}
|
|
375
377
|
query = `${query.slice(0, -2)} WHERE id IN ${$i(l, 0)}`;
|
|
376
378
|
return { query, args };
|
|
@@ -411,9 +413,9 @@ class Upsert {
|
|
|
411
413
|
let cols = this._cols;
|
|
412
414
|
if (consumerId !== undefined && consumerName !== undefined) {
|
|
413
415
|
propsToUse.push("consumerId", "consumerName");
|
|
414
|
-
quotedPropsToUse.push(`
|
|
416
|
+
quotedPropsToUse.push(`creatorid`, `name`);
|
|
415
417
|
nbProps += 2;
|
|
416
|
-
cols += `,
|
|
418
|
+
cols += `, creatorid, name`;
|
|
417
419
|
}
|
|
418
420
|
const conflictColumns = Array.isArray(conflictTarget)
|
|
419
421
|
? conflictTarget.map(col => quoteIfUppercase(col)).join(", ")
|
|
@@ -422,14 +424,13 @@ class Upsert {
|
|
|
422
424
|
const args = [];
|
|
423
425
|
let i = 0;
|
|
424
426
|
for (const row of rows) {
|
|
425
|
-
if (consumerId !== undefined && consumerName !== undefined) {
|
|
426
|
-
row.consumerId = consumerId;
|
|
427
|
-
row.consumerName = consumerName;
|
|
428
|
-
}
|
|
429
427
|
query += `${$i(nbProps, i)}, `;
|
|
430
|
-
for (const prop of
|
|
428
|
+
for (const prop of this._props) {
|
|
431
429
|
args.push(row[prop]);
|
|
432
430
|
}
|
|
431
|
+
if (consumerId !== undefined && consumerName !== undefined) {
|
|
432
|
+
args.push(consumerId, consumerName);
|
|
433
|
+
}
|
|
433
434
|
i += nbProps;
|
|
434
435
|
}
|
|
435
436
|
query = query.slice(0, -2);
|
|
@@ -466,12 +467,12 @@ var __awaiter$2 = (undefined && undefined.__awaiter) || function (thisArg, _argu
|
|
|
466
467
|
};
|
|
467
468
|
class Archive {
|
|
468
469
|
query(schema, table, rows, consumerId, consumerName) {
|
|
469
|
-
log.debug(`${LOGS_PREFIX}Archive query input rows: ${JSON.stringify(rows, null, 2)}`);
|
|
470
|
+
log.debug(() => `${LOGS_PREFIX}Archive query input rows: ${JSON.stringify(rows, null, 2)}`);
|
|
470
471
|
const l = rows.length;
|
|
471
472
|
const args = rows.map(row => row.id);
|
|
472
473
|
let query = `UPDATE ${quoteIfUppercase(schema)}.${quoteIfUppercase(table)} SET archived = true`;
|
|
473
474
|
if (consumerId !== undefined && consumerName !== undefined) {
|
|
474
|
-
query += `,
|
|
475
|
+
query += `, updaterid = $${l + 1}, name = $${l + 2}`;
|
|
475
476
|
args.push(consumerId, consumerName);
|
|
476
477
|
}
|
|
477
478
|
query += ` WHERE id IN ${$i(l, 0)}`;
|
|
@@ -587,12 +588,12 @@ function cleanFilters(filters, properties) {
|
|
|
587
588
|
if (filters.hasOwnProperty(k)) {
|
|
588
589
|
const prop = properties.find(p => p.key === k);
|
|
589
590
|
if (!prop) {
|
|
590
|
-
log.warn(`${LOGS_PREFIX}Filters: skipping unknown property: ${k}`);
|
|
591
|
+
log.warn(() => `${LOGS_PREFIX}Filters: skipping unknown property: ${k}`);
|
|
591
592
|
delete filters[k];
|
|
592
593
|
continue;
|
|
593
594
|
}
|
|
594
595
|
if (!prop.isFilterable) {
|
|
595
|
-
log.warn(`${LOGS_PREFIX}Filters: skipping unfilterable property: ${k}`);
|
|
596
|
+
log.warn(() => `${LOGS_PREFIX}Filters: skipping unfilterable property: ${k}`);
|
|
596
597
|
delete filters[k];
|
|
597
598
|
continue;
|
|
598
599
|
}
|
|
@@ -602,7 +603,7 @@ function cleanFilters(filters, properties) {
|
|
|
602
603
|
const validFilters = filterArray.filter((f) => {
|
|
603
604
|
const { matchMode: matchMode$1 } = f;
|
|
604
605
|
if (!matchMode$1 || !matchMode(type$1, matchMode$1)) {
|
|
605
|
-
log.warn(`${LOGS_PREFIX}Filters: skipping invalid match mode: "${matchMode$1}" for type: "${type$1}" at property: "${k}"`);
|
|
606
|
+
log.warn(() => `${LOGS_PREFIX}Filters: skipping invalid match mode: "${matchMode$1}" for type: "${type$1}" at property: "${k}"`);
|
|
606
607
|
return false;
|
|
607
608
|
}
|
|
608
609
|
return true;
|
|
@@ -617,9 +618,8 @@ function cleanFilters(filters, properties) {
|
|
|
617
618
|
}
|
|
618
619
|
|
|
619
620
|
function logSummary(name, table, properties) {
|
|
620
|
-
const summary = generateSummary(name, table, properties);
|
|
621
621
|
log.info(`${LOGS_PREFIX}Entity "${name}" created successfully`);
|
|
622
|
-
log.info(`${LOGS_PREFIX}Entity Summary:\n${
|
|
622
|
+
log.info(() => `${LOGS_PREFIX}Entity Summary:\n${generateSummary(name, table, properties)}`);
|
|
623
623
|
}
|
|
624
624
|
function generateSummary(name, table, properties) {
|
|
625
625
|
const lines = [];
|
|
@@ -702,20 +702,20 @@ class SQLEntity extends Entity {
|
|
|
702
702
|
select: (first = 0, rows = null, sortField = null, sortOrder = null, filters = null) => {
|
|
703
703
|
return this.sel.query(this.schema, this.table, first, rows, sortField, sortOrder, filters);
|
|
704
704
|
},
|
|
705
|
-
update: (rows,
|
|
706
|
-
return this.upd.query(this.schema, this.table, rows,
|
|
705
|
+
update: (rows, consumer) => {
|
|
706
|
+
return this.upd.query(this.schema, this.table, rows, consumer === null || consumer === void 0 ? void 0 : consumer.id, consumer === null || consumer === void 0 ? void 0 : consumer.nickname);
|
|
707
707
|
},
|
|
708
|
-
insert: (rows,
|
|
709
|
-
return this.ins.query(this.schema, this.table, rows,
|
|
708
|
+
insert: (rows, consumer, rtn = "") => {
|
|
709
|
+
return this.ins.query(this.schema, this.table, rows, consumer === null || consumer === void 0 ? void 0 : consumer.id, consumer === null || consumer === void 0 ? void 0 : consumer.nickname, rtn);
|
|
710
710
|
},
|
|
711
|
-
upsert: (rows, conflictTarget,
|
|
712
|
-
return this.ups.query(this.schema, this.table, rows, conflictTarget,
|
|
711
|
+
upsert: (rows, conflictTarget, consumer, rtn = "") => {
|
|
712
|
+
return this.ups.query(this.schema, this.table, rows, conflictTarget, consumer === null || consumer === void 0 ? void 0 : consumer.id, consumer === null || consumer === void 0 ? void 0 : consumer.nickname, rtn);
|
|
713
713
|
},
|
|
714
714
|
delete: (ids) => {
|
|
715
715
|
return queryById(this.schema, this.table, ids);
|
|
716
716
|
},
|
|
717
|
-
archive: (rows,
|
|
718
|
-
return this.arc.query(this.schema, this.table, rows,
|
|
717
|
+
archive: (rows, consumer) => {
|
|
718
|
+
return this.arc.query(this.schema, this.table, rows, consumer === null || consumer === void 0 ? void 0 : consumer.id, consumer === null || consumer === void 0 ? void 0 : consumer.nickname);
|
|
719
719
|
},
|
|
720
720
|
deleteArchive: () => {
|
|
721
721
|
return queryByDate();
|
|
@@ -735,9 +735,7 @@ class SQLEntity extends Entity {
|
|
|
735
735
|
const filters = cleanFilters(b.filters, this.properties) || null;
|
|
736
736
|
const pagination = b.pagination || false;
|
|
737
737
|
const dbClient = l.dbClient || null;
|
|
738
|
-
log.debug(`get(first='${first}', rows='${rows}',
|
|
739
|
-
sortOrder='${sortOrder}', sortField='${sortField}',
|
|
740
|
-
pagination=${pagination}, filters=${JSON.stringify(filters)}`);
|
|
738
|
+
log.debug(() => `get(first='${first}', rows='${rows}', sortOrder='${sortOrder}', sortField='${sortField}', pagination=${pagination}, filters=${JSON.stringify(filters)}`);
|
|
741
739
|
const { query, args } = this.sel.query(this._schema, this._table, first, rows, sortField, sortOrder, filters);
|
|
742
740
|
this.sel.execute(query, args, dbClient)
|
|
743
741
|
.then((r) => {
|
|
@@ -748,12 +746,13 @@ class SQLEntity extends Entity {
|
|
|
748
746
|
.catch((err) => next(err));
|
|
749
747
|
};
|
|
750
748
|
this.add = (req, res, next) => __awaiter(this, void 0, void 0, function* () {
|
|
749
|
+
var _a, _b;
|
|
751
750
|
const l = res.locals;
|
|
752
751
|
const rows = req.body.rows;
|
|
753
752
|
const dbClient = l.dbClient || null;
|
|
754
|
-
const cId = l.
|
|
755
|
-
const cName = l.
|
|
756
|
-
log.debug(`${LOGS_PREFIX}addMany(rows=${rows.length}, consumerId=${cId})`);
|
|
753
|
+
const cId = (_a = l.consumer) === null || _a === void 0 ? void 0 : _a.id;
|
|
754
|
+
const cName = (_b = l.consumer) === null || _b === void 0 ? void 0 : _b.nickname;
|
|
755
|
+
log.debug(() => `${LOGS_PREFIX}addMany(rows=${rows.length}, consumerId=${cId})`);
|
|
757
756
|
const rtn = this.ins.rtn("id");
|
|
758
757
|
const chunks = chunk(rows);
|
|
759
758
|
for (const c of chunks) {
|
|
@@ -774,12 +773,13 @@ class SQLEntity extends Entity {
|
|
|
774
773
|
next();
|
|
775
774
|
});
|
|
776
775
|
this.update = (req, res, next) => __awaiter(this, void 0, void 0, function* () {
|
|
776
|
+
var _a, _b;
|
|
777
777
|
const l = res.locals;
|
|
778
778
|
const r = req.body.rows;
|
|
779
779
|
const dbClient = l.dbClient || null;
|
|
780
|
-
const cId = l.
|
|
781
|
-
const cName = l.
|
|
782
|
-
log.debug(`${LOGS_PREFIX}update(rows=${r.length}, consumerId=${cId})`);
|
|
780
|
+
const cId = (_a = l.consumer) === null || _a === void 0 ? void 0 : _a.id;
|
|
781
|
+
const cName = (_b = l.consumer) === null || _b === void 0 ? void 0 : _b.nickname;
|
|
782
|
+
log.debug(() => `${LOGS_PREFIX}update(rows=${r.length}, consumerId=${cId})`);
|
|
783
783
|
const chunks = chunk(r);
|
|
784
784
|
for (const c of chunks) {
|
|
785
785
|
const { query, args } = this.upd.query(this._schema, this._table, c, cId, cName);
|
|
@@ -794,19 +794,20 @@ class SQLEntity extends Entity {
|
|
|
794
794
|
next();
|
|
795
795
|
});
|
|
796
796
|
this.upsert = (req, res, next) => __awaiter(this, void 0, void 0, function* () {
|
|
797
|
+
var _a, _b;
|
|
797
798
|
const l = res.locals;
|
|
798
799
|
const rows = req.body.rows;
|
|
799
800
|
const conflictTarget = req.body.conflictTarget;
|
|
800
801
|
const dbClient = l.dbClient || null;
|
|
801
|
-
const cId = l.
|
|
802
|
-
const cName = l.
|
|
802
|
+
const cId = (_a = l.consumer) === null || _a === void 0 ? void 0 : _a.id;
|
|
803
|
+
const cName = (_b = l.consumer) === null || _b === void 0 ? void 0 : _b.nickname;
|
|
803
804
|
if (!conflictTarget) {
|
|
804
805
|
return next({ status: 400, msg: "Missing conflictTarget for upsert operation" });
|
|
805
806
|
}
|
|
806
807
|
if (!rows || !Array.isArray(rows) || rows.length === 0) {
|
|
807
808
|
return next({ status: 400, msg: "Missing or empty rows array for upsert operation" });
|
|
808
809
|
}
|
|
809
|
-
log.debug(`${LOGS_PREFIX}upsert(rows=${rows.length}, conflictTarget=${conflictTarget}, consumerId=${cId})`);
|
|
810
|
+
log.debug(() => `${LOGS_PREFIX}upsert(rows=${rows.length}, conflictTarget=${conflictTarget}, consumerId=${cId})`);
|
|
810
811
|
const rtn = this.ups.rtn("id");
|
|
811
812
|
const chunks = chunk(rows);
|
|
812
813
|
for (const c of chunks) {
|
|
@@ -827,12 +828,13 @@ class SQLEntity extends Entity {
|
|
|
827
828
|
next();
|
|
828
829
|
});
|
|
829
830
|
this.archive = (req, res, next) => __awaiter(this, void 0, void 0, function* () {
|
|
831
|
+
var _a, _b;
|
|
830
832
|
const l = res.locals;
|
|
831
|
-
|
|
833
|
+
const r = req.body.rows;
|
|
832
834
|
const dbClient = l.dbClient || null;
|
|
833
|
-
const cId = l.
|
|
834
|
-
const cName = l.
|
|
835
|
-
log.debug(`${LOGS_PREFIX}archive(rows=${r.length}, consumerId=${cId})`);
|
|
835
|
+
const cId = (_a = l.consumer) === null || _a === void 0 ? void 0 : _a.id;
|
|
836
|
+
const cName = (_b = l.consumer) === null || _b === void 0 ? void 0 : _b.nickname;
|
|
837
|
+
log.debug(() => `${LOGS_PREFIX}archive(rows=${r.length}, consumerId=${cId})`);
|
|
836
838
|
const chunks = chunk(r);
|
|
837
839
|
for (const c of chunks) {
|
|
838
840
|
const { query, args } = this.arc.query(this._schema, this._table, c, cId, cName);
|
|
@@ -849,7 +851,7 @@ class SQLEntity extends Entity {
|
|
|
849
851
|
const rows = req.body.rows;
|
|
850
852
|
const dbClient = res.locals.dbClient || null;
|
|
851
853
|
const ids = rows.map((row) => row.id);
|
|
852
|
-
log.debug(`${LOGS_PREFIX}delete ${rows.length} rows : (${ids.join(", ")})`);
|
|
854
|
+
log.debug(() => `${LOGS_PREFIX}delete ${rows.length} rows : (${ids.join(", ")})`);
|
|
853
855
|
const { query, args } = queryById(this._schema, this._table, ids);
|
|
854
856
|
try {
|
|
855
857
|
yield execute(query, args, dbClient);
|
|
@@ -862,7 +864,7 @@ class SQLEntity extends Entity {
|
|
|
862
864
|
this.deleteArchive = (req, res, next) => {
|
|
863
865
|
const date = req.body.date;
|
|
864
866
|
const dbClient = res.locals.dbClient || null;
|
|
865
|
-
log.debug(`${LOGS_PREFIX}deleteArchive(schema=${this._schema}, table=${this._table}, date=${date})`);
|
|
867
|
+
log.debug(() => `${LOGS_PREFIX}deleteArchive(schema=${this._schema}, table=${this._table}, date=${date})`);
|
|
866
868
|
executeArchived(this._schema, this._table, date, queryByDate(), dbClient)
|
|
867
869
|
.then(() => next())
|
|
868
870
|
.catch((err) => next(err));
|
|
@@ -871,9 +873,10 @@ class SQLEntity extends Entity {
|
|
|
871
873
|
const id = req.params.id;
|
|
872
874
|
const dbClient = res.locals.dbClient || null;
|
|
873
875
|
if (!id) {
|
|
874
|
-
|
|
876
|
+
next({ status: 400, msg: "Missing id" });
|
|
877
|
+
return;
|
|
875
878
|
}
|
|
876
|
-
log.debug(`${LOGS_PREFIX}getHistory(schema=${this._schema}, table=${this._table}, id=${id})`);
|
|
879
|
+
log.debug(() => `${LOGS_PREFIX}getHistory(schema=${this._schema}, table=${this._table}, id=${id})`);
|
|
877
880
|
const sql = `
|
|
878
881
|
SELECT id, tstamp, operation, "consumerId", "consumerName"
|
|
879
882
|
FROM log.history
|
|
@@ -895,16 +898,16 @@ class SQLEntity extends Entity {
|
|
|
895
898
|
.catch((err) => next(err));
|
|
896
899
|
};
|
|
897
900
|
this.sync = (req, res, next) => __awaiter(this, void 0, void 0, function* () {
|
|
898
|
-
var _a;
|
|
901
|
+
var _a, _b, _c;
|
|
899
902
|
const l = res.locals;
|
|
900
903
|
const rows = req.body.rows;
|
|
901
904
|
const idField = (_a = req.body.idField) !== null && _a !== void 0 ? _a : 'id';
|
|
902
|
-
const cId = l.
|
|
903
|
-
const cName = l.
|
|
905
|
+
const cId = (_b = l.consumer) === null || _b === void 0 ? void 0 : _b.id;
|
|
906
|
+
const cName = (_c = l.consumer) === null || _c === void 0 ? void 0 : _c.nickname;
|
|
904
907
|
if (!rows || !Array.isArray(rows)) {
|
|
905
908
|
return next({ status: 400, msg: "Missing or invalid rows array for sync operation" });
|
|
906
909
|
}
|
|
907
|
-
log.debug(`${LOGS_PREFIX}sync(rows=${rows.length}, idField=${idField}, consumerId=${cId})`);
|
|
910
|
+
log.debug(() => `${LOGS_PREFIX}sync(rows=${rows.length}, idField=${idField}, consumerId=${cId})`);
|
|
908
911
|
const cleanedFilters = cleanFilters(req.body.filters, this.properties) || null;
|
|
909
912
|
const { conditions, args: filterArgs } = add(cleanedFilters);
|
|
910
913
|
const whereClause = conditions.length ? ` WHERE ${conditions.join(' AND ')}` : '';
|
|
@@ -964,7 +967,7 @@ class SQLEntity extends Entity {
|
|
|
964
967
|
});
|
|
965
968
|
this._table = name;
|
|
966
969
|
this._schema = schema;
|
|
967
|
-
log.info(`${LOGS_PREFIX}Creating SQLEntity: "${name}"`);
|
|
970
|
+
log.info(() => `${LOGS_PREFIX}Creating SQLEntity: "${name}"`);
|
|
968
971
|
for (const p of properties) {
|
|
969
972
|
this.mapProps(p.operations, p.key);
|
|
970
973
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dwtechs/antity-pgsql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Open source library to add PostgreSQL support to @dwtechs/Antity entities.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"entities"
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@dwtechs/checkard": "3.6.0",
|
|
40
|
-
"@dwtechs/winstan": "0.
|
|
40
|
+
"@dwtechs/winstan": "0.7.0",
|
|
41
41
|
"@dwtechs/antity": "0.16.0",
|
|
42
42
|
"@dwtechs/sparray": "0.2.1",
|
|
43
43
|
"pg": "8.20.0"
|