prostgles-server 4.2.46 → 4.2.47

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.
Files changed (53) hide show
  1. package/dist/AuthHandler.d.ts.map +1 -1
  2. package/dist/AuthHandler.js +2 -0
  3. package/dist/AuthHandler.js.map +1 -1
  4. package/dist/DboBuilder/QueryBuilder/getNewQuery.d.ts +1 -0
  5. package/dist/DboBuilder/QueryBuilder/getNewQuery.d.ts.map +1 -1
  6. package/dist/DboBuilder/QueryBuilder/getNewQuery.js +9 -2
  7. package/dist/DboBuilder/QueryBuilder/getNewQuery.js.map +1 -1
  8. package/dist/DboBuilder/QueryBuilder/prepareHaving.d.ts +11 -0
  9. package/dist/DboBuilder/QueryBuilder/prepareHaving.d.ts.map +1 -0
  10. package/dist/DboBuilder/QueryBuilder/prepareHaving.js +20 -0
  11. package/dist/DboBuilder/QueryBuilder/prepareHaving.js.map +1 -0
  12. package/dist/DboBuilder/TableHandler/TableHandler.d.ts +1 -1
  13. package/dist/DboBuilder/TableHandler/TableHandler.d.ts.map +1 -1
  14. package/dist/DboBuilder/TableHandler/TableHandler.js +2 -28
  15. package/dist/DboBuilder/TableHandler/TableHandler.js.map +1 -1
  16. package/dist/DboBuilder/TableHandler/upsert.d.ts +6 -0
  17. package/dist/DboBuilder/TableHandler/upsert.d.ts.map +1 -0
  18. package/dist/DboBuilder/TableHandler/upsert.js +34 -0
  19. package/dist/DboBuilder/TableHandler/upsert.js.map +1 -0
  20. package/dist/DboBuilder/ViewHandler/ViewHandler.d.ts.map +1 -1
  21. package/dist/DboBuilder/ViewHandler/ViewHandler.js +11 -15
  22. package/dist/DboBuilder/ViewHandler/ViewHandler.js.map +1 -1
  23. package/dist/DboBuilder/find.d.ts.map +1 -1
  24. package/dist/DboBuilder/find.js +18 -11
  25. package/dist/DboBuilder/find.js.map +1 -1
  26. package/dist/DboBuilder/getCondition.d.ts.map +1 -1
  27. package/dist/DboBuilder/getCondition.js +8 -4
  28. package/dist/DboBuilder/getCondition.js.map +1 -1
  29. package/dist/Filtering.d.ts +3 -3
  30. package/dist/Filtering.d.ts.map +1 -1
  31. package/dist/Filtering.js +22 -14
  32. package/dist/Filtering.js.map +1 -1
  33. package/examples/server/typescript/index.ts +0 -2
  34. package/lib/AuthHandler.ts +1 -0
  35. package/lib/DboBuilder/QueryBuilder/getNewQuery.ts +9 -2
  36. package/lib/DboBuilder/QueryBuilder/prepareHaving.ts +23 -0
  37. package/lib/DboBuilder/TableHandler/TableHandler.ts +2 -26
  38. package/lib/DboBuilder/TableHandler/upsert.ts +32 -0
  39. package/lib/DboBuilder/ViewHandler/ViewHandler.ts +11 -15
  40. package/lib/DboBuilder/find.ts +20 -9
  41. package/lib/DboBuilder/getCondition.ts +8 -4
  42. package/lib/Filtering.ts +27 -13
  43. package/package.json +9 -9
  44. package/tests/client/package-lock.json +8 -8
  45. package/tests/client/package.json +1 -1
  46. package/tests/clientRestApi.spec.ts +30 -29
  47. package/tests/isomorphicQueries.spec.ts +22 -10
  48. package/tests/server/package-lock.json +17 -17
  49. package/examples/server/typescript/DBoGenerated.d.ts +0 -168
  50. package/examples/server/typescript/DBoGenerated.d.ts.map +0 -1
  51. package/examples/server/typescript/DBoGenerated.js +0 -5
  52. package/examples/server/typescript/DBoGenerated.js.map +0 -1
  53. package/examples/server/typescript/DBoGenerated.ts +0 -125
@@ -13,7 +13,11 @@ export const find = async function(this: ViewHandler, filter?: Filter, selectPar
13
13
  try {
14
14
  await this._log({ command: "find", localParams, data: { filter, selectParams } });
15
15
  filter = filter || {};
16
- const allowedReturnTypes = Object.keys({ row: 1, statement: 1, value: 1, values: 1, "statement-no-rls": 1, "statement-where": 1 } satisfies Record<Required<SelectParams>["returnType"], 1>);
16
+ const allowedReturnTypes = Object.keys({
17
+ row: 1, statement: 1, value: 1, values: 1,
18
+ "statement-no-rls": 1, "statement-where": 1,
19
+ } satisfies Record<Required<SelectParams>["returnType"], 1>);
20
+
17
21
  const { returnType } = selectParams || {};
18
22
  if (returnType && !allowedReturnTypes.includes(returnType)) {
19
23
  throw `returnType (${returnType}) can only be ${allowedReturnTypes.join(" OR ")}`
@@ -23,12 +27,13 @@ export const find = async function(this: ViewHandler, filter?: Filter, selectPar
23
27
 
24
28
  if (testRule) return [];
25
29
  if (selectParams) {
26
- const good_params = Object.keys({
27
- "select": 1, "orderBy": 1, "offset": 1, "limit": 1, "returnType": 1, "groupBy": 1
30
+ const validParamNames = Object.keys({
31
+ "select": 1, "orderBy": 1, "offset": 1, "limit": 1,
32
+ "returnType": 1, "groupBy": 1, "having": 1
28
33
  } satisfies Record<keyof SelectParams, 1>);
29
34
 
30
- const bad_params = Object.keys(selectParams).filter(k => !good_params.includes(k as any));
31
- if (bad_params && bad_params.length) throw "Invalid params: " + bad_params.join(", ") + " \n Expecting: " + good_params.join(", ");
35
+ const invalidParams = Object.keys(selectParams).filter(k => !validParamNames.includes(k as any));
36
+ if (invalidParams && invalidParams.length) throw "Invalid params: " + invalidParams.join(", ") + " \n Expecting: " + validParamNames.join(", ");
32
37
  }
33
38
 
34
39
  /* Validate publish */
@@ -38,9 +43,15 @@ export const find = async function(this: ViewHandler, filter?: Filter, selectPar
38
43
  const fields = tableRules.select.fields;
39
44
  const maxLimit = tableRules.select.maxLimit;
40
45
 
41
- if (<any>tableRules.select !== "*" && typeof tableRules.select !== "boolean" && !isObject(tableRules.select)) throw `\nINVALID publish.${this.name}.select\nExpecting any of: "*" | { fields: "*" } | true | false`
42
- if (!fields) throw ` invalid ${this.name}.select rule -> fields (required) setting missing.\nExpecting any of: "*" | { col_name: false } | { col1: true, col2: true }`;
43
- if (maxLimit && !Number.isInteger(maxLimit)) throw ` invalid publish.${this.name}.select.maxLimit -> expecting integer but got ` + maxLimit;
46
+ if (<any>tableRules.select !== "*" && typeof tableRules.select !== "boolean" && !isObject(tableRules.select)) {
47
+ throw `\nInvalid publish.${this.name}.select\nExpecting any of: "*" | { fields: "*" } | true | false`;
48
+ }
49
+ if (!fields) {
50
+ throw ` invalid ${this.name}.select rule -> fields (required) setting missing.\nExpecting any of: "*" | { col_name: false } | { col1: true, col2: true }`;
51
+ }
52
+ if (maxLimit && !Number.isInteger(maxLimit)) {
53
+ throw ` invalid publish.${this.name}.select.maxLimit -> expecting integer but got ` + maxLimit;
54
+ }
44
55
  }
45
56
 
46
57
  const _selectParams = selectParams ?? {}
@@ -68,7 +79,7 @@ export const find = async function(this: ViewHandler, filter?: Filter, selectPar
68
79
  return [];
69
80
  } catch (e) {
70
81
  console.error(e);
71
- throw `INTERNAL ERROR: Publish config is not valid for publish.${this.name}.select `
82
+ throw `Internal error: publish config is not valid for publish.${this.name}.select `
72
83
  }
73
84
  }
74
85
 
@@ -57,7 +57,7 @@ export async function getCondition(
57
57
  existsCond = (await Promise.all(existsConfigs.map(async existsConfig => await getExistsCondition.bind(this)(existsConfig, localParams)))).join(" AND ");
58
58
  }
59
59
 
60
- /* Computed field queries */
60
+ /* Computed field queries ($rowhash) */
61
61
  const p = this.getValidatedRules(tableRules, localParams);
62
62
  const computedFields = p.allColumns.filter(c => c.type === "computed");
63
63
  const computedColConditions: string[] = [];
@@ -85,8 +85,8 @@ export async function getCondition(
85
85
  if (select) {
86
86
  /* Allow filtering by selected fields/funcs */
87
87
  allowedSelect = select.filter(s => {
88
- /* */
89
88
  if (["function", "computed", "column"].includes(s.type)) {
89
+ /* */
90
90
  if (s.type !== "column" || allowed_colnames.includes(s.alias)) {
91
91
  return true;
92
92
  }
@@ -202,6 +202,10 @@ export async function getCondition(
202
202
  ));
203
203
 
204
204
  if (invalidColumn) {
205
+ const selItem = select?.find(s => s.alias === invalidColumn);
206
+ if(selItem?.type === "aggregation"){
207
+ throw new Error(`Filtering by ${invalidColumn} is not allowed. Aggregations cannot be filtered. Use HAVING clause instead.`);
208
+ }
205
209
  const allowedCols = allowedSelect.map(s => s.type === "column" ? s.getQuery() : s.alias).join(", ");
206
210
  const errMessage = `Table: ${this.name} -> disallowed/inexistent columns in filter: ${invalidColumn} \n Expecting one of: ${allowedCols}`;
207
211
  throw errMessage;
@@ -214,8 +218,8 @@ export async function getCondition(
214
218
  const q = parseFilterItem({
215
219
  filter: f,
216
220
  tableAlias,
217
- pgp,
218
- select: allowedSelect
221
+ select: allowedSelect,
222
+ allowedColumnNames: !tableRules? this.column_names.slice(0) : this.parseFieldFilter(tableRules.select?.filterFields ?? tableRules.select?.fields),
219
223
  });
220
224
 
221
225
  let templates: string[] = [q].filter(q => q);
package/lib/Filtering.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  import {
4
4
  CompareFilterKeys,
5
5
  CompareInFilterKeys,
6
- EXISTS_KEYS, FilterDataType,
6
+ FilterDataType,
7
7
  FullFilter,
8
8
  GeomFilterKeys, GeomFilter_Funcs,
9
9
  JsonbFilterKeys,
@@ -14,14 +14,21 @@ import {
14
14
  isObject
15
15
  } from "prostgles-types";
16
16
  import { SelectItem } from "./DboBuilder/QueryBuilder/QueryBuilder";
17
+ import { pgp } from "./DboBuilder/DboBuilderTypes";
17
18
 
18
19
  /**
19
20
  * Parse a single filter
20
21
  * Ensure only single key objects reach this point
21
22
  */
22
- type ParseFilterItemArgs = { filter: FullFilter<void, void>, select?: SelectItem[], tableAlias?: string, pgp: any };
23
+ type ParseFilterItemArgs = {
24
+ filter: FullFilter<void, void>;
25
+ select: SelectItem[] | undefined;
26
+ tableAlias: string | undefined;
27
+ allowedColumnNames: string[];
28
+ };
29
+
23
30
  export const parseFilterItem = (args: ParseFilterItemArgs): string => {
24
- const { filter: _f, select, tableAlias, pgp } = args;
31
+ const { filter: _f, select, tableAlias, allowedColumnNames } = args;
25
32
 
26
33
  if(!_f || isEmpty(_f)) return "";
27
34
 
@@ -42,7 +49,7 @@ export const parseFilterItem = (args: ParseFilterItemArgs): string => {
42
49
  filter: { [fk]: _f[fk] },
43
50
  select,
44
51
  tableAlias,
45
- pgp,
52
+ allowedColumnNames,
46
53
  }))
47
54
  .sort() /* sorted to ensure duplicate subscription channels are not created due to different condition order */
48
55
  .join(" AND ")
@@ -50,17 +57,24 @@ export const parseFilterItem = (args: ParseFilterItemArgs): string => {
50
57
 
51
58
  const fKey: string = fKeys[0]!;
52
59
 
53
- /* Exists filter */
54
- if(EXISTS_KEYS.find(k => k in _f)){
55
- // parseExistsFilter()
56
- }
57
-
58
60
  let selItem: SelectItem | undefined;
59
- if(select) selItem = select.find(s => fKey === s.alias);
61
+ if(select) {
62
+ selItem = select.find(s => fKey === s.alias);
63
+ }
60
64
  let rightF: FilterDataType<any> = (_f as any)[fKey];
61
65
 
66
+ const validateSelectedItemFilter = (selectedItem: SelectItem | undefined) => {
67
+ const fields = selectedItem?.getFields();
68
+ if(Array.isArray(fields) && fields.length > 1) {
69
+ const dissallowedFields = fields.filter(fname => !allowedColumnNames.includes(fname));
70
+ if(dissallowedFields.length){
71
+ throw new Error(`Invalid/disallowed columns found in filter: ${dissallowedFields}`)
72
+ }
73
+ }
74
+ }
62
75
  const getLeftQ = (selItm: SelectItem) => {
63
- if(selItm.type === "function") return selItm.getQuery();
76
+ validateSelectedItemFilter(selItem);
77
+ if(selItm.type === "function" || selItm.type === "aggregation") return selItm.getQuery();
64
78
  return selItm.getQuery(tableAlias);
65
79
  }
66
80
 
@@ -81,6 +95,7 @@ export const parseFilterItem = (args: ParseFilterItemArgs): string => {
81
95
  selItem = select.find(s =>
82
96
  dot_notation_delims.find(delimiter => fKey.startsWith(s.alias + delimiter))
83
97
  );
98
+ validateSelectedItemFilter(selItem);
84
99
  }
85
100
  if(!selItem) {
86
101
  return mErr("Bad filter. Could not match to a column or alias or dot notation: ");
@@ -135,7 +150,6 @@ export const parseFilterItem = (args: ParseFilterItemArgs): string => {
135
150
  nextSep = undefined;
136
151
  }
137
152
 
138
- // console.log({ currSep, nextSep })
139
153
  leftQ += currSep.sep + asValue(remainingStr.slice(currSep.idx + currSep.sep.length, nextIdx));
140
154
  currSep = nextSep;
141
155
  }
@@ -175,7 +189,7 @@ export const parseFilterItem = (args: ParseFilterItemArgs): string => {
175
189
 
176
190
  rightF = res;
177
191
  } else {
178
- console.trace(141, select, selItem, remainingStr)
192
+ // console.trace(141, select, selItem, remainingStr)
179
193
  mErr("Bad filter. Could not find the valid col name or alias or col json path")
180
194
  }
181
195
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prostgles-server",
3
- "version": "4.2.46",
3
+ "version": "4.2.47",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -35,23 +35,23 @@
35
35
  ],
36
36
  "homepage": "https://prostgles.com",
37
37
  "dependencies": {
38
- "@types/express": "^4.17.13",
39
- "@types/json-schema": "^7.0.11",
40
- "@types/pg": "^8.10.9",
41
38
  "bluebird": "^3.7.2",
42
39
  "body-parser": "^1.20.2",
43
- "check-disk-space": "^3.3.1",
40
+ "check-disk-space": "^3.4.0",
44
41
  "file-type": "^18.5.0",
45
- "pg": "^8.11.3",
46
- "pg-cursor": "^2.10.3",
47
- "pg-promise": "^11.5.4",
42
+ "pg": "^8.11.5",
43
+ "pg-cursor": "^2.10.5",
44
+ "pg-promise": "^11.6.0",
48
45
  "prostgles-client": "^4.0.53",
49
- "prostgles-types": "^4.0.74"
46
+ "prostgles-types": "^4.0.75"
50
47
  },
51
48
  "devDependencies": {
52
49
  "@types/bluebird": "^3.5.36",
50
+ "@types/express": "^4.17.21",
53
51
  "@types/file-type": "^10.9.1",
52
+ "@types/json-schema": "^7.0.15",
54
53
  "@types/node": "^18.0.3",
54
+ "@types/pg": "^8.11.5",
55
55
  "@types/pg-cursor": "^2.7.2",
56
56
  "@types/sharp": "^0.30.4",
57
57
  "@types/socket.io": "^3.0.2",
@@ -10,7 +10,7 @@
10
10
  "license": "ISC",
11
11
  "dependencies": {
12
12
  "@types/node": "^20.9.2",
13
- "prostgles-client": "^4.0.115",
13
+ "prostgles-client": "^4.0.117",
14
14
  "prostgles-types": "^4.0.51",
15
15
  "socket.io-client": "^4.7.5"
16
16
  },
@@ -351,11 +351,11 @@
351
351
  }
352
352
  },
353
353
  "node_modules/prostgles-client": {
354
- "version": "4.0.115",
355
- "resolved": "https://registry.npmjs.org/prostgles-client/-/prostgles-client-4.0.115.tgz",
356
- "integrity": "sha512-YMnWOY352K8v++eR8Nb2Ikmi+3U0bxaFbPlsHAa1XmFhGq3UqRponUMcgYLiXjbWrvMRULSVWdYezdMTZU/a/g==",
354
+ "version": "4.0.117",
355
+ "resolved": "https://registry.npmjs.org/prostgles-client/-/prostgles-client-4.0.117.tgz",
356
+ "integrity": "sha512-bhA/o84sWPrlqiYFpitNRqcn1bDgNN5yOuPGSaS805t4Gm6vooKI1K392qlrskK92j6qH7VnPsKxeF4Ul7vn/g==",
357
357
  "dependencies": {
358
- "prostgles-types": "^4.0.74"
358
+ "prostgles-types": "^4.0.75"
359
359
  },
360
360
  "peerDependencies": {
361
361
  "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
@@ -371,9 +371,9 @@
371
371
  }
372
372
  },
373
373
  "node_modules/prostgles-types": {
374
- "version": "4.0.74",
375
- "resolved": "https://registry.npmjs.org/prostgles-types/-/prostgles-types-4.0.74.tgz",
376
- "integrity": "sha512-6+MW7q1INIpO3JOkSCDD8oUG3U22VYbSWt2s0j3/0Uog4Dr90EzxX2TE7jV0NXmK+AAs+HHZ9u5olipMv0sHmQ==",
374
+ "version": "4.0.75",
375
+ "resolved": "https://registry.npmjs.org/prostgles-types/-/prostgles-types-4.0.75.tgz",
376
+ "integrity": "sha512-s0v0NbLO2/w9kzsjxlC48ZyMtnUJ/5dhuH5eucAnGv41wwuJ+HjA2zsvVqBiStdEVA6AhwrWefmrO8GTkuiKhA==",
377
377
  "dependencies": {
378
378
  "json-schema": "^0.4.0"
379
379
  }
@@ -13,7 +13,7 @@
13
13
  "license": "ISC",
14
14
  "dependencies": {
15
15
  "@types/node": "^20.9.2",
16
- "prostgles-client": "^4.0.115",
16
+ "prostgles-client": "^4.0.117",
17
17
  "prostgles-types": "^4.0.51",
18
18
  "socket.io-client": "^4.7.5"
19
19
  },
@@ -7,36 +7,11 @@ export const clientRestApi = async(db: DBHandlerClient, auth: Auth, log: (...arg
7
7
 
8
8
  await describe("clientRestApi", async () => {
9
9
 
10
- const post = async ({ path, noAuth }: { path: string; noAuth?: boolean}, ...params: any[]) => {
11
- const headers = new Headers({
12
- 'Authorization': `Bearer ${Buffer.from(noAuth? "noAuth" : token, "utf-8").toString("base64")}`,
13
- 'Accept': 'application/json',
14
- 'Content-Type': 'application/json'
15
- });
16
- const res = await fetch(`http://127.0.0.1:3001/api/${path}`, {
17
- method: "POST",
18
- headers,
19
- body: !params?.length? undefined : JSON.stringify(params)
20
- });
21
- const resBodyJson = await res.text()
22
- .then(text => {
23
- try {
24
- return JSON.parse(text);
25
- } catch {
26
- return text;
27
- }
28
- });
29
-
30
- if(res.status !== 200){
31
- return Promise.reject(resBodyJson);
32
- }
33
- return resBodyJson;
34
- }
35
- const rest = async ({ tableName, command, noAuth }: { tableName: string; command: string; noAuth?: boolean; }, ...params: any[]) => post({ path: `db/${tableName}/${command}`, noAuth }, ...(params ?? []))
10
+ const rest = async ({ tableName, command, noAuth }: { tableName: string; command: string; noAuth?: boolean; }, ...params: any[]) => post({ path: `db/${tableName}/${command}`, noAuth, token }, ...(params ?? []))
36
11
  const dbRest = (tableName: string, command: string, ...params: any[]) => rest({ tableName, command }, ...(params ?? []))
37
12
  const dbRestNoAuth = (tableName: string, command: string, ...params: any[]) => rest({ tableName, command, noAuth: true }, ...(params ?? []));
38
- const sqlRest = (query: string, ...params: any[]) => post({ path: `db/sql` }, query, ...(params ?? []))
39
- const sqlMethods = (methodName: string, ...params: any[]) => post({ path: `methods/${methodName}` }, ...(params ?? []))
13
+ const sqlRest = (query: string, ...params: any[]) => post({ path: `db/sql`, token }, query, ...(params ?? []))
14
+ const sqlMethods = (methodName: string, ...params: any[]) => post({ path: `methods/${methodName}`, token }, ...(params ?? []))
40
15
 
41
16
  await test("Rest api test", async () => {
42
17
  const dataFilter = { id: 123123123, last_updated: Date.now() };
@@ -59,7 +34,7 @@ export const clientRestApi = async(db: DBHandlerClient, auth: Auth, log: (...arg
59
34
  const sqlRes = await sqlRest("select 1 as a", {}, { returnType: "rows" });
60
35
  assert.deepStrictEqual(sqlRes, [{ a: 1 }]);
61
36
 
62
- const restTableSchema = await post({ path: "schema" });
37
+ const restTableSchema = await post({ path: "schema", token });
63
38
  assert.deepStrictEqual(tableSchema, restTableSchema.tableSchema);
64
39
  await Promise.all(tableSchema.map(async tbl => {
65
40
  const cols = await db[tbl.name]?.getColumns?.();
@@ -78,4 +53,30 @@ export const clientRestApi = async(db: DBHandlerClient, auth: Auth, log: (...arg
78
53
 
79
54
 
80
55
  });
56
+ }
57
+
58
+ const post = async ({ path, noAuth, token }: { path: string; token: string; noAuth?: boolean}, ...params: any[]) => {
59
+ const headers = new Headers({
60
+ 'Authorization': `Bearer ${Buffer.from(noAuth? "noAuth" : token, "utf-8").toString("base64")}`,
61
+ 'Accept': 'application/json',
62
+ 'Content-Type': 'application/json'
63
+ });
64
+ const res = await fetch(`http://127.0.0.1:3001/api/${path}`, {
65
+ method: "POST",
66
+ headers,
67
+ body: !params?.length? undefined : JSON.stringify(params)
68
+ });
69
+ const resBodyJson = await res.text()
70
+ .then(text => {
71
+ try {
72
+ return JSON.parse(text);
73
+ } catch {
74
+ return text;
75
+ }
76
+ });
77
+
78
+ if(res.status !== 200){
79
+ return Promise.reject(resBodyJson);
80
+ }
81
+ return resBodyJson;
81
82
  }
@@ -2,7 +2,11 @@ import { strict as assert } from 'assert';
2
2
  import * as fs from "fs";
3
3
  import { DBOFullyTyped } from "../dist/DBSchemaBuilder";
4
4
  import type { DBHandlerClient } from "./client";
5
- import { test, describe } from "node:test";
5
+ import {
6
+ test,
7
+ //@ts-ignore
8
+ describe
9
+ } from "node:test";
6
10
  import { pickKeys } from "prostgles-types";
7
11
 
8
12
 
@@ -78,12 +82,20 @@ export const isomorphicQueries = async (db: DBOFullyTyped | DBHandlerClient, log
78
82
 
79
83
  await db.sql("TRUNCATE files CASCADE");
80
84
  });
85
+
86
+ await test("Having clause", async () => {
87
+ const res = await db.items.find!({}, { select: { name: 1, c: { $countAll: [] } }, having: { c: 2 } });
88
+ assert.deepStrictEqual(res, [{
89
+ c: '2',
90
+ name: 'a'
91
+ }]);
92
+ })
81
93
 
82
94
  const json = { a: true, arr: "2", arr1: 3, arr2: [1], arrStr: ["1123.string"] }
83
95
  await test("merge json", async () => {
84
96
  const inserted = await db.tjson.insert!({ colOneOf: "a", json }, { returning: "*" });
85
97
  const res = await db.tjson.update!({ colOneOf: "a" },{ json: { $merge: [{ a: false }] } }, { returning: "*" });
86
- assert.deepStrictEqual(res[0].json, { ...json, a: false });
98
+ assert.deepStrictEqual(res?.[0].json, { ...json, a: false });
87
99
  });
88
100
 
89
101
  await test("json array converted to pg array filter bug", async () => {
@@ -172,7 +184,7 @@ export const isomorphicQueries = async (db: DBOFullyTyped | DBHandlerClient, log
172
184
 
173
185
  const newF = await db.files.findOne!({ id: original.id });
174
186
 
175
- assert.equal(newF.original_name, newFile.name)
187
+ assert.equal(newF?.original_name, newFile.name)
176
188
  });
177
189
 
178
190
  await test("getColumns definition", async () => {
@@ -528,8 +540,8 @@ export const isomorphicQueries = async (db: DBOFullyTyped | DBHandlerClient, log
528
540
  await test("template_string function", async () => {
529
541
  const res = await db.various.findOne!({ name: 'abc9' }, { select: { tstr: { $template_string: ["{name} is hehe"] } } });
530
542
  const res2 = await db.various.findOne!({ name: 'abc9' }, { select: { tstr: { $template_string: ["is hehe"] } } });
531
- assert.equal(res.tstr, "abc9 is hehe")
532
- assert.equal(res2.tstr, "is hehe")
543
+ assert.equal(res?.tstr, "abc9 is hehe")
544
+ assert.equal(res2?.tstr, "is hehe")
533
545
  });
534
546
 
535
547
  await test("Between filtering", async () => {
@@ -577,8 +589,8 @@ export const isomorphicQueries = async (db: DBOFullyTyped | DBHandlerClient, log
577
589
  await test("Function example", async () => {
578
590
 
579
591
  const f = await db.items4.findOne!({}, { select: { public: 1, p_5: { $left: ["public", 3] } } });
580
- assert.equal(f.p_5.length, 3);
581
- assert.equal(f.p_5, f.public.substr(0, 3));
592
+ assert.equal(f?.p_5.length, 3);
593
+ assert.equal(f?.p_5, f.public.substr(0, 3));
582
594
 
583
595
  // Nested function
584
596
  const fg = await db.items2.findOne!({}, { select: { id: 1, name: 1, items3: { name: "$upper" } } });// { $upper: ["public"] } } });
@@ -816,7 +828,7 @@ export const isomorphicQueries = async (db: DBOFullyTyped | DBHandlerClient, log
816
828
  select: {
817
829
  "*": 1,
818
830
  items2: "*",
819
- items2j: db.leftJoin.items2({}, "*")
831
+ items2j: db.leftJoin?.items2({}, "*")
820
832
  }
821
833
  });
822
834
 
@@ -848,7 +860,7 @@ export const isomorphicQueries = async (db: DBOFullyTyped | DBHandlerClient, log
848
860
  /* $rowhash -> Custom column that returms md5(ctid + allowed select columns). Used in joins & CRUD to bypass PKey details */
849
861
  await test("$rowhash example", async () => {
850
862
  const rowhash = await db.items.findOne!({}, { select: { $rowhash: 1, "*": 1 }});
851
- const f = { $rowhash: rowhash.$rowhash };
863
+ const f = { $rowhash: rowhash?.$rowhash };
852
864
  const rowhashView = await db.v_items.findOne!({}, { select: { $rowhash: 1 }});
853
865
  const rh1 = await db.items.findOne!({ $rowhash: rowhash?.$rowhash }, { select: { $rowhash: 1 }});
854
866
  const rhView = await db.v_items.findOne!({ $rowhash: rowhashView?.$rowhash }, { select: { $rowhash: 1 }});
@@ -1076,7 +1088,7 @@ export const isomorphicQueries = async (db: DBOFullyTyped | DBHandlerClient, log
1076
1088
  {
1077
1089
  select: {
1078
1090
  "*": 1,
1079
- i0: db.innerJoin.items_multi(
1091
+ i0: db.innerJoin?.items_multi(
1080
1092
  { name: "multi0" },
1081
1093
  "*",
1082
1094
  { path: [{ table: "items", on: [{ items0_id: "id" }] }] }
@@ -21,26 +21,26 @@
21
21
  },
22
22
  "../..": {
23
23
  "name": "prostgles-server",
24
- "version": "4.2.45",
24
+ "version": "4.2.46",
25
25
  "license": "MIT",
26
26
  "dependencies": {
27
- "@types/express": "^4.17.13",
28
- "@types/json-schema": "^7.0.11",
29
- "@types/pg": "^8.10.9",
30
27
  "bluebird": "^3.7.2",
31
28
  "body-parser": "^1.20.2",
32
- "check-disk-space": "^3.3.1",
29
+ "check-disk-space": "^3.4.0",
33
30
  "file-type": "^18.5.0",
34
- "pg": "^8.11.3",
35
- "pg-cursor": "^2.10.3",
36
- "pg-promise": "^11.5.4",
31
+ "pg": "^8.11.5",
32
+ "pg-cursor": "^2.10.5",
33
+ "pg-promise": "^11.6.0",
37
34
  "prostgles-client": "^4.0.53",
38
- "prostgles-types": "^4.0.74"
35
+ "prostgles-types": "^4.0.75"
39
36
  },
40
37
  "devDependencies": {
41
38
  "@types/bluebird": "^3.5.36",
39
+ "@types/express": "^4.17.21",
42
40
  "@types/file-type": "^10.9.1",
41
+ "@types/json-schema": "^7.0.15",
43
42
  "@types/node": "^18.0.3",
43
+ "@types/pg": "^8.11.5",
44
44
  "@types/pg-cursor": "^2.7.2",
45
45
  "@types/sharp": "^0.30.4",
46
46
  "@types/socket.io": "^3.0.2",
@@ -1529,11 +1529,11 @@
1529
1529
  "version": "file:../..",
1530
1530
  "requires": {
1531
1531
  "@types/bluebird": "^3.5.36",
1532
- "@types/express": "^4.17.13",
1532
+ "@types/express": "^4.17.21",
1533
1533
  "@types/file-type": "^10.9.1",
1534
- "@types/json-schema": "^7.0.11",
1534
+ "@types/json-schema": "^7.0.15",
1535
1535
  "@types/node": "^18.0.3",
1536
- "@types/pg": "^8.10.9",
1536
+ "@types/pg": "^8.11.5",
1537
1537
  "@types/pg-cursor": "^2.7.2",
1538
1538
  "@types/sharp": "^0.30.4",
1539
1539
  "@types/socket.io": "^3.0.2",
@@ -1541,14 +1541,14 @@
1541
1541
  "@typescript-eslint/parser": "^5.62.0",
1542
1542
  "bluebird": "^3.7.2",
1543
1543
  "body-parser": "^1.20.2",
1544
- "check-disk-space": "^3.3.1",
1544
+ "check-disk-space": "^3.4.0",
1545
1545
  "eslint": "^8.51.0",
1546
1546
  "file-type": "^18.5.0",
1547
- "pg": "^8.11.3",
1548
- "pg-cursor": "^2.10.3",
1549
- "pg-promise": "^11.5.4",
1547
+ "pg": "^8.11.5",
1548
+ "pg-cursor": "^2.10.5",
1549
+ "pg-promise": "^11.6.0",
1550
1550
  "prostgles-client": "^4.0.53",
1551
- "prostgles-types": "^4.0.74",
1551
+ "prostgles-types": "^4.0.75",
1552
1552
  "typescript": "^5.3.3"
1553
1553
  }
1554
1554
  },