@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 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 = "select" | "insert" | "update" | "merge" | "delete";
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: (paginate: boolean) => string;
167
- update: (rows: Record<string, unknown>[], consumerId: number | string, consumerName: string) => {
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: unknown[];
170
- };
171
- insert: (rows: Record<string, unknown>[], consumerId: number | string, consumerName: string, rtn?: string) => {
172
- query: string;
173
- args: unknown[];
174
- };
175
- delete: () => string;
176
- return: (prop: string) => string;
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.
@@ -24,8 +24,9 @@ SOFTWARE.
24
24
  https://github.com/DWTechs/Antity-pgsql.js
25
25
  */
26
26
 
27
- import { Entity, Property } from "@dwtechs/antity";
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: (paginate: boolean) => string;
80
- update: (rows: Record<string, unknown>[], consumerId: number | string, consumerName: string) => {
81
- query: string;
82
- args: unknown[];
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
- insert: (rows: Record<string, unknown>[], consumerId: number | string, consumerName: string, rtn?: string) => {
85
- query: string;
86
- args: unknown[];
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;
@@ -24,79 +24,12 @@ SOFTWARE.
24
24
  https://github.com/DWTechs/Antity-pgsql.js
25
25
  */
26
26
 
27
- import { isIn, isArray, isString } from '@dwtechs/checkard';
28
- import { deleteProps, add as add$1, chunk, flatten } from '@dwtechs/sparray';
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(`Pgsql: { Query : '${q}', Args : '${a}' }`);
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
- return `SELECT ${c}${p} FROM ${quoteIfUppercase(table)}`;
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 = ["consumerId", "consumerName"];
212
- this._cols = "*";
213
- this._nbProps = 2;
256
+ this._props = [];
257
+ this._nbProps = 0;
258
+ this._cols = "";
214
259
  }
215
260
  addProp(prop) {
216
- this._props = add$1(this._props, quoteIfUppercase(prop), this._props.length - 2);
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
- let query = `INSERT INTO ${quoteIfUppercase(table)} (${this._cols}) VALUES `;
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
- row.consumerId = consumerId;
226
- row.consumerName = consumerName;
227
- query += `${$i(this._nbProps, i)}, `;
228
- for (const prop of this._props) {
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 += this._nbProps;
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 = ["consumerId", "consumerName"];
312
+ this._props = [];
258
313
  }
259
314
  addProp(prop) {
260
- this._props = add$1(this._props, quoteIfUppercase(prop), this._props.length - 2);
315
+ this._props.push(quoteIfUppercase(prop));
261
316
  }
262
317
  query(table, rows, consumerId, consumerName) {
263
- rows = this.addConsumer(rows, consumerId, consumerName);
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 this._props) {
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 index(index, matchMode) {
319
- const i = index.map((i) => `$${i}`);
320
- switch (matchMode) {
321
- case "startsWith":
322
- return `${i}%`;
323
- case "endsWith":
324
- return `%${i}`;
325
- case "contains":
326
- return `%${i}%`;
327
- case "notContains":
328
- return `%${i}%`;
329
- case "in":
330
- return `(${i})`;
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 `${i}`;
433
+ return s;
333
434
  }
334
435
  }
335
436
 
336
- function comparator(matchMode) {
337
- switch (matchMode) {
338
- case "startsWith":
339
- return "LIKE";
340
- case "endsWith":
341
- return "LIKE";
342
- case "contains":
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 add(filters) {
374
- const conditions = [];
375
- const args = [];
376
- if (filters) {
377
- let i = 1;
378
- for (const k in filters) {
379
- const { value, matchMode } = filters[k];
380
- const indexes = isArray(value) ? value.map(() => i++) : [i++];
381
- const cond = addOne(k, indexes, matchMode);
382
- if (cond) {
383
- conditions.push(cond);
384
- if (isArray(value))
385
- args.push(...value);
386
- else
387
- args.push(value);
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 { conditions, args };
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 filter(first, rows, sortField, sortOrder, filters) {
401
- const { conditions, args } = add(filters);
402
- const filterClause = where(conditions)
403
- + orderBy(sortField, sortOrder)
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 where(conditions, operator = "AND") {
408
- if (!conditions.length)
409
- return "";
410
- const c = conditions.join(` ${operator} `).trim();
411
- return ` WHERE ${c}`;
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 orderBy(sortField, sortOrder) {
414
- if (!sortField)
415
- return "";
416
- const o = sortOrder || "ASC";
417
- return ` ORDER BY ${quoteIfUppercase(sortField)} ${o}`;
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 limit(rows, first) {
420
- return rows ? ` LIMIT ${rows} OFFSET ${first}` : "";
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 = this.cleanFilters(b.filters) || null;
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 { filterClause, args } = filter(first, rows, sortField, sortOrder, filters);
470
- const q = this.sel.query(this._table, pagination) + filterClause;
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(`addMany(rows=${rows.length}, consumerId=${cId})`);
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(`update(rows=${rows.length}, consumerId=${cId})`);
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(`archive(rows=${rows.length}, consumerId=${cId})`);
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(`delete archived`);
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.methods, p.key);
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('table must be a string of length > 0');
679
+ throw new Error(`${LOGS_PREFIX}table must be a string of length > 0`);
564
680
  this._table = table;
565
681
  }
566
- cleanFilters(filters) {
567
- for (const k in filters) {
568
- if (filters.hasOwnProperty(k)) {
569
- const prop = this.getProp(k);
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 "PATCH":
593
- this.upd.addProp(key);
594
- break;
595
- case "PUT":
688
+ case "UPDATE":
596
689
  this.upd.addProp(key);
597
690
  break;
598
- case "POST":
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.3.3",
4
- "description": "Open source library for easy entity management",
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.2.3",
40
- "@dwtechs/winstan": "0.4.0",
41
- "@dwtechs/antity": "0.11.1",
42
- "@dwtechs/sparray": "0.2.0",
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": {