@dwtechs/antity-pgsql 0.3.3 → 0.5.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 +64 -11
- package/dist/antity-pgsql.d.ts +19 -8
- package/dist/antity-pgsql.js +310 -217
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -52,7 +52,9 @@ const entity = new Entity("consumers", [
|
|
|
52
52
|
min: 0,
|
|
53
53
|
max: 120,
|
|
54
54
|
typeCheck: true,
|
|
55
|
+
filter: true,
|
|
55
56
|
methods: ["GET", "PUT", "DELETE"],
|
|
57
|
+
operations: ["SELECT", "UPDATE", "DELETE"],
|
|
56
58
|
required: true,
|
|
57
59
|
safe: true,
|
|
58
60
|
sanitize: true,
|
|
@@ -68,7 +70,9 @@ const entity = new Entity("consumers", [
|
|
|
68
70
|
min: 0,
|
|
69
71
|
max: 255,
|
|
70
72
|
typeCheck: true,
|
|
73
|
+
filter: false,
|
|
71
74
|
methods: ["GET", "POST", "PUT", "DELETE"],
|
|
75
|
+
operations: ["SELECT", "UPDATE", "DELETE"],
|
|
72
76
|
required: true,
|
|
73
77
|
safe: true,
|
|
74
78
|
sanitize: true,
|
|
@@ -84,7 +88,9 @@ const entity = new Entity("consumers", [
|
|
|
84
88
|
min: 0,
|
|
85
89
|
max: 255,
|
|
86
90
|
typeCheck: true,
|
|
91
|
+
filter: false,
|
|
87
92
|
methods: ["GET", "POST", "PUT", "DELETE"],
|
|
93
|
+
operations: ["SELECT", "UPDATE", "DELETE"],
|
|
88
94
|
required: true,
|
|
89
95
|
safe: true,
|
|
90
96
|
sanitize: true,
|
|
@@ -100,7 +106,9 @@ const entity = new Entity("consumers", [
|
|
|
100
106
|
min: 0,
|
|
101
107
|
max: 255,
|
|
102
108
|
typeCheck: true,
|
|
109
|
+
filter: true,
|
|
103
110
|
methods: ["GET", "POST", "PUT", "DELETE"],
|
|
111
|
+
operations: ["SELECT", "UPDATE", "DELETE"],
|
|
104
112
|
required: true,
|
|
105
113
|
safe: true,
|
|
106
114
|
sanitize: true,
|
|
@@ -124,7 +132,7 @@ router.put("/", ..., entity.archive);
|
|
|
124
132
|
|
|
125
133
|
```javascript
|
|
126
134
|
|
|
127
|
-
type Operation = "
|
|
135
|
+
type Operation = "SELECT" | "INSERT" | "UPDATE" | "DELETE";
|
|
128
136
|
|
|
129
137
|
type MatchMode =
|
|
130
138
|
"startsWith" |
|
|
@@ -163,17 +171,33 @@ class SQLEntity {
|
|
|
163
171
|
set table(table: string);
|
|
164
172
|
|
|
165
173
|
query: {
|
|
166
|
-
select: (
|
|
167
|
-
|
|
174
|
+
select: (
|
|
175
|
+
paginate: boolean,
|
|
176
|
+
first?: number,
|
|
177
|
+
rows?: number | null,
|
|
178
|
+
sortField?: string | null,
|
|
179
|
+
sortOrder?: "ASC" | "DESC" | null,
|
|
180
|
+
filters?: Filters | null) => {
|
|
168
181
|
query: string;
|
|
169
|
-
args:
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
182
|
+
args: (Filter["value"])[];
|
|
183
|
+
};
|
|
184
|
+
update: (
|
|
185
|
+
rows: Record<string, unknown>[],
|
|
186
|
+
consumerId?: number | string,
|
|
187
|
+
consumerName?: string) => {
|
|
188
|
+
query: string;
|
|
189
|
+
args: unknown[];
|
|
190
|
+
};
|
|
191
|
+
insert: (
|
|
192
|
+
rows: Record<string, unknown>[],
|
|
193
|
+
consumerId?: number | string,
|
|
194
|
+
consumerName?: string,
|
|
195
|
+
rtn?: string) => {
|
|
196
|
+
query: string;
|
|
197
|
+
args: unknown[];
|
|
198
|
+
};
|
|
199
|
+
delete: () => string;
|
|
200
|
+
return: (prop: string) => string;
|
|
177
201
|
};
|
|
178
202
|
get: (req: Request, res: Response, next: NextFunction) => void;
|
|
179
203
|
add: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
@@ -273,6 +297,35 @@ List of secondary types :
|
|
|
273
297
|
| json | object |
|
|
274
298
|
| object | object |
|
|
275
299
|
|
|
300
|
+
|
|
301
|
+
## Available options for a property
|
|
302
|
+
|
|
303
|
+
Any of these can be passed into the options object for each function.
|
|
304
|
+
|
|
305
|
+
| Name | Type | Description | Default value |
|
|
306
|
+
| :-------------- | :------------------------ | :------------------------------------------------ | :-------------- |
|
|
307
|
+
| key | string | Name of the property |
|
|
308
|
+
| type | Type | Type of the property |
|
|
309
|
+
| min | number \| Date | Minimum value | 0 \| 1900-01-01
|
|
310
|
+
| max | number \| Date | Maximum value | 999999999 \| 2200-12-31
|
|
311
|
+
| required | boolean | Property is required during validation | false
|
|
312
|
+
| safe | boolean | Property is sent in the response | true
|
|
313
|
+
| typeCheck | boolean | Type is checked during validation | false
|
|
314
|
+
| filter | boolean | property is filterable in a SELECT operation | true
|
|
315
|
+
| methods | Method[] | property is validated for the listed methods only | [ "GET", "POST", "PUT", "DELETE" ]
|
|
316
|
+
| operations | Operation[] | SQL DML operations for the property | [ "SELECT", "INSERT", "UPDATE", "DELETE" ]
|
|
317
|
+
| sanitize | boolean | Sanitize the property if true | true
|
|
318
|
+
| normalize | boolean | Normalize the property if true | false
|
|
319
|
+
| validate | boolean | validate the property if true | true
|
|
320
|
+
| sanitizer | ((v:any) => any) \| null | Custom sanitizer function if sanitize is true | null
|
|
321
|
+
| normalizer | ((v:any) => any) \| null | Custop Normalizer function if normalize is true | null
|
|
322
|
+
| validator | ((v:any, min:number, max:number, typeCheck:boolean) => any) \| null | validator function if validate is true | null
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
* *Min and max parameters are not used for boolean type*
|
|
326
|
+
* *TypeCheck Parameter is not used for boolean, string and array types*
|
|
327
|
+
|
|
328
|
+
|
|
276
329
|
## Contributors
|
|
277
330
|
|
|
278
331
|
Antity.js is still in development and we would be glad to get all the help you can provide.
|
package/dist/antity-pgsql.d.ts
CHANGED
|
@@ -24,8 +24,9 @@ SOFTWARE.
|
|
|
24
24
|
https://github.com/DWTechs/Antity-pgsql.js
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
|
-
import { Entity
|
|
27
|
+
import { Entity } from "@dwtechs/antity";
|
|
28
28
|
import { Type } from "@dwtechs/antity";
|
|
29
|
+
import { Property } from './property';
|
|
29
30
|
import type { Request, Response, NextFunction } from 'express';
|
|
30
31
|
|
|
31
32
|
export type Operation = "SELECT" | "INSERT" | "UPDATE" | "DELETE";
|
|
@@ -76,14 +77,24 @@ declare class SQLEntity extends Entity {
|
|
|
76
77
|
get table(): string;
|
|
77
78
|
set table(table: string);
|
|
78
79
|
query: {
|
|
79
|
-
select: (
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
select: (
|
|
81
|
+
paginate: boolean,
|
|
82
|
+
first?: number,
|
|
83
|
+
rows?: number | null,
|
|
84
|
+
sortField?: string | null,
|
|
85
|
+
sortOrder?: "ASC" | "DESC" | null,
|
|
86
|
+
filters?: Filters | null) => {
|
|
87
|
+
query: string;
|
|
88
|
+
args: (Filter["value"])[];
|
|
83
89
|
};
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
90
|
+
update: (rows: Record<string,
|
|
91
|
+
unknown>[], consumerId?: number | string, consumerName?: string) => {
|
|
92
|
+
query: string;
|
|
93
|
+
args: unknown[];
|
|
94
|
+
};
|
|
95
|
+
insert: (rows: Record<string, unknown>[], consumerId?: number | string, consumerName?: string, rtn?: string) => {
|
|
96
|
+
query: string;
|
|
97
|
+
args: unknown[];
|
|
87
98
|
};
|
|
88
99
|
delete: () => string;
|
|
89
100
|
return: (prop: string) => string;
|
package/dist/antity-pgsql.js
CHANGED
|
@@ -24,79 +24,12 @@ SOFTWARE.
|
|
|
24
24
|
https://github.com/DWTechs/Antity-pgsql.js
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
|
-
import {
|
|
28
|
-
import { deleteProps,
|
|
27
|
+
import { isArray, isIn, isString } from '@dwtechs/checkard';
|
|
28
|
+
import { deleteProps, chunk, flatten } from '@dwtechs/sparray';
|
|
29
29
|
import { log } from '@dwtechs/winstan';
|
|
30
30
|
import { Entity } from '@dwtechs/antity';
|
|
31
31
|
import Pool from 'pg-pool';
|
|
32
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 "password":
|
|
59
|
-
return s;
|
|
60
|
-
case "email":
|
|
61
|
-
return s;
|
|
62
|
-
case "regex":
|
|
63
|
-
return s;
|
|
64
|
-
case "ipAddress":
|
|
65
|
-
return s;
|
|
66
|
-
case "slug":
|
|
67
|
-
return s;
|
|
68
|
-
case "hexadecimal":
|
|
69
|
-
return s;
|
|
70
|
-
case "date":
|
|
71
|
-
return d;
|
|
72
|
-
case "timestamp":
|
|
73
|
-
return d;
|
|
74
|
-
case "function":
|
|
75
|
-
return s;
|
|
76
|
-
case "htmlElement":
|
|
77
|
-
return s;
|
|
78
|
-
case "htmlEventAttribute":
|
|
79
|
-
return s;
|
|
80
|
-
case "node":
|
|
81
|
-
return s;
|
|
82
|
-
case "json":
|
|
83
|
-
return s;
|
|
84
|
-
case "object":
|
|
85
|
-
return s;
|
|
86
|
-
default:
|
|
87
|
-
return s;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const matchModes = {
|
|
92
|
-
string: ["startsWith", "contains", "endsWith", "notContains", "equals", "notEquals", "lt", "lte", "gt", "gte"],
|
|
93
|
-
number: ["equals", "notEquals", "lt", "lte", "gt", "gte"],
|
|
94
|
-
date: ["is", "isNot", "dateAfter"],
|
|
95
|
-
};
|
|
96
|
-
function matchMode(type, matchMode) {
|
|
97
|
-
return isIn(matchModes[type], matchMode);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
33
|
const { DB_HOST, DB_USER, DB_PWD, DB_NAME, DB_PORT, DB_MAX } = process.env;
|
|
101
34
|
var pool = new Pool({
|
|
102
35
|
host: DB_HOST,
|
|
@@ -107,10 +40,12 @@ var pool = new Pool({
|
|
|
107
40
|
max: DB_MAX ? +DB_MAX : 10,
|
|
108
41
|
});
|
|
109
42
|
|
|
43
|
+
const LOGS_PREFIX = '[Antity-PGSQL] ';
|
|
44
|
+
|
|
110
45
|
function start(query, args) {
|
|
111
46
|
const a = JSON.stringify(args);
|
|
112
47
|
const q = query.replace(/[\n\r]+/g, "").replace(/\s{2,}/g, " ");
|
|
113
|
-
log.debug(
|
|
48
|
+
log.debug(`${LOGS_PREFIX}Pgsql: { Query : '${q}', Args : '${a}' }`);
|
|
114
49
|
return Date.now();
|
|
115
50
|
}
|
|
116
51
|
function end(res, time) {
|
|
@@ -169,6 +104,111 @@ function quoteIfUppercase(word) {
|
|
|
169
104
|
return word;
|
|
170
105
|
}
|
|
171
106
|
|
|
107
|
+
function index(index, matchMode) {
|
|
108
|
+
const i = index.map((i) => `$${i}`);
|
|
109
|
+
switch (matchMode) {
|
|
110
|
+
case "startsWith":
|
|
111
|
+
return `${i}%`;
|
|
112
|
+
case "endsWith":
|
|
113
|
+
return `%${i}`;
|
|
114
|
+
case "contains":
|
|
115
|
+
return `%${i}%`;
|
|
116
|
+
case "notContains":
|
|
117
|
+
return `%${i}%`;
|
|
118
|
+
case "in":
|
|
119
|
+
return `(${i})`;
|
|
120
|
+
default:
|
|
121
|
+
return `${i}`;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function comparator(matchMode) {
|
|
126
|
+
switch (matchMode) {
|
|
127
|
+
case "startsWith":
|
|
128
|
+
return "LIKE";
|
|
129
|
+
case "endsWith":
|
|
130
|
+
return "LIKE";
|
|
131
|
+
case "contains":
|
|
132
|
+
return "LIKE";
|
|
133
|
+
case "notContains":
|
|
134
|
+
return "NOT LIKE";
|
|
135
|
+
case "equals":
|
|
136
|
+
return "=";
|
|
137
|
+
case "notEquals":
|
|
138
|
+
return "<>";
|
|
139
|
+
case "in":
|
|
140
|
+
return "IN";
|
|
141
|
+
case "lt":
|
|
142
|
+
return "<";
|
|
143
|
+
case "lte":
|
|
144
|
+
return "<=";
|
|
145
|
+
case "gt":
|
|
146
|
+
return ">";
|
|
147
|
+
case "gte":
|
|
148
|
+
return ">=";
|
|
149
|
+
case "is":
|
|
150
|
+
return "IS";
|
|
151
|
+
case "isNot":
|
|
152
|
+
return "IS NOT";
|
|
153
|
+
case "before":
|
|
154
|
+
return "<";
|
|
155
|
+
case "after":
|
|
156
|
+
return ">";
|
|
157
|
+
default:
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function add(filters) {
|
|
163
|
+
const conditions = [];
|
|
164
|
+
const args = [];
|
|
165
|
+
if (filters) {
|
|
166
|
+
let i = 1;
|
|
167
|
+
for (const k in filters) {
|
|
168
|
+
const { value, matchMode } = filters[k];
|
|
169
|
+
const indexes = isArray(value) ? value.map(() => i++) : [i++];
|
|
170
|
+
const cond = addOne(k, indexes, matchMode);
|
|
171
|
+
if (cond) {
|
|
172
|
+
conditions.push(cond);
|
|
173
|
+
if (isArray(value))
|
|
174
|
+
args.push(...value);
|
|
175
|
+
else
|
|
176
|
+
args.push(value);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return { conditions, args };
|
|
181
|
+
}
|
|
182
|
+
function addOne(key, indexes, matchMode) {
|
|
183
|
+
const sqlKey = `${quoteIfUppercase(key)}`;
|
|
184
|
+
const comparator$1 = comparator(matchMode);
|
|
185
|
+
const index$1 = index(indexes, matchMode);
|
|
186
|
+
return comparator$1 ? `${sqlKey} ${comparator$1} ${index$1}` : "";
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function filter(first, rows, sortField, sortOrder, filters) {
|
|
190
|
+
const { conditions, args } = add(filters);
|
|
191
|
+
const filterClause = where(conditions)
|
|
192
|
+
+ orderBy(sortField, sortOrder)
|
|
193
|
+
+ limit(rows, first);
|
|
194
|
+
return { filterClause, args };
|
|
195
|
+
}
|
|
196
|
+
function where(conditions, operator = "AND") {
|
|
197
|
+
if (!conditions.length)
|
|
198
|
+
return "";
|
|
199
|
+
const c = conditions.join(` ${operator} `).trim();
|
|
200
|
+
return ` WHERE ${c}`;
|
|
201
|
+
}
|
|
202
|
+
function orderBy(sortField, sortOrder) {
|
|
203
|
+
if (!sortField)
|
|
204
|
+
return "";
|
|
205
|
+
const o = sortOrder || "ASC";
|
|
206
|
+
return ` ORDER BY ${quoteIfUppercase(sortField)} ${o}`;
|
|
207
|
+
}
|
|
208
|
+
function limit(rows, first) {
|
|
209
|
+
return rows ? ` LIMIT ${rows} OFFSET ${first}` : "";
|
|
210
|
+
}
|
|
211
|
+
|
|
172
212
|
class Select {
|
|
173
213
|
constructor() {
|
|
174
214
|
this._props = [];
|
|
@@ -182,10 +222,15 @@ class Select {
|
|
|
182
222
|
get props() {
|
|
183
223
|
return this._cols;
|
|
184
224
|
}
|
|
185
|
-
query(table, paginate) {
|
|
225
|
+
query(table, paginate, first = 0, rows = null, sortField = null, sortOrder = null, filters = null) {
|
|
186
226
|
const p = paginate ? this._count : '';
|
|
187
227
|
const c = this._cols ? this._cols : '*';
|
|
188
|
-
|
|
228
|
+
const baseQuery = `SELECT ${c}${p} FROM ${quoteIfUppercase(table)}`;
|
|
229
|
+
const { filterClause, args } = filter(first, rows, sortField, sortOrder, filters);
|
|
230
|
+
return {
|
|
231
|
+
query: baseQuery + filterClause,
|
|
232
|
+
args
|
|
233
|
+
};
|
|
189
234
|
}
|
|
190
235
|
execute(query, args, client) {
|
|
191
236
|
return execute$1(query, args, client)
|
|
@@ -208,27 +253,37 @@ function $i(qty, start) {
|
|
|
208
253
|
|
|
209
254
|
class Insert {
|
|
210
255
|
constructor() {
|
|
211
|
-
this._props = [
|
|
212
|
-
this.
|
|
213
|
-
this.
|
|
256
|
+
this._props = [];
|
|
257
|
+
this._nbProps = 0;
|
|
258
|
+
this._cols = "";
|
|
214
259
|
}
|
|
215
260
|
addProp(prop) {
|
|
216
|
-
this._props
|
|
217
|
-
this._cols = this._props.join(", ");
|
|
261
|
+
this._props.push(quoteIfUppercase(prop));
|
|
218
262
|
this._nbProps++;
|
|
263
|
+
this._cols = this._props.join(", ");
|
|
219
264
|
}
|
|
220
265
|
query(table, rows, consumerId, consumerName, rtn = "") {
|
|
221
|
-
|
|
266
|
+
const propsToUse = [...this._props];
|
|
267
|
+
let nbProps = this._nbProps;
|
|
268
|
+
let cols = this._cols;
|
|
269
|
+
if (consumerId !== undefined && consumerName !== undefined) {
|
|
270
|
+
propsToUse.push("consumerId", "consumerName");
|
|
271
|
+
nbProps += 2;
|
|
272
|
+
cols += ", consumerId, consumerName";
|
|
273
|
+
}
|
|
274
|
+
let query = `INSERT INTO ${quoteIfUppercase(table)} (${cols}) VALUES `;
|
|
222
275
|
const args = [];
|
|
223
276
|
let i = 0;
|
|
224
277
|
for (const row of rows) {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
278
|
+
if (consumerId !== undefined && consumerName !== undefined) {
|
|
279
|
+
row.consumerId = consumerId;
|
|
280
|
+
row.consumerName = consumerName;
|
|
281
|
+
}
|
|
282
|
+
query += `${$i(nbProps, i)}, `;
|
|
283
|
+
for (const prop of propsToUse) {
|
|
229
284
|
args.push(row[prop]);
|
|
230
285
|
}
|
|
231
|
-
i +=
|
|
286
|
+
i += nbProps;
|
|
232
287
|
}
|
|
233
288
|
query = query.slice(0, -2);
|
|
234
289
|
if (rtn)
|
|
@@ -254,18 +309,24 @@ var __awaiter$2 = (undefined && undefined.__awaiter) || function (thisArg, _argu
|
|
|
254
309
|
};
|
|
255
310
|
class Update {
|
|
256
311
|
constructor() {
|
|
257
|
-
this._props = [
|
|
312
|
+
this._props = [];
|
|
258
313
|
}
|
|
259
314
|
addProp(prop) {
|
|
260
|
-
this._props
|
|
315
|
+
this._props.push(quoteIfUppercase(prop));
|
|
261
316
|
}
|
|
262
317
|
query(table, rows, consumerId, consumerName) {
|
|
263
|
-
|
|
318
|
+
if (consumerId !== undefined && consumerName !== undefined) {
|
|
319
|
+
rows = this.addConsumer(rows, consumerId, consumerName);
|
|
320
|
+
}
|
|
321
|
+
const propsToUse = [...this._props];
|
|
322
|
+
if (consumerId !== undefined && consumerName !== undefined) {
|
|
323
|
+
propsToUse.push("consumerId", "consumerName");
|
|
324
|
+
}
|
|
264
325
|
const l = rows.length;
|
|
265
326
|
const args = rows.map(row => row.id);
|
|
266
327
|
let query = `UPDATE "${quoteIfUppercase(table)}" SET `;
|
|
267
328
|
let i = args.length + 1;
|
|
268
|
-
for (const p of
|
|
329
|
+
for (const p of propsToUse) {
|
|
269
330
|
if (rows[0][p] === undefined)
|
|
270
331
|
continue;
|
|
271
332
|
query += `${p} = CASE `;
|
|
@@ -315,109 +376,163 @@ function execute(date, query, client) {
|
|
|
315
376
|
});
|
|
316
377
|
}
|
|
317
378
|
|
|
318
|
-
function
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
case "
|
|
324
|
-
return
|
|
325
|
-
case "
|
|
326
|
-
return
|
|
327
|
-
case "
|
|
328
|
-
return
|
|
329
|
-
case "
|
|
330
|
-
return
|
|
379
|
+
function type(type) {
|
|
380
|
+
const s = "string";
|
|
381
|
+
const n = "number";
|
|
382
|
+
const d = "date";
|
|
383
|
+
switch (type) {
|
|
384
|
+
case "integer":
|
|
385
|
+
return n;
|
|
386
|
+
case "float":
|
|
387
|
+
return n;
|
|
388
|
+
case "even":
|
|
389
|
+
return n;
|
|
390
|
+
case "odd":
|
|
391
|
+
return n;
|
|
392
|
+
case "positive":
|
|
393
|
+
return n;
|
|
394
|
+
case "negative":
|
|
395
|
+
return n;
|
|
396
|
+
case "powerOfTwo":
|
|
397
|
+
return n;
|
|
398
|
+
case "ascii":
|
|
399
|
+
return n;
|
|
400
|
+
case "jwt":
|
|
401
|
+
return s;
|
|
402
|
+
case "symbol":
|
|
403
|
+
return s;
|
|
404
|
+
case "password":
|
|
405
|
+
return s;
|
|
406
|
+
case "email":
|
|
407
|
+
return s;
|
|
408
|
+
case "regex":
|
|
409
|
+
return s;
|
|
410
|
+
case "ipAddress":
|
|
411
|
+
return s;
|
|
412
|
+
case "slug":
|
|
413
|
+
return s;
|
|
414
|
+
case "hexadecimal":
|
|
415
|
+
return s;
|
|
416
|
+
case "date":
|
|
417
|
+
return d;
|
|
418
|
+
case "timestamp":
|
|
419
|
+
return d;
|
|
420
|
+
case "function":
|
|
421
|
+
return s;
|
|
422
|
+
case "htmlElement":
|
|
423
|
+
return s;
|
|
424
|
+
case "htmlEventAttribute":
|
|
425
|
+
return s;
|
|
426
|
+
case "node":
|
|
427
|
+
return s;
|
|
428
|
+
case "json":
|
|
429
|
+
return s;
|
|
430
|
+
case "object":
|
|
431
|
+
return s;
|
|
331
432
|
default:
|
|
332
|
-
return
|
|
433
|
+
return s;
|
|
333
434
|
}
|
|
334
435
|
}
|
|
335
436
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
return "LIKE";
|
|
344
|
-
case "notContains":
|
|
345
|
-
return "NOT LIKE";
|
|
346
|
-
case "equals":
|
|
347
|
-
return "=";
|
|
348
|
-
case "notEquals":
|
|
349
|
-
return "<>";
|
|
350
|
-
case "in":
|
|
351
|
-
return "IN";
|
|
352
|
-
case "lt":
|
|
353
|
-
return "<";
|
|
354
|
-
case "lte":
|
|
355
|
-
return "<=";
|
|
356
|
-
case "gt":
|
|
357
|
-
return ">";
|
|
358
|
-
case "gte":
|
|
359
|
-
return ">=";
|
|
360
|
-
case "is":
|
|
361
|
-
return "IS";
|
|
362
|
-
case "isNot":
|
|
363
|
-
return "IS NOT";
|
|
364
|
-
case "before":
|
|
365
|
-
return "<";
|
|
366
|
-
case "after":
|
|
367
|
-
return ">";
|
|
368
|
-
default:
|
|
369
|
-
return null;
|
|
370
|
-
}
|
|
437
|
+
const matchModes = {
|
|
438
|
+
string: ["startsWith", "contains", "endsWith", "notContains", "equals", "notEquals", "lt", "lte", "gt", "gte"],
|
|
439
|
+
number: ["equals", "notEquals", "lt", "lte", "gt", "gte"],
|
|
440
|
+
date: ["is", "isNot", "dateAfter"],
|
|
441
|
+
};
|
|
442
|
+
function matchMode(type, matchMode) {
|
|
443
|
+
return isIn(matchModes[type], matchMode);
|
|
371
444
|
}
|
|
372
445
|
|
|
373
|
-
function
|
|
374
|
-
const
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
if (
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
446
|
+
function cleanFilters(filters, properties) {
|
|
447
|
+
for (const k in filters) {
|
|
448
|
+
if (filters.hasOwnProperty(k)) {
|
|
449
|
+
const prop = properties.find(p => p.key === k);
|
|
450
|
+
if (!prop) {
|
|
451
|
+
log.warn(`${LOGS_PREFIX}Filters: skipping unknown property: ${k}`);
|
|
452
|
+
delete filters[k];
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
455
|
+
if (!prop.filter) {
|
|
456
|
+
log.warn(`${LOGS_PREFIX}Filters: skipping unfilterable property: ${k}`);
|
|
457
|
+
delete filters[k];
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
const type$1 = type(prop.type);
|
|
461
|
+
const { matchMode: matchMode$1 } = filters[k];
|
|
462
|
+
if (!matchMode$1 || !matchMode(type$1, matchMode$1)) {
|
|
463
|
+
log.warn(`${LOGS_PREFIX}Filters: skipping invalid match mode: "${matchMode$1}" for type: "${type$1}" at property: "${k}"`);
|
|
464
|
+
delete filters[k];
|
|
465
|
+
continue;
|
|
388
466
|
}
|
|
389
467
|
}
|
|
390
468
|
}
|
|
391
|
-
return
|
|
392
|
-
}
|
|
393
|
-
function addOne(key, indexes, matchMode) {
|
|
394
|
-
const sqlKey = `${quoteIfUppercase(key)}`;
|
|
395
|
-
const comparator$1 = comparator(matchMode);
|
|
396
|
-
const index$1 = index(indexes, matchMode);
|
|
397
|
-
return comparator$1 ? `${sqlKey} ${comparator$1} ${index$1}` : "";
|
|
469
|
+
return filters;
|
|
398
470
|
}
|
|
399
471
|
|
|
400
|
-
function
|
|
401
|
-
const
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
+ limit(rows, first);
|
|
405
|
-
return { filterClause, args };
|
|
472
|
+
function logSummary(name, table, properties) {
|
|
473
|
+
const summary = generateSummary(name, table, properties);
|
|
474
|
+
log.info(`${LOGS_PREFIX}Entity "${name}" created successfully`);
|
|
475
|
+
log.info(`${LOGS_PREFIX}Entity Summary:\n${summary}`);
|
|
406
476
|
}
|
|
407
|
-
function
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
477
|
+
function generateSummary(name, table, properties) {
|
|
478
|
+
const lines = [];
|
|
479
|
+
const propLen = properties.length;
|
|
480
|
+
lines.push(`┌─ SQLEntity: "${name}" (Table: ${table})`);
|
|
481
|
+
lines.push(`├─ Total Properties: ${propLen}`);
|
|
482
|
+
const operationStats = getOperationStatistics(properties);
|
|
483
|
+
lines.push(`├─ Operation Distribution:`);
|
|
484
|
+
Object.entries(operationStats).forEach(([op, count]) => {
|
|
485
|
+
lines.push(`│ └─ ${op}: ${count} properties`);
|
|
486
|
+
});
|
|
487
|
+
lines.push(`├─ Property Details:`);
|
|
488
|
+
properties.forEach((p) => {
|
|
489
|
+
lines.push(`│ ├─ ${p.key}:`);
|
|
490
|
+
lines.push(`│ │ ├─ Type: ${p.type}`);
|
|
491
|
+
lines.push(`│ │ ├─ Min: ${p.min}`);
|
|
492
|
+
lines.push(`│ │ ├─ Max: ${p.max}`);
|
|
493
|
+
lines.push(`│ │ ├─ Required: ${p.required}`);
|
|
494
|
+
lines.push(`│ │ ├─ Safe: ${p.safe}`);
|
|
495
|
+
lines.push(`│ │ ├─ TypeCheck: ${p.typeCheck}`);
|
|
496
|
+
lines.push(`│ │ ├─ Filter: ${p.filter}`);
|
|
497
|
+
lines.push(`│ │ ├─ Methods: [${p.methods.join(', ')}]`);
|
|
498
|
+
lines.push(`│ │ ├─ Operations: [${p.operations.join(', ')}]`);
|
|
499
|
+
lines.push(`│ │ ├─ Sanitize: ${p.sanitize}`);
|
|
500
|
+
lines.push(`│ │ ├─ Normalize: ${p.normalize}`);
|
|
501
|
+
lines.push(`│ │ ├─ Validate: ${p.validate}`);
|
|
502
|
+
});
|
|
503
|
+
const crudMappings = getCrudMappings(properties);
|
|
504
|
+
lines.push(`├─ CRUD Mappings:`);
|
|
505
|
+
Object.entries(crudMappings).forEach(([operation, props]) => {
|
|
506
|
+
lines.push(`│ ├─ ${operation}: [${props.join(', ')}]`);
|
|
507
|
+
});
|
|
508
|
+
lines.push(`└─ Entity initialization completed`);
|
|
509
|
+
return lines.join('\n');
|
|
412
510
|
}
|
|
413
|
-
function
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
511
|
+
function getOperationStatistics(properties) {
|
|
512
|
+
const stats = {};
|
|
513
|
+
properties.forEach(prop => {
|
|
514
|
+
prop.operations.forEach(op => {
|
|
515
|
+
stats[op] = (stats[op] || 0) + 1;
|
|
516
|
+
});
|
|
517
|
+
});
|
|
518
|
+
return stats;
|
|
418
519
|
}
|
|
419
|
-
function
|
|
420
|
-
|
|
520
|
+
function getCrudMappings(properties) {
|
|
521
|
+
const mappings = {
|
|
522
|
+
'SELECT': [],
|
|
523
|
+
'INSERT': [],
|
|
524
|
+
'UPDATE': [],
|
|
525
|
+
'DELETE': []
|
|
526
|
+
};
|
|
527
|
+
properties.forEach(prop => {
|
|
528
|
+
const p = prop;
|
|
529
|
+
prop.operations.forEach(op => {
|
|
530
|
+
if (mappings[op]) {
|
|
531
|
+
mappings[op].push(p.key);
|
|
532
|
+
}
|
|
533
|
+
});
|
|
534
|
+
});
|
|
535
|
+
return mappings;
|
|
421
536
|
}
|
|
422
537
|
|
|
423
538
|
var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
@@ -436,8 +551,8 @@ class SQLEntity extends Entity {
|
|
|
436
551
|
this.ins = new Insert();
|
|
437
552
|
this.upd = new Update();
|
|
438
553
|
this.query = {
|
|
439
|
-
select: (paginate) => {
|
|
440
|
-
return this.sel.query(this.table, paginate);
|
|
554
|
+
select: (paginate, first = 0, rows = null, sortField = null, sortOrder = null, filters = null) => {
|
|
555
|
+
return this.sel.query(this.table, paginate, first, rows, sortField, sortOrder, filters);
|
|
441
556
|
},
|
|
442
557
|
update: (rows, consumerId, consumerName) => {
|
|
443
558
|
return this.upd.query(this.table, rows, consumerId, consumerName);
|
|
@@ -460,15 +575,14 @@ class SQLEntity extends Entity {
|
|
|
460
575
|
const rows = b.rows || null;
|
|
461
576
|
const sortField = b.sortField || null;
|
|
462
577
|
const sortOrder = b.sortOrder === -1 || b.sortOrder === "DESC" ? "DESC" : "ASC";
|
|
463
|
-
const filters =
|
|
578
|
+
const filters = cleanFilters(b.filters, this.properties || []) || null;
|
|
464
579
|
const pagination = b.pagination || false;
|
|
465
580
|
const dbClient = l.dbClient || null;
|
|
466
581
|
log.debug(`get(first='${first}', rows='${rows}',
|
|
467
582
|
sortOrder='${sortOrder}', sortField='${sortField}',
|
|
468
583
|
pagination=${pagination}, filters=${JSON.stringify(filters)}`);
|
|
469
|
-
const {
|
|
470
|
-
|
|
471
|
-
this.sel.execute(q, args, dbClient)
|
|
584
|
+
const { query, args } = this.sel.query(this._table, pagination, first, rows, sortField, sortOrder, filters);
|
|
585
|
+
this.sel.execute(query, args, dbClient)
|
|
472
586
|
.then((r) => {
|
|
473
587
|
l.rows = r.rows;
|
|
474
588
|
l.total = r.total;
|
|
@@ -482,7 +596,7 @@ class SQLEntity extends Entity {
|
|
|
482
596
|
const dbClient = l.dbClient || null;
|
|
483
597
|
const cId = l.consumerId;
|
|
484
598
|
const cName = l.consumerName;
|
|
485
|
-
log.debug(
|
|
599
|
+
log.debug(`${LOGS_PREFIX}addMany(rows=${rows.length}, consumerId=${cId})`);
|
|
486
600
|
const rtn = this.ins.rtn("id");
|
|
487
601
|
const chunks = chunk(rows);
|
|
488
602
|
for (const c of chunks) {
|
|
@@ -508,7 +622,7 @@ class SQLEntity extends Entity {
|
|
|
508
622
|
const dbClient = l.dbClient || null;
|
|
509
623
|
const cId = l.consumerId;
|
|
510
624
|
const cName = l.consumerName;
|
|
511
|
-
log.debug(
|
|
625
|
+
log.debug(`${LOGS_PREFIX}update(rows=${rows.length}, consumerId=${cId})`);
|
|
512
626
|
const chunks = chunk(rows);
|
|
513
627
|
for (const c of chunks) {
|
|
514
628
|
const { query, args } = this.upd.query(this._table, c, cId, cName);
|
|
@@ -527,7 +641,7 @@ class SQLEntity extends Entity {
|
|
|
527
641
|
const dbClient = l.dbClient || null;
|
|
528
642
|
const cId = l.consumerId;
|
|
529
643
|
const cName = l.consumerName;
|
|
530
|
-
log.debug(
|
|
644
|
+
log.debug(`${LOGS_PREFIX}archive(rows=${rows.length}, consumerId=${cId})`);
|
|
531
645
|
rows = rows.map((id) => (Object.assign(Object.assign({}, id), { archived: true })));
|
|
532
646
|
const chunks = chunk(rows);
|
|
533
647
|
for (const c of chunks) {
|
|
@@ -544,58 +658,37 @@ class SQLEntity extends Entity {
|
|
|
544
658
|
this.delete = (req, res, next) => {
|
|
545
659
|
const date = req.body.date;
|
|
546
660
|
const dbClient = res.locals.dbClient || null;
|
|
547
|
-
log.debug(
|
|
661
|
+
log.debug(`${LOGS_PREFIX}delete archived`);
|
|
548
662
|
const q = query(this._table);
|
|
549
663
|
execute(date, q, dbClient)
|
|
550
664
|
.then(() => next())
|
|
551
665
|
.catch((err) => next(err));
|
|
552
666
|
};
|
|
553
667
|
this._table = name;
|
|
668
|
+
log.info(`${LOGS_PREFIX}Creating SQLEntity: "${name}"`);
|
|
554
669
|
for (const p of properties) {
|
|
555
|
-
this.mapProps(p.
|
|
670
|
+
this.mapProps(p.operations, p.key);
|
|
556
671
|
}
|
|
672
|
+
logSummary(name, this._table, properties);
|
|
557
673
|
}
|
|
558
674
|
get table() {
|
|
559
675
|
return this._table;
|
|
560
676
|
}
|
|
561
677
|
set table(table) {
|
|
562
678
|
if (!isString(table, "!0"))
|
|
563
|
-
throw new Error(
|
|
679
|
+
throw new Error(`${LOGS_PREFIX}table must be a string of length > 0`);
|
|
564
680
|
this._table = table;
|
|
565
681
|
}
|
|
566
|
-
|
|
567
|
-
for (const
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
if (!prop) {
|
|
571
|
-
log.warn(`Filters: skipping unknown property: ${k}`);
|
|
572
|
-
delete filters[k];
|
|
573
|
-
continue;
|
|
574
|
-
}
|
|
575
|
-
const type$1 = type(prop.type);
|
|
576
|
-
const { matchMode: matchMode$1 } = filters[k];
|
|
577
|
-
if (!matchMode$1 || !matchMode(type$1, matchMode$1)) {
|
|
578
|
-
log.warn(`Filters: skipping invalid match mode: "${matchMode$1}" for type: "${type$1}" at property: "${k}"`);
|
|
579
|
-
delete filters[k];
|
|
580
|
-
continue;
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
return filters;
|
|
585
|
-
}
|
|
586
|
-
mapProps(methods, key) {
|
|
587
|
-
for (const m of methods) {
|
|
588
|
-
switch (m) {
|
|
589
|
-
case "GET":
|
|
682
|
+
mapProps(operations, key) {
|
|
683
|
+
for (const o of operations) {
|
|
684
|
+
switch (o) {
|
|
685
|
+
case "SELECT":
|
|
590
686
|
this.sel.addProp(key);
|
|
591
687
|
break;
|
|
592
|
-
case "
|
|
593
|
-
this.upd.addProp(key);
|
|
594
|
-
break;
|
|
595
|
-
case "PUT":
|
|
688
|
+
case "UPDATE":
|
|
596
689
|
this.upd.addProp(key);
|
|
597
690
|
break;
|
|
598
|
-
case "
|
|
691
|
+
case "INSERT":
|
|
599
692
|
this.ins.addProp(key);
|
|
600
693
|
break;
|
|
601
694
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dwtechs/antity-pgsql",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Open source library
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Open source library to add PostgreSQL support to @dwtechs/Antity entities.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"entities"
|
|
7
7
|
],
|
|
@@ -36,10 +36,10 @@
|
|
|
36
36
|
"dist/"
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@dwtechs/checkard": "3.
|
|
40
|
-
"@dwtechs/winstan": "0.
|
|
41
|
-
"@dwtechs/antity": "0.
|
|
42
|
-
"@dwtechs/sparray": "0.2.
|
|
39
|
+
"@dwtechs/checkard": "3.6.0",
|
|
40
|
+
"@dwtechs/winstan": "0.5.0",
|
|
41
|
+
"@dwtechs/antity": "0.13.0",
|
|
42
|
+
"@dwtechs/sparray": "0.2.1",
|
|
43
43
|
"pg": "8.13.1"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|