@dwtechs/antity-pgsql 0.1.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/LICENSE +21 -0
- package/README.md +289 -0
- package/dist/antity-pgsql.d.ts +117 -0
- package/dist/antity-pgsql.js +588 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 DWTechs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
|
|
2
|
+
[](https://opensource.org/licenses/MIT)
|
|
3
|
+
[](https://www.npmjs.com/package/@dwtechs/antity-pgsql)
|
|
4
|
+
[](https://www.npmjs.com/package/@dwtechs/antity-pgsql)
|
|
5
|
+

|
|
6
|
+
[](https://www.npmjs.com/package/@dwtechs/antity-pgsql)
|
|
7
|
+
|
|
8
|
+
- [Synopsis](#synopsis)
|
|
9
|
+
- [Support](#support)
|
|
10
|
+
- [Installation](#installation)
|
|
11
|
+
- [Usage](#usage)
|
|
12
|
+
- [ES6](#es6)
|
|
13
|
+
- [API Reference](#api-reference)
|
|
14
|
+
- [Contributors](#contributors)
|
|
15
|
+
- [Stack](#stack)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## Synopsis
|
|
19
|
+
|
|
20
|
+
**[Antity-pgsql.js](https://github.com/DWTechs/Antity-pgsql.js)** adds PostgreSQL features to **Antity.js** library.
|
|
21
|
+
|
|
22
|
+
- Very lightweight
|
|
23
|
+
- Thoroughly tested
|
|
24
|
+
- Works in Javascript, Typescript
|
|
25
|
+
- Can be used as EcmaScrypt module
|
|
26
|
+
- Written in Typescript
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
## Support
|
|
30
|
+
|
|
31
|
+
- node: 22
|
|
32
|
+
|
|
33
|
+
This is the oldest targeted versions. The library should work properly on older versions of Node.js but we do not support it officially.
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
$ npm i @dwtechs/antity-pgsql
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
### ES6 / TypeScript
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
|
|
50
|
+
import { SQLEntity } from "@dwtechs/antity-pgsql";
|
|
51
|
+
import { normalizeName, normalizeNickname } from "@dwtechs/checkard";
|
|
52
|
+
|
|
53
|
+
const entity = new Entity("consumers", [
|
|
54
|
+
{
|
|
55
|
+
key: "id",
|
|
56
|
+
type: "integer",
|
|
57
|
+
min: 0,
|
|
58
|
+
max: 120,
|
|
59
|
+
typeCheck: true,
|
|
60
|
+
methods: ["GET", "PUT", "DELETE"],
|
|
61
|
+
required: true,
|
|
62
|
+
safe: true,
|
|
63
|
+
sanitize: true,
|
|
64
|
+
normalize: true,
|
|
65
|
+
validate: true,
|
|
66
|
+
sanitizer: null,
|
|
67
|
+
normalizer: null,
|
|
68
|
+
validator: null,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
key: "firstName",
|
|
72
|
+
type: "string",
|
|
73
|
+
min: 0,
|
|
74
|
+
max: 255,
|
|
75
|
+
typeCheck: true,
|
|
76
|
+
methods: ["GET", "POST", "PUT", "DELETE"],
|
|
77
|
+
required: true,
|
|
78
|
+
safe: true,
|
|
79
|
+
sanitize: true,
|
|
80
|
+
normalize: true,
|
|
81
|
+
validate: true,
|
|
82
|
+
sanitizer: null,
|
|
83
|
+
normalizer: normalizeName,
|
|
84
|
+
validator: null,
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
key: "lastName",
|
|
88
|
+
type: "string",
|
|
89
|
+
min: 0,
|
|
90
|
+
max: 255,
|
|
91
|
+
typeCheck: true,
|
|
92
|
+
methods: ["GET", "POST", "PUT", "DELETE"],
|
|
93
|
+
required: true,
|
|
94
|
+
safe: true,
|
|
95
|
+
sanitize: true,
|
|
96
|
+
normalize: true,
|
|
97
|
+
validate: true,
|
|
98
|
+
sanitizer: null,
|
|
99
|
+
normalizer: normalizeName,
|
|
100
|
+
validator: null,
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
key: "nickname",
|
|
104
|
+
type: "string",
|
|
105
|
+
min: 0,
|
|
106
|
+
max: 255,
|
|
107
|
+
typeCheck: true,
|
|
108
|
+
methods: ["GET", "POST", "PUT", "DELETE"],
|
|
109
|
+
required: true,
|
|
110
|
+
safe: true,
|
|
111
|
+
sanitize: true,
|
|
112
|
+
normalize: true,
|
|
113
|
+
validate: true,
|
|
114
|
+
sanitizer: null,
|
|
115
|
+
normalizer: normalizeNickname,
|
|
116
|
+
validator: null,
|
|
117
|
+
},
|
|
118
|
+
]);
|
|
119
|
+
|
|
120
|
+
// add a consumer. Used when loggin in from user service
|
|
121
|
+
router.gett("/", ..., entity.get);
|
|
122
|
+
router.post("/", entity.normalize, entity.validate, ..., entity.add);
|
|
123
|
+
router.put("/", entity.normalize, entity.validate, ..., entity.update);
|
|
124
|
+
router.put("/", ..., entity.archive);
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## API Reference
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
```javascript
|
|
132
|
+
|
|
133
|
+
type Operation = "select" | "insert" | "update" | "merge" | "delete";
|
|
134
|
+
|
|
135
|
+
type MatchMode =
|
|
136
|
+
"startsWith" |
|
|
137
|
+
"endsWith" |
|
|
138
|
+
"contains" |
|
|
139
|
+
"notContains" |
|
|
140
|
+
"equals" |
|
|
141
|
+
"notEquals" |
|
|
142
|
+
"between" |
|
|
143
|
+
"in" |
|
|
144
|
+
"lt" |
|
|
145
|
+
"lte" |
|
|
146
|
+
"gt" |
|
|
147
|
+
"gte" |
|
|
148
|
+
"is" |
|
|
149
|
+
"isNot" |
|
|
150
|
+
"before" |
|
|
151
|
+
"after" |
|
|
152
|
+
"st_contains" |
|
|
153
|
+
"st_dwithin";
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
type Filter = {
|
|
157
|
+
value: string | number | boolean | Date | number[];
|
|
158
|
+
subProps?: string[];
|
|
159
|
+
matchMode?: MatchMode;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
class SQLEntity {
|
|
163
|
+
constructor(name: string, properties: Property[]);
|
|
164
|
+
get name(): string;
|
|
165
|
+
get table(): string;
|
|
166
|
+
get unsafeProps(): string[];
|
|
167
|
+
get properties(): Property[];
|
|
168
|
+
set name(name: string);
|
|
169
|
+
set table(table: string);
|
|
170
|
+
|
|
171
|
+
query: {
|
|
172
|
+
select: (paginate: boolean) => string;
|
|
173
|
+
update: (chunk: Record<string, unknown>[], consumerId: number | string, consumerName: string) => {
|
|
174
|
+
query: string;
|
|
175
|
+
args: unknown[];
|
|
176
|
+
};
|
|
177
|
+
insert: (chunk: Record<string, unknown>[], consumerId: number | string, consumerName: string, rtn?: string) => {
|
|
178
|
+
query: string;
|
|
179
|
+
args: unknown[];
|
|
180
|
+
};
|
|
181
|
+
delete: () => string;
|
|
182
|
+
return: (prop: string) => string;
|
|
183
|
+
};
|
|
184
|
+
get(req: Request, res: Response, next: NextFunction): void;
|
|
185
|
+
add(req: Request, res: Response, next: NextFunction): Promise<void>;
|
|
186
|
+
update(req: Request, res: Response, next: NextFunction): Promise<void>;
|
|
187
|
+
archive(req: Request, res: Response, next: NextFunction): Promise<void>;
|
|
188
|
+
delete(req: Request, res: Response, next: NextFunction): void;
|
|
189
|
+
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
filter(
|
|
193
|
+
first: number,
|
|
194
|
+
rows: number | null,
|
|
195
|
+
sortField: string | null,
|
|
196
|
+
sortOrder: Sort,
|
|
197
|
+
filters: Filters | null,
|
|
198
|
+
): { filterClause: string, args: (Filter["value"])[] } {
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
get(), add(), update(), archive() and delete() methods are made to be used as Express.js middlewares.
|
|
203
|
+
Each method will look for data to work on in the **req.body.rows** parameter.
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
## Match modes
|
|
207
|
+
|
|
208
|
+
List of possible match modes :
|
|
209
|
+
|
|
210
|
+
| Name | alias | types | Description |
|
|
211
|
+
| :---------- | :---- | :---------------------- | :-------------------------------------------------------- |
|
|
212
|
+
| startsWith | | string | Whether the value starts with the filter value |
|
|
213
|
+
| contains | | string | Whether the value contains the filter value |
|
|
214
|
+
| endsWith | | string | Whether the value ends with the filter value |
|
|
215
|
+
| notContains | | string | Whether the value does not contain filter value |
|
|
216
|
+
| equals | | string \| number | Whether the value equals the filter value |
|
|
217
|
+
| notEquals | | string \| number | Whether the value does not equal the filter value |
|
|
218
|
+
| in | | string[] \| number[] | Whether the value contains the filter value |
|
|
219
|
+
| lt | | string \| number | Whether the value is less than the filter value |
|
|
220
|
+
| lte | | string \| number | Whether the value is less than or equals to the filter value |
|
|
221
|
+
| gt | | string \| number | Whether the value is greater than the filter value |
|
|
222
|
+
| gte | | string \| number | Whether the value is greater than or equals to the filter value |
|
|
223
|
+
| is | | date \| boolean \| null | Whether the value equals the filter value, alias to equals |
|
|
224
|
+
| isNot | | date \| boolean \| null | Whether the value does not equal the filter value, alias to notEquals |
|
|
225
|
+
| before | | date | Whether the date value is before the filter date |
|
|
226
|
+
| after | | date | Whether the date value is after the filter date |
|
|
227
|
+
| between | | date[2] \| number[2] | Whether the value is between the filter values |
|
|
228
|
+
| st_contains | | geometry | Whether the geometry completely contains other geometries |
|
|
229
|
+
| st_dwithin | | geometry | Whether geometries are within a specified distance from another geometry |
|
|
230
|
+
|
|
231
|
+
## Types
|
|
232
|
+
|
|
233
|
+
List of compatible match modes for each property types
|
|
234
|
+
|
|
235
|
+
| Name | Match modes |
|
|
236
|
+
| :---------- | :---------------------- |
|
|
237
|
+
| string | startsWith,<br>contains,<br>endsWith,<br>notContains,<br>equals,<br>notEquals,<br>lt,<br>lte,<br>gt,<br>gte |
|
|
238
|
+
| number | equals,<br>notEquals,<br>lt,<br>lte,<br>gt,<br>gte |
|
|
239
|
+
| date | is,<br>isNot,<br>before,<br>after |
|
|
240
|
+
| boolean | is,<br>isNot |
|
|
241
|
+
| string[] | in |
|
|
242
|
+
| number[] | in,<br>between |
|
|
243
|
+
| date[] | between |
|
|
244
|
+
| geometry | st_contains,<br>st_dwithin |
|
|
245
|
+
|
|
246
|
+
List of secondary types :
|
|
247
|
+
|
|
248
|
+
| Name | equivalent |
|
|
249
|
+
| :----------------- | :--------- |
|
|
250
|
+
| integer | number |
|
|
251
|
+
| float | number |
|
|
252
|
+
| even | number |
|
|
253
|
+
| odd | number |
|
|
254
|
+
| positive | number |
|
|
255
|
+
| negative | number |
|
|
256
|
+
| powerOfTwo | number |
|
|
257
|
+
| ascii | number |
|
|
258
|
+
| array | any[] |
|
|
259
|
+
| jwt | string |
|
|
260
|
+
| symbol | string |
|
|
261
|
+
| email | string |
|
|
262
|
+
| regex | string |
|
|
263
|
+
| ipAddress | string |
|
|
264
|
+
| slug | string |
|
|
265
|
+
| hexadecimal | string |
|
|
266
|
+
| date | date |
|
|
267
|
+
| timestamp | date |
|
|
268
|
+
| function | string |
|
|
269
|
+
| htmlElement | string |
|
|
270
|
+
| htmlEventAttribute | string |
|
|
271
|
+
| node | string |
|
|
272
|
+
| json | object |
|
|
273
|
+
| object | object |
|
|
274
|
+
|
|
275
|
+
## Contributors
|
|
276
|
+
|
|
277
|
+
Antity.js is still in development and we would be glad to get all the help you can provide.
|
|
278
|
+
To contribute please read **[contributor.md](https://github.com/DWTechs/Antity.js/blob/main/contributor.md)** for detailed installation guide.
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
## Stack
|
|
282
|
+
|
|
283
|
+
| Purpose | Choice | Motivation |
|
|
284
|
+
| :-------------- | :------------------------------------------: | -------------------------------------------------------------: |
|
|
285
|
+
| repository | [Github](https://github.com/) | hosting for software development version control using Git |
|
|
286
|
+
| package manager | [npm](https://www.npmjs.com/get-npm) | default node.js package manager |
|
|
287
|
+
| language | [TypeScript](https://www.typescriptlang.org) | static type checking along with the latest ECMAScript features |
|
|
288
|
+
| module bundler | [Rollup](https://rollupjs.org) | advanced module bundler for ES6 modules |
|
|
289
|
+
| unit testing | [Jest](https://jestjs.io/) | delightful testing with a focus on simplicity |
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/*
|
|
2
|
+
MIT License
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2025 DWTechs
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
|
23
|
+
|
|
24
|
+
https://github.com/DWTechs/Antity-pgsql.js
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { Entity, Property } from "@dwtechs/antity";
|
|
28
|
+
import type { Request, NextFunction } from 'express';
|
|
29
|
+
|
|
30
|
+
export type Operation = "SELECT" | "INSERT" | "UPDATE" | "DELETE";
|
|
31
|
+
export type Sort = "ASC" | "DESC";
|
|
32
|
+
export type Filters = {
|
|
33
|
+
[key: string]: Filter;
|
|
34
|
+
};
|
|
35
|
+
export type Filter = {
|
|
36
|
+
value: string | number | boolean | Date | number[];
|
|
37
|
+
subProps?: string[];
|
|
38
|
+
matchMode?: MatchMode;
|
|
39
|
+
};
|
|
40
|
+
export type LogicalOperator = "AND" | "OR";
|
|
41
|
+
export type Comparator = "=" | "<" | ">" | "<=" | ">=" | "<>" | "IS" | "IS NOT" | "IN" | "LIKE" | "NOT LIKE";
|
|
42
|
+
export type MatchMode = "startsWith" | "endsWith" | "contains" | "notContains" | "equals" | "notEquals" | "between" | "in" | "lt" | "lte" | "gt" | "gte" | "is" | "isNot" | "before" | "after" | "st_contains" | "st_dwithin";
|
|
43
|
+
export type MappedType = "string" | "number" | "date";
|
|
44
|
+
export type Type = "boolean" | "string" | "number" | "integer" | "float" | "even" | "odd" | "positive" | "negative" | "powerOfTwo" | "ascii" | "array" | "jwt" | "symbol" | "email" | "regex" | "json" | "ipAddress" | "slug" | "hexadecimal" | "date" | "timestamp" | "function" | "htmlElement" | "htmlEventAttribute" | "node" | "object" | "geometry";
|
|
45
|
+
export type Geometry = {
|
|
46
|
+
lng: number;
|
|
47
|
+
lat: number;
|
|
48
|
+
radius: number;
|
|
49
|
+
bounds: {
|
|
50
|
+
minLng: number;
|
|
51
|
+
minLat: number;
|
|
52
|
+
maxLng: number;
|
|
53
|
+
maxLat: number;
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
export type PGResponse = {
|
|
57
|
+
rows: Record<string, unknown>[];
|
|
58
|
+
rowCount: number;
|
|
59
|
+
total?: number;
|
|
60
|
+
command?: string;
|
|
61
|
+
oid?: number;
|
|
62
|
+
fields?: unknown[];
|
|
63
|
+
_parsers?: unknown[];
|
|
64
|
+
_types?: unknown;
|
|
65
|
+
RowCtor?: unknown;
|
|
66
|
+
rowAsArray?: boolean;
|
|
67
|
+
};
|
|
68
|
+
export type Response = {
|
|
69
|
+
rows: Record<string, unknown>[];
|
|
70
|
+
total?: number;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
declare class SQLEntity extends Entity {
|
|
74
|
+
private _table;
|
|
75
|
+
private sel;
|
|
76
|
+
private ins;
|
|
77
|
+
private upd;
|
|
78
|
+
constructor(name: string, properties: Property[]);
|
|
79
|
+
get table(): string;
|
|
80
|
+
set table(table: string);
|
|
81
|
+
query: {
|
|
82
|
+
select: (paginate: boolean) => string;
|
|
83
|
+
update: (rows: Record<string, unknown>[], consumerId: number | string, consumerName: string) => {
|
|
84
|
+
query: string;
|
|
85
|
+
args: unknown[];
|
|
86
|
+
};
|
|
87
|
+
insert: (rows: Record<string, unknown>[], consumerId: number | string, consumerName: string, rtn?: string) => {
|
|
88
|
+
query: string;
|
|
89
|
+
args: unknown[];
|
|
90
|
+
};
|
|
91
|
+
delete: () => string;
|
|
92
|
+
return: (prop: string) => string;
|
|
93
|
+
};
|
|
94
|
+
get(req: Request, res: Response, next: NextFunction): void;
|
|
95
|
+
add(req: Request, res: Response, next: NextFunction): Promise<void>;
|
|
96
|
+
update(req: Request, res: Response, next: NextFunction): Promise<void>;
|
|
97
|
+
archive(req: Request, res: Response, next: NextFunction): Promise<void>;
|
|
98
|
+
delete(req: Request, res: Response, next: NextFunction): void;
|
|
99
|
+
private cleanFilters;
|
|
100
|
+
private mapProps;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
declare filter(
|
|
104
|
+
first: number,
|
|
105
|
+
rows: number | null,
|
|
106
|
+
sortField: string | null,
|
|
107
|
+
sortOrder: Sort,
|
|
108
|
+
filters: Filters | null,
|
|
109
|
+
): { filterClause: string, args: (Filter["value"])[] } {]
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
export {
|
|
113
|
+
SQLEntity,
|
|
114
|
+
Property,
|
|
115
|
+
filter,
|
|
116
|
+
};
|
|
117
|
+
|
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
/*
|
|
2
|
+
MIT License
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2025 DWTechs
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
|
23
|
+
|
|
24
|
+
https://github.com/DWTechs/Antity-pgsql.js
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { isIn, isArray, isString } from '@dwtechs/checkard';
|
|
28
|
+
import { deleteProps, add as add$1, chunk, flatten } from '@dwtechs/sparray';
|
|
29
|
+
import { log } from '@dwtechs/winstan';
|
|
30
|
+
import { Entity } from '@dwtechs/antity';
|
|
31
|
+
import Pool from 'pg-pool';
|
|
32
|
+
|
|
33
|
+
function type(type) {
|
|
34
|
+
const s = "string";
|
|
35
|
+
const n = "number";
|
|
36
|
+
const d = "date";
|
|
37
|
+
switch (type) {
|
|
38
|
+
case "integer":
|
|
39
|
+
return n;
|
|
40
|
+
case "float":
|
|
41
|
+
return n;
|
|
42
|
+
case "even":
|
|
43
|
+
return n;
|
|
44
|
+
case "odd":
|
|
45
|
+
return n;
|
|
46
|
+
case "positive":
|
|
47
|
+
return n;
|
|
48
|
+
case "negative":
|
|
49
|
+
return n;
|
|
50
|
+
case "powerOfTwo":
|
|
51
|
+
return n;
|
|
52
|
+
case "ascii":
|
|
53
|
+
return n;
|
|
54
|
+
case "jwt":
|
|
55
|
+
return s;
|
|
56
|
+
case "symbol":
|
|
57
|
+
return s;
|
|
58
|
+
case "email":
|
|
59
|
+
return s;
|
|
60
|
+
case "regex":
|
|
61
|
+
return s;
|
|
62
|
+
case "ipAddress":
|
|
63
|
+
return s;
|
|
64
|
+
case "slug":
|
|
65
|
+
return s;
|
|
66
|
+
case "hexadecimal":
|
|
67
|
+
return s;
|
|
68
|
+
case "date":
|
|
69
|
+
return d;
|
|
70
|
+
case "timestamp":
|
|
71
|
+
return d;
|
|
72
|
+
case "function":
|
|
73
|
+
return s;
|
|
74
|
+
case "htmlElement":
|
|
75
|
+
return s;
|
|
76
|
+
case "htmlEventAttribute":
|
|
77
|
+
return s;
|
|
78
|
+
case "node":
|
|
79
|
+
return s;
|
|
80
|
+
case "json":
|
|
81
|
+
return s;
|
|
82
|
+
case "object":
|
|
83
|
+
return s;
|
|
84
|
+
default:
|
|
85
|
+
return s;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const matchModes = {
|
|
90
|
+
string: ["startsWith", "contains", "endsWith", "notContains", "equals", "notEquals", "lt", "lte", "gt", "gte"],
|
|
91
|
+
number: ["equals", "notEquals", "lt", "lte", "gt", "gte"],
|
|
92
|
+
date: ["is", "isNot", "dateAfter"],
|
|
93
|
+
};
|
|
94
|
+
function matchMode(type, matchMode) {
|
|
95
|
+
return isIn(matchModes[type], matchMode);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const { DB_HOST, DB_USER, DB_PWD, DB_NAME, DB_PORT, DB_MAX } = process.env;
|
|
99
|
+
var pool = new Pool({
|
|
100
|
+
host: DB_HOST,
|
|
101
|
+
user: DB_USER,
|
|
102
|
+
password: DB_PWD,
|
|
103
|
+
database: DB_NAME,
|
|
104
|
+
port: +(DB_PORT || 5432),
|
|
105
|
+
max: DB_MAX ? +DB_MAX : 10,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
function start(query, args) {
|
|
109
|
+
const a = JSON.stringify(args);
|
|
110
|
+
const q = query.replace(/[\n\r]+/g, "").replace(/\s{2,}/g, " ");
|
|
111
|
+
log.debug(`Pgsql: { Query : '${q}', Args : '${a}' }`);
|
|
112
|
+
return Date.now();
|
|
113
|
+
}
|
|
114
|
+
function end(res, time) {
|
|
115
|
+
const r = JSON.stringify(res);
|
|
116
|
+
const t = Date.now() - time;
|
|
117
|
+
log.debug(`Pgsql response in ${t}ms : ${r}`);
|
|
118
|
+
}
|
|
119
|
+
var perf = {
|
|
120
|
+
start,
|
|
121
|
+
end,
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
function execute$1(query, args, clt) {
|
|
125
|
+
const time = perf.start(query, args);
|
|
126
|
+
const client = clt || pool;
|
|
127
|
+
return client
|
|
128
|
+
.query(query, args)
|
|
129
|
+
.then((res) => {
|
|
130
|
+
perf.end(res, time);
|
|
131
|
+
deleteIdleProperties(res);
|
|
132
|
+
return res;
|
|
133
|
+
})
|
|
134
|
+
.catch((err) => {
|
|
135
|
+
err.msg = `Postgre error: ${err.message}`;
|
|
136
|
+
throw err;
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
function deleteIdleProperties(res) {
|
|
140
|
+
res.command = undefined;
|
|
141
|
+
res.oid = undefined;
|
|
142
|
+
res.fields = undefined;
|
|
143
|
+
res._parsers = undefined;
|
|
144
|
+
res._types = undefined;
|
|
145
|
+
res.RowCtor = undefined;
|
|
146
|
+
res.rowAsArray = undefined;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
class Select {
|
|
150
|
+
constructor() {
|
|
151
|
+
this._props = [];
|
|
152
|
+
this._cols = "";
|
|
153
|
+
this._count = ", COUNT(*) OVER () AS total";
|
|
154
|
+
}
|
|
155
|
+
addProp(prop) {
|
|
156
|
+
this._props.push(prop);
|
|
157
|
+
this._cols = this._props.join(", ");
|
|
158
|
+
}
|
|
159
|
+
get props() {
|
|
160
|
+
return this._cols;
|
|
161
|
+
}
|
|
162
|
+
query(table, paginate) {
|
|
163
|
+
const p = paginate ? this._count : '';
|
|
164
|
+
const c = this._cols ? this._cols : '*';
|
|
165
|
+
return `SELECT ${c}${p} FROM "${table}"`;
|
|
166
|
+
}
|
|
167
|
+
execute(query, args, client) {
|
|
168
|
+
return execute$1(query, args, client)
|
|
169
|
+
.then((r) => {
|
|
170
|
+
if (!r.rowCount)
|
|
171
|
+
throw { status: 404, msg: "Resource not found" };
|
|
172
|
+
const f = r.rows[0];
|
|
173
|
+
if (f.total) {
|
|
174
|
+
r.total = Number(f.total);
|
|
175
|
+
r.rows = deleteProps(r.rows, ["total"]);
|
|
176
|
+
}
|
|
177
|
+
return r;
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function $i(qty, start) {
|
|
183
|
+
return `(${Array.from({ length: qty }, (_, i) => `$${start + i + 1}`).join(", ")})`;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
class Insert {
|
|
187
|
+
constructor() {
|
|
188
|
+
this._props = ["consumerId", "consumerName"];
|
|
189
|
+
this._cols = "*";
|
|
190
|
+
this._nbProps = 2;
|
|
191
|
+
}
|
|
192
|
+
addProp(prop) {
|
|
193
|
+
this._props = add$1(this._props, prop, this._props.length - 2);
|
|
194
|
+
this._cols = this._props.join(", ");
|
|
195
|
+
this._nbProps++;
|
|
196
|
+
}
|
|
197
|
+
query(table, rows, consumerId, consumerName, rtn = "") {
|
|
198
|
+
let query = `INSERT INTO "${table}" (${this._cols}) VALUES `;
|
|
199
|
+
const args = [];
|
|
200
|
+
let i = 0;
|
|
201
|
+
for (const row of rows) {
|
|
202
|
+
row.consumerId = consumerId;
|
|
203
|
+
row.consumerName = consumerName;
|
|
204
|
+
query += `${$i(this._nbProps, i)}, `;
|
|
205
|
+
for (const prop of this._props) {
|
|
206
|
+
args.push(row[prop]);
|
|
207
|
+
}
|
|
208
|
+
i += this._nbProps;
|
|
209
|
+
}
|
|
210
|
+
query = query.slice(0, -2);
|
|
211
|
+
if (rtn)
|
|
212
|
+
query += ` ${rtn}`;
|
|
213
|
+
return { query, args };
|
|
214
|
+
}
|
|
215
|
+
rtn(prop) {
|
|
216
|
+
return `RETURNING "${prop}"`;
|
|
217
|
+
}
|
|
218
|
+
execute(query, args, client) {
|
|
219
|
+
return execute$1(query, args, client);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
var __awaiter$2 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
224
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
225
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
226
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
227
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
228
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
229
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
230
|
+
});
|
|
231
|
+
};
|
|
232
|
+
class Update {
|
|
233
|
+
constructor() {
|
|
234
|
+
this._props = ["consumerId", "consumerName"];
|
|
235
|
+
}
|
|
236
|
+
addProp(prop) {
|
|
237
|
+
this._props = add$1(this._props, prop, this._props.length - 2);
|
|
238
|
+
}
|
|
239
|
+
query(table, rows, consumerId, consumerName) {
|
|
240
|
+
rows = this.addConsumer(rows, consumerId, consumerName);
|
|
241
|
+
const l = rows.length;
|
|
242
|
+
const args = rows.map(row => row.id);
|
|
243
|
+
let query = `UPDATE "${table}" SET `;
|
|
244
|
+
let i = args.length + 1;
|
|
245
|
+
for (const p of this._props) {
|
|
246
|
+
if (rows[0][p] === undefined)
|
|
247
|
+
continue;
|
|
248
|
+
query += `${p} = CASE `;
|
|
249
|
+
for (let j = 0; j < l; j++) {
|
|
250
|
+
const row = rows[j];
|
|
251
|
+
query += `WHEN id = $${j + 1} THEN $${i++} `;
|
|
252
|
+
args.push(row[p]);
|
|
253
|
+
}
|
|
254
|
+
query += `END, `;
|
|
255
|
+
}
|
|
256
|
+
query = `${query.slice(0, -2)} WHERE id IN ${$i(l, 0)}`;
|
|
257
|
+
return { query, args };
|
|
258
|
+
}
|
|
259
|
+
execute(query, args, client) {
|
|
260
|
+
return __awaiter$2(this, void 0, void 0, function* () {
|
|
261
|
+
return execute$1(query, args, client);
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
addConsumer(rows, consumerId, consumerName) {
|
|
265
|
+
return rows.map((row) => (Object.assign(Object.assign({}, row), { consumerId,
|
|
266
|
+
consumerName })));
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
var __awaiter$1 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
271
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
272
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
273
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
274
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
275
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
276
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
277
|
+
});
|
|
278
|
+
};
|
|
279
|
+
function query(table) {
|
|
280
|
+
return `DELETE FROM "${table}" WHERE "archivedAt" < $1`;
|
|
281
|
+
}
|
|
282
|
+
function execute(date, query, client) {
|
|
283
|
+
return __awaiter$1(this, void 0, void 0, function* () {
|
|
284
|
+
let db;
|
|
285
|
+
try {
|
|
286
|
+
db = yield execute$1(query, [date], client);
|
|
287
|
+
}
|
|
288
|
+
catch (err) {
|
|
289
|
+
throw err;
|
|
290
|
+
}
|
|
291
|
+
return db;
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function index(index, matchMode) {
|
|
296
|
+
const i = index.map((i) => `$${i}`);
|
|
297
|
+
switch (matchMode) {
|
|
298
|
+
case "startsWith":
|
|
299
|
+
return `${i}%`;
|
|
300
|
+
case "endsWith":
|
|
301
|
+
return `%${i}`;
|
|
302
|
+
case "contains":
|
|
303
|
+
return `%${i}%`;
|
|
304
|
+
case "notContains":
|
|
305
|
+
return `%${i}%`;
|
|
306
|
+
case "in":
|
|
307
|
+
return `(${i})`;
|
|
308
|
+
default:
|
|
309
|
+
return `${i}`;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function comparator(matchMode) {
|
|
314
|
+
switch (matchMode) {
|
|
315
|
+
case "startsWith":
|
|
316
|
+
return "LIKE";
|
|
317
|
+
case "endsWith":
|
|
318
|
+
return "LIKE";
|
|
319
|
+
case "contains":
|
|
320
|
+
return "LIKE";
|
|
321
|
+
case "notContains":
|
|
322
|
+
return "NOT LIKE";
|
|
323
|
+
case "equals":
|
|
324
|
+
return "=";
|
|
325
|
+
case "notEquals":
|
|
326
|
+
return "<>";
|
|
327
|
+
case "in":
|
|
328
|
+
return "IN";
|
|
329
|
+
case "lt":
|
|
330
|
+
return "<";
|
|
331
|
+
case "lte":
|
|
332
|
+
return "<=";
|
|
333
|
+
case "gt":
|
|
334
|
+
return ">";
|
|
335
|
+
case "gte":
|
|
336
|
+
return ">=";
|
|
337
|
+
case "is":
|
|
338
|
+
return "IS";
|
|
339
|
+
case "isNot":
|
|
340
|
+
return "IS NOT";
|
|
341
|
+
case "before":
|
|
342
|
+
return "<";
|
|
343
|
+
case "after":
|
|
344
|
+
return ">";
|
|
345
|
+
default:
|
|
346
|
+
return null;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function add(filters) {
|
|
351
|
+
const conditions = [];
|
|
352
|
+
const args = [];
|
|
353
|
+
if (filters) {
|
|
354
|
+
let i = 1;
|
|
355
|
+
for (const k in filters) {
|
|
356
|
+
const { value, matchMode } = filters[k];
|
|
357
|
+
const indexes = isArray(value) ? value.map(() => i++) : [i++];
|
|
358
|
+
const cond = addOne(k, indexes, matchMode);
|
|
359
|
+
if (cond) {
|
|
360
|
+
conditions.push(cond);
|
|
361
|
+
if (isArray(value))
|
|
362
|
+
args.push(...value);
|
|
363
|
+
else
|
|
364
|
+
args.push(value);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return { conditions, args };
|
|
369
|
+
}
|
|
370
|
+
function addOne(key, indexes, matchMode) {
|
|
371
|
+
const sqlKey = `\"${key}\"`;
|
|
372
|
+
const comparator$1 = comparator(matchMode);
|
|
373
|
+
const index$1 = index(indexes, matchMode);
|
|
374
|
+
return comparator$1 ? `${sqlKey} ${comparator$1} ${index$1}` : "";
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function filter(first, rows, sortField, sortOrder, filters) {
|
|
378
|
+
const { conditions, args } = add(filters);
|
|
379
|
+
const filterClause = where(conditions)
|
|
380
|
+
+ orderBy(sortField, sortOrder)
|
|
381
|
+
+ limit(rows, first);
|
|
382
|
+
return { filterClause, args };
|
|
383
|
+
}
|
|
384
|
+
function where(conditions, operator = "AND") {
|
|
385
|
+
if (!conditions.length)
|
|
386
|
+
return "";
|
|
387
|
+
const c = conditions.join(` ${operator} `).trim();
|
|
388
|
+
return ` WHERE ${c}`;
|
|
389
|
+
}
|
|
390
|
+
function orderBy(sortField, sortOrder) {
|
|
391
|
+
if (!sortField)
|
|
392
|
+
return "";
|
|
393
|
+
return ` ORDER BY "${sortField}" ${sortOrder}`;
|
|
394
|
+
}
|
|
395
|
+
function limit(rows, first) {
|
|
396
|
+
return rows ? ` LIMIT ${rows} OFFSET ${first}` : "";
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
400
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
401
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
402
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
403
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
404
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
405
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
406
|
+
});
|
|
407
|
+
};
|
|
408
|
+
class SQLEntity extends Entity {
|
|
409
|
+
constructor(name, properties) {
|
|
410
|
+
super(name, properties);
|
|
411
|
+
this.sel = new Select();
|
|
412
|
+
this.ins = new Insert();
|
|
413
|
+
this.upd = new Update();
|
|
414
|
+
this.query = {
|
|
415
|
+
select: (paginate) => {
|
|
416
|
+
return this.sel.query(this.table, paginate);
|
|
417
|
+
},
|
|
418
|
+
update: (rows, consumerId, consumerName) => {
|
|
419
|
+
return this.upd.query(this.table, rows, consumerId, consumerName);
|
|
420
|
+
},
|
|
421
|
+
insert: (rows, consumerId, consumerName, rtn = "") => {
|
|
422
|
+
return this.ins.query(this.table, rows, consumerId, consumerName, rtn);
|
|
423
|
+
},
|
|
424
|
+
delete: () => {
|
|
425
|
+
return query(this.table);
|
|
426
|
+
},
|
|
427
|
+
return: (prop) => {
|
|
428
|
+
return this.ins.rtn(prop);
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
this._table = name;
|
|
432
|
+
for (const p of properties) {
|
|
433
|
+
this.mapProps(p.methods, p.key);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
get table() {
|
|
437
|
+
return this._table;
|
|
438
|
+
}
|
|
439
|
+
set table(table) {
|
|
440
|
+
if (!isString(table, "!0"))
|
|
441
|
+
throw new Error('table must be a string of length > 0');
|
|
442
|
+
this._table = table;
|
|
443
|
+
}
|
|
444
|
+
get(req, res, next) {
|
|
445
|
+
var _a;
|
|
446
|
+
const l = res.locals;
|
|
447
|
+
const b = req.body;
|
|
448
|
+
const first = (_a = b === null || b === void 0 ? void 0 : b.first) !== null && _a !== void 0 ? _a : 0;
|
|
449
|
+
const rows = b.rows || null;
|
|
450
|
+
const sortField = b.sortField || null;
|
|
451
|
+
const sortOrder = b.sortOrder === -1 || b.sortOrder === "DESC" ? "DESC" : "ASC";
|
|
452
|
+
const filters = this.cleanFilters(b.filters) || null;
|
|
453
|
+
const pagination = b.pagination || false;
|
|
454
|
+
const dbClient = l.dbClient || null;
|
|
455
|
+
log.debug(`get(first='${first}', rows='${rows}',
|
|
456
|
+
sortOrder='${sortOrder}', sortField='${sortField}',
|
|
457
|
+
pagination=${pagination}, filters=${JSON.stringify(filters)}`);
|
|
458
|
+
const { filterClause, args } = filter(first, rows, sortField, sortOrder, filters);
|
|
459
|
+
const q = this.sel.query(this._table, pagination) + filterClause;
|
|
460
|
+
this.sel.execute(q, args, dbClient)
|
|
461
|
+
.then((r) => {
|
|
462
|
+
l.rows = r.rows;
|
|
463
|
+
l.total = r.total;
|
|
464
|
+
next();
|
|
465
|
+
})
|
|
466
|
+
.catch((err) => next(err));
|
|
467
|
+
}
|
|
468
|
+
add(req, res, next) {
|
|
469
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
470
|
+
const l = res.locals;
|
|
471
|
+
const rows = req.body.rows;
|
|
472
|
+
const dbClient = l.dbClient || null;
|
|
473
|
+
const cId = l.consumerId;
|
|
474
|
+
const cName = l.consumerName;
|
|
475
|
+
log.debug(`addMany(rows=${rows.length}, consumerId=${cId})`);
|
|
476
|
+
const rtn = this.ins.rtn("id");
|
|
477
|
+
const chunks = chunk(rows);
|
|
478
|
+
for (const c of chunks) {
|
|
479
|
+
const { query, args } = this.ins.query(this._table, c, cId, cName, rtn);
|
|
480
|
+
let db;
|
|
481
|
+
try {
|
|
482
|
+
db = yield execute$1(query, args, dbClient);
|
|
483
|
+
}
|
|
484
|
+
catch (err) {
|
|
485
|
+
return next(err);
|
|
486
|
+
}
|
|
487
|
+
const r = db.rows;
|
|
488
|
+
for (let i = 0; i < c.length; i++) {
|
|
489
|
+
c[i].id = r[i].id;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
l.rows = flatten(chunks);
|
|
493
|
+
next();
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
update(req, res, next) {
|
|
497
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
498
|
+
const l = res.locals;
|
|
499
|
+
const rows = req.body.rows;
|
|
500
|
+
const dbClient = l.dbClient || null;
|
|
501
|
+
const cId = l.consumerId;
|
|
502
|
+
const cName = l.consumerName;
|
|
503
|
+
log.debug(`update(rows=${rows.length}, consumerId=${cId})`);
|
|
504
|
+
const chunks = chunk(rows);
|
|
505
|
+
for (const c of chunks) {
|
|
506
|
+
const { query, args } = this.upd.query(this._table, c, cId, cName);
|
|
507
|
+
try {
|
|
508
|
+
yield execute$1(query, args, dbClient);
|
|
509
|
+
}
|
|
510
|
+
catch (err) {
|
|
511
|
+
return next(err);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
next();
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
archive(req, res, next) {
|
|
518
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
519
|
+
const l = res.locals;
|
|
520
|
+
let rows = req.body.rows;
|
|
521
|
+
const dbClient = l.dbClient || null;
|
|
522
|
+
const cId = l.consumerId;
|
|
523
|
+
const cName = l.consumerName;
|
|
524
|
+
log.debug(`archive(rows=${rows.length}, consumerId=${cId})`);
|
|
525
|
+
rows = rows.map((id) => (Object.assign(Object.assign({}, id), { archived: true })));
|
|
526
|
+
const chunks = chunk(rows);
|
|
527
|
+
for (const c of chunks) {
|
|
528
|
+
const { query, args } = this.upd.query(this._table, c, cId, cName);
|
|
529
|
+
try {
|
|
530
|
+
yield execute$1(query, args, dbClient);
|
|
531
|
+
}
|
|
532
|
+
catch (err) {
|
|
533
|
+
return next(err);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
next();
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
delete(req, res, next) {
|
|
540
|
+
const date = req.body.date;
|
|
541
|
+
const dbClient = res.locals.dbClient || null;
|
|
542
|
+
log.debug(`delete archived`);
|
|
543
|
+
const q = query(this._table);
|
|
544
|
+
execute(date, q, dbClient)
|
|
545
|
+
.then(() => next())
|
|
546
|
+
.catch((err) => next(err));
|
|
547
|
+
}
|
|
548
|
+
cleanFilters(filters) {
|
|
549
|
+
for (const k in filters) {
|
|
550
|
+
if (filters.hasOwnProperty(k)) {
|
|
551
|
+
const prop = this.getProp(k);
|
|
552
|
+
if (!prop) {
|
|
553
|
+
log.warn(`Filters: skipping unknown property: ${k}`);
|
|
554
|
+
delete filters[k];
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
const type$1 = type(prop.type);
|
|
558
|
+
const { matchMode: matchMode$1 } = filters[k];
|
|
559
|
+
if (!matchMode$1 || !matchMode(type$1, matchMode$1)) {
|
|
560
|
+
log.warn(`Filters: skipping invalid match mode: "${matchMode$1}" for type: "${type$1}" at property: "${k}"`);
|
|
561
|
+
delete filters[k];
|
|
562
|
+
continue;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
return filters;
|
|
567
|
+
}
|
|
568
|
+
mapProps(methods, key) {
|
|
569
|
+
for (const m of methods) {
|
|
570
|
+
switch (m) {
|
|
571
|
+
case "GET":
|
|
572
|
+
this.sel.addProp(key);
|
|
573
|
+
break;
|
|
574
|
+
case "PATCH":
|
|
575
|
+
this.upd.addProp(key);
|
|
576
|
+
break;
|
|
577
|
+
case "PUT":
|
|
578
|
+
this.upd.addProp(key);
|
|
579
|
+
break;
|
|
580
|
+
case "POST":
|
|
581
|
+
this.ins.addProp(key);
|
|
582
|
+
break;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
export { SQLEntity, filter };
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dwtechs/antity-pgsql",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Open source library for easy entity management",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"entities"
|
|
7
|
+
],
|
|
8
|
+
"homepage": "https://github.com/DWTechs/Antity.js",
|
|
9
|
+
"main": "dist/antity",
|
|
10
|
+
"types": "dist/antity",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "https://github.com/DWTechs/Antity.js"
|
|
14
|
+
},
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/DWTechs/Antity.js/issues",
|
|
17
|
+
"email": ""
|
|
18
|
+
},
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"author": {
|
|
21
|
+
"name": "Ludovic Cluber",
|
|
22
|
+
"email": "http://www.lcluber.com/contact",
|
|
23
|
+
"url": "http://www.lcluber.com"
|
|
24
|
+
},
|
|
25
|
+
"contributors": [],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"start": "",
|
|
28
|
+
"prebuild": "npm install",
|
|
29
|
+
"build": "node ./scripts/clear && tsc && npm run rollup && node ./scripts/copy && npm run test",
|
|
30
|
+
"rollup:mjs": "rollup --config rollup.config.mjs",
|
|
31
|
+
"rollup:cjs": "rollup --config rollup.config.cjs.mjs",
|
|
32
|
+
"rollup": "npm run rollup:mjs",
|
|
33
|
+
"test": "jest --coverage"
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist/"
|
|
37
|
+
],
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@dwtechs/checkard": "3.2.3",
|
|
40
|
+
"@dwtechs/winstan": "0.4.0",
|
|
41
|
+
"@dwtechs/antity": "0.9.0",
|
|
42
|
+
"@dwtechs/sparray": "0.2.0",
|
|
43
|
+
"pg": "8.13.1"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@babel/core": "7.26.0",
|
|
47
|
+
"@babel/preset-env": "7.26.0",
|
|
48
|
+
"@rollup/plugin-commonjs": "28.0.1",
|
|
49
|
+
"@rollup/plugin-node-resolve": "15.3.0",
|
|
50
|
+
"@types/jest": "29.5.14",
|
|
51
|
+
"@types/node": "22.10.1",
|
|
52
|
+
"@types/pg-pool": "2.0.6",
|
|
53
|
+
"@types/express": "5.0.0",
|
|
54
|
+
"babel-jest": "29.7.0",
|
|
55
|
+
"core-js": "3.33.0",
|
|
56
|
+
"jest": "29.7.0",
|
|
57
|
+
"rollup": "4.24.0",
|
|
58
|
+
"typescript": "5.6.3"
|
|
59
|
+
}
|
|
60
|
+
}
|