prostgles-server 4.2.34 → 4.2.36

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 (48) hide show
  1. package/dist/DboBuilder/QueryBuilder/QueryBuilder.d.ts +2 -1
  2. package/dist/DboBuilder/QueryBuilder/QueryBuilder.d.ts.map +1 -1
  3. package/dist/DboBuilder/QueryBuilder/QueryBuilder.js +1 -2
  4. package/dist/DboBuilder/QueryBuilder/QueryBuilder.js.map +1 -1
  5. package/dist/DboBuilder/QueryBuilder/getNewQuery.d.ts +69 -69
  6. package/dist/DboBuilder/QueryBuilder/getNewQuery.d.ts.map +1 -1
  7. package/dist/DboBuilder/QueryBuilder/getNewQuery.js +4 -8
  8. package/dist/DboBuilder/QueryBuilder/getNewQuery.js.map +1 -1
  9. package/dist/DboBuilder/dboBuilderUtils.js +1 -1
  10. package/dist/DboBuilder/dboBuilderUtils.js.map +1 -1
  11. package/dist/DboBuilder/getCondition.d.ts.map +1 -1
  12. package/dist/DboBuilder/getCondition.js +12 -12
  13. package/dist/DboBuilder/getCondition.js.map +1 -1
  14. package/dist/DboBuilder/getSubscribeRelatedTables.d.ts +4 -8
  15. package/dist/DboBuilder/getSubscribeRelatedTables.d.ts.map +1 -1
  16. package/dist/DboBuilder/getSubscribeRelatedTables.js +5 -4
  17. package/dist/DboBuilder/getSubscribeRelatedTables.js.map +1 -1
  18. package/dist/DboBuilder/runSQL.d.ts +2 -2
  19. package/dist/DboBuilder/runSQL.d.ts.map +1 -1
  20. package/dist/DboBuilder/runSQL.js +3 -3
  21. package/dist/DboBuilder/runSQL.js.map +1 -1
  22. package/dist/DboBuilder/subscribe.d.ts +1 -1
  23. package/dist/DboBuilder/subscribe.d.ts.map +1 -1
  24. package/dist/DboBuilder/subscribe.js +10 -5
  25. package/dist/DboBuilder/subscribe.js.map +1 -1
  26. package/dist/Filtering.d.ts.map +1 -1
  27. package/dist/Filtering.js +0 -4
  28. package/dist/Filtering.js.map +1 -1
  29. package/dist/PubSubManager/PubSubManager.d.ts +4 -5
  30. package/dist/PubSubManager/PubSubManager.d.ts.map +1 -1
  31. package/dist/PubSubManager/PubSubManager.js +9 -37
  32. package/dist/PubSubManager/PubSubManager.js.map +1 -1
  33. package/lib/DboBuilder/QueryBuilder/QueryBuilder.ts +3 -3
  34. package/lib/DboBuilder/QueryBuilder/getNewQuery.ts +4 -10
  35. package/lib/DboBuilder/dboBuilderUtils.ts +1 -1
  36. package/lib/DboBuilder/getCondition.ts +28 -26
  37. package/lib/DboBuilder/getSubscribeRelatedTables.ts +6 -10
  38. package/lib/DboBuilder/runSQL.ts +5 -5
  39. package/lib/DboBuilder/subscribe.ts +14 -12
  40. package/lib/Filtering.ts +0 -4
  41. package/lib/PubSubManager/PubSubManager.ts +12 -40
  42. package/package.json +2 -2
  43. package/tests/client/hooks.spec.ts +119 -0
  44. package/tests/client/index.ts +3 -2
  45. package/tests/client/package-lock.json +1590 -159
  46. package/tests/client/package.json +4 -1
  47. package/tests/isomorphicQueries.spec.ts +0 -3
  48. package/tests/server/package-lock.json +3 -3
@@ -81,7 +81,7 @@ export async function getCondition(
81
81
  });
82
82
 
83
83
  let allowedSelect: SelectItem[] = [];
84
- /* Select aliases take precedence over col names. This is to ensure filters work correctly and even on computed cols*/
84
+ /* Select aliases take precedence over col names. This is to ensure filters work correctly even on computed cols*/
85
85
  if (select) {
86
86
  /* Allow filtering by selected fields/funcs */
87
87
  allowedSelect = select.filter(s => {
@@ -96,23 +96,24 @@ export async function getCondition(
96
96
  }
97
97
 
98
98
  /* Add remaining allowed fields */
99
+ const remainingNonSelectedColumns: SelectItem[] = p.allColumns.filter(c =>
100
+ allowed_colnames.includes(c.name) &&
101
+ !allowedSelect.find(s => s.alias === c.name)
102
+ ).map(f => ({
103
+ type: f.type,
104
+ alias: f.name,
105
+ columnName: f.type === "column"? f.name : undefined as any,
106
+ getQuery: (tableAlias) => f.getQuery({
107
+ tableAlias,
108
+ allColumns: this.columns,
109
+ allowedFields: allowed_colnames
110
+ }),
111
+ selected: false,
112
+ getFields: () => [f.name],
113
+ column_udt_type: f.type === "column" ? this.columns.find(c => c.name === f.name)?.udt_name : undefined
114
+ }))
99
115
  allowedSelect = allowedSelect.concat(
100
- p.allColumns.filter(c =>
101
- allowed_colnames.includes(c.name) &&
102
- !allowedSelect.find(s => s.alias === c.name)
103
- ).map(f => ({
104
- type: f.type,
105
- alias: f.name,
106
- columnName: f.type === "column"? f.name : undefined as any,
107
- getQuery: (tableAlias) => f.getQuery({
108
- tableAlias,
109
- allColumns: this.columns,
110
- allowedFields: allowed_colnames
111
- }),
112
- selected: false,
113
- getFields: () => [f.name],
114
- column_udt_type: f.type === "column" ? this.columns.find(c => c.name === f.name)?.udt_name : undefined
115
- }))
116
+ remainingNonSelectedColumns
116
117
  );
117
118
 
118
119
  /* Parse complex filters
@@ -180,15 +181,14 @@ export async function getCondition(
180
181
  will make an exists filter
181
182
  */
182
183
 
183
- const filterKeys = Object.keys(filter).filter(k => k !== complexFilterKey && !funcFilter.find(ek => ek.name === k) && !computedFields.find(cf => cf.name === k) && !existsConfigs.find(ek => ek.existType === k));
184
- // if(allowed_colnames){
185
- // const aliasedColumns = (select || []).filter(s =>
186
- // ["function", "computed", "column"].includes(s.type) && allowed_colnames.includes(s.alias) ||
187
- // s.getFields().find(f => allowed_colnames.includes(f))
188
- // ).map(s => s.alias);
189
- // const validCols = [...allowed_colnames, ...aliasedColumns];
184
+ const filterKeys = Object.keys(filter)
185
+ .filter(k =>
186
+ k !== complexFilterKey &&
187
+ !funcFilter.find(ek => ek.name === k) &&
188
+ !computedFields.find(cf => cf.name === k) &&
189
+ !existsConfigs.find(ek => ek.existType === k)
190
+ );
190
191
 
191
- // }
192
192
  const validFieldNames = allowedSelect.map(s => s.alias);
193
193
  const invalidColumn = filterKeys
194
194
  .find(fName => !validFieldNames.find(c =>
@@ -202,7 +202,9 @@ export async function getCondition(
202
202
  ));
203
203
 
204
204
  if (invalidColumn) {
205
- throw `Table: ${this.name} -> disallowed/inexistent columns in filter: ${invalidColumn} \n Expecting one of: ${allowedSelect.map(s => s.type === "column" ? s.getQuery() : s.alias).join(", ")}`;
205
+ const allowedCols = allowedSelect.map(s => s.type === "column" ? s.getQuery() : s.alias).join(", ");
206
+ const errMessage = `Table: ${this.name} -> disallowed/inexistent columns in filter: ${invalidColumn} \n Expecting one of: ${allowedCols}`;
207
+ throw errMessage;
206
208
  }
207
209
 
208
210
  /* TODO: Allow filter funcs */
@@ -1,7 +1,7 @@
1
1
  import { AnyObject, asName, reverseParsedPath, SubscribeParams } from "prostgles-types";
2
- import { ExistsFilterConfig, Filter, LocalParams, getClientErrorFromPGError } from "./DboBuilder";
3
2
  import { TableRule } from "../PublishParser/PublishParser";
4
3
  import { log, ViewSubscriptionOptions } from "../PubSubManager/PubSubManager";
4
+ import { Filter, getClientErrorFromPGError, LocalParams } from "./DboBuilder";
5
5
  import { NewQuery } from "./QueryBuilder/QueryBuilder";
6
6
  import { ViewHandler } from "./ViewHandler/ViewHandler";
7
7
 
@@ -10,17 +10,14 @@ type Args = {
10
10
  filter: Filter;
11
11
  table_rules: TableRule<AnyObject, void> | undefined;
12
12
  localParams: LocalParams | undefined;
13
- condition: string;
14
- filterOpts: {
15
- where: string;
16
- filter: AnyObject;
17
- exists: ExistsFilterConfig[];
18
- };
13
+ newQuery: NewQuery;
19
14
  }
20
- export async function getSubscribeRelatedTables(this: ViewHandler, { selectParams, filter, localParams, table_rules, condition, filterOpts }: Args){
15
+ export async function getSubscribeRelatedTables(this: ViewHandler, { selectParams, filter, localParams, table_rules, newQuery }: Args){
21
16
 
22
17
  let viewOptions: ViewSubscriptionOptions | undefined = undefined;
18
+ const { condition } = newQuery.whereOpts;
23
19
  if (this.is_view) {
20
+ /** TODO: this needs to be memoized on schema fetch */
24
21
  const viewName = this.name;
25
22
  const viewNameEscaped = this.escapedName;
26
23
  const { current_schema } = await this.db.oneOrNone("SELECT current_schema")
@@ -129,7 +126,6 @@ export async function getSubscribeRelatedTables(this: ViewHandler, { selectParam
129
126
 
130
127
  /** Any joined table used within select or filter must also be added a trigger for this recordset */
131
128
  } else {
132
- const newQuery = await this.find(filter, { ...selectParams, limit: 0 }, undefined, table_rules, { ...localParams, returnNewQuery: true }) as unknown as NewQuery;
133
129
  viewOptions = {
134
130
  type: "table",
135
131
  relatedTables: []
@@ -137,7 +133,7 @@ export async function getSubscribeRelatedTables(this: ViewHandler, { selectParam
137
133
  /**
138
134
  * Avoid nested exists error. Will affect performance
139
135
  */
140
- const nonExistsFilter = filterOpts.exists.length ? {} : filter;
136
+ const nonExistsFilter = newQuery.whereOpts.exists.length ? {} : filter;
141
137
  for await (const j of (newQuery.joins ?? [])) {
142
138
  if (!viewOptions!.relatedTables.find(rt => rt.tableName === j.table)) {
143
139
  /**
@@ -1,9 +1,9 @@
1
- import { AnyObject, SQLOptions, SQLResult, SQLResultInfo } from "prostgles-types";
2
- import { DboBuilder, LocalParams, pgp, postgresToTsType } from "./DboBuilder";
3
- import { DB, Prostgles } from "../Prostgles";
4
- import { PubSubManager } from "../PubSubManager/PubSubManager";
5
1
  import { ParameterizedQuery as PQ, ParameterizedQuery } from 'pg-promise';
6
2
  import pg from "pg-promise/typescript/pg-subset";
3
+ import { AnyObject, SQLOptions, SQLResult, SQLResultInfo } from "prostgles-types";
4
+ import { EVENT_TRIGGER_TAGS } from "../Event_Trigger_Tags";
5
+ import { DB, Prostgles } from "../Prostgles";
6
+ import { DboBuilder, LocalParams, pgp, postgresToTsType } from "./DboBuilder";
7
7
 
8
8
 
9
9
  export async function runSQL(this: DboBuilder, queryWithoutRLS: string, args: undefined | AnyObject | any[], options: SQLOptions | undefined, localParams?: LocalParams) {
@@ -117,7 +117,7 @@ export const watchSchemaFallback = async function(this: DboBuilder, { queryWitho
117
117
  this.prostgles.onSchemaChange({ command, query: queryWithoutRLS })
118
118
  } else if (queryWithoutRLS) {
119
119
  const cleanedQuery = queryWithoutRLS.toLowerCase().replace(/\s\s+/g, ' ');
120
- if (PubSubManager.SCHEMA_ALTERING_QUERIES.some(q => cleanedQuery.includes(q.toLowerCase() + " "))) {
120
+ if (EVENT_TRIGGER_TAGS.some(q => cleanedQuery.includes(q.toLowerCase() + " "))) {
121
121
  this.prostgles.onSchemaChange({ command, query: queryWithoutRLS })
122
122
  }
123
123
  }
@@ -1,7 +1,7 @@
1
1
  import { AnyObject, SubscribeParams, SubscriptionChannels } from "prostgles-types";
2
- import { Filter, LocalParams, parseError } from "./DboBuilder";
3
2
  import { TableRule } from "../PublishParser/PublishParser";
4
- import { omitKeys } from "../PubSubManager/PubSubManager";
3
+ import { Filter, LocalParams, parseError } from "./DboBuilder";
4
+ import { NewQuery } from "./QueryBuilder/QueryBuilder";
5
5
  import { ViewHandler } from "./ViewHandler/ViewHandler";
6
6
  import { getSubscribeRelatedTables } from "./getSubscribeRelatedTables";
7
7
 
@@ -42,10 +42,10 @@ async function subscribe(this: ViewHandler, filter: Filter, params: SubscribePar
42
42
  throw " Cannot have localFunc AND socket ";
43
43
  }
44
44
 
45
- const { filterFields, forcedFilter } = table_rules?.select || {},
46
- filterOpts = await this.prepareWhere({ filter, forcedFilter, addWhere: false, filterFields, tableAlias: undefined, localParams, tableRule: table_rules }),
47
- condition = filterOpts.where,
48
- { throttle = 0, throttleOpts, ...selectParams } = params;
45
+ const { throttle = 0, throttleOpts, ...selectParams } = params;
46
+
47
+ /** Ensure request is valid */
48
+ await this.find(filter, { ...selectParams, limit: 0 }, undefined, table_rules, localParams);
49
49
 
50
50
  /** app_triggers condition field has an index which limits it's value.
51
51
  * TODO: use condition md5 hash
@@ -58,16 +58,18 @@ async function subscribe(this: ViewHandler, filter: Filter, params: SubscribePar
58
58
  throw "Subscribe not possible. Must be superuser to add triggers 1856";
59
59
  }
60
60
 
61
- /** Ensure request is valid */
62
- await this.find(filter, { ...selectParams, limit: 0 }, undefined, table_rules, localParams);
63
-
64
- const viewOptions = await getSubscribeRelatedTables.bind(this)({ filter, selectParams, table_rules, localParams, condition, filterOpts })
61
+ const newQuery: NewQuery = await this.find(filter, { ...selectParams, limit: 0 }, undefined, table_rules, { ...localParams, returnNewQuery: true }) as any;
62
+ const viewOptions = await getSubscribeRelatedTables.bind(this)({
63
+ filter, selectParams,
64
+ table_rules, localParams,
65
+ newQuery
66
+ });
65
67
 
66
68
  const commonSubOpts = {
67
69
  table_info: this.tableOrViewInfo,
68
70
  viewOptions,
69
71
  table_rules,
70
- condition,
72
+ condition: newQuery.whereOpts.condition,
71
73
  table_name: this.name,
72
74
  filter: { ...filter },
73
75
  params: { ...selectParams },
@@ -109,4 +111,4 @@ async function subscribe(this: ViewHandler, filter: Filter, params: SubscribePar
109
111
  }
110
112
  }
111
113
 
112
- export { subscribe }
114
+ export { subscribe };
package/lib/Filtering.ts CHANGED
@@ -244,13 +244,9 @@ export const parseFilterItem = (args: ParseFilterItemArgs): string => {
244
244
  * geom && st_makeenvelope(funcArgs)
245
245
  */
246
246
  const filterValueKeys = Object.keys(filterValue);
247
- // if(!filterValueKeys.length || filterValueKeys.length !== 1){
248
- // return mErr("Bad filter. Expecting a nested object with one key only but got: " + JSON.stringify(filterValue, null, 2));
249
- // }
250
247
  funcName = filterValueKeys[0] as any;
251
248
  if(ALLOWED_FUNCS.includes(funcName as any)){
252
249
  funcArgs = filterValue[funcName as any];
253
- // return mErr(`Bad filter. Nested function ${funcName} could not be found. Expecting one of: ${ALLOWED_FUNCS}`);
254
250
  } else {
255
251
  funcName = undefined;
256
252
  }
@@ -3,28 +3,28 @@
3
3
  * Licensed under the MIT License. See LICENSE in the project root for license information.
4
4
  *--------------------------------------------------------------------------------------------*/
5
5
 
6
+ import { DBHandlerServer, DboBuilder, PRGLIOSocket, TableInfo, TableOrViewInfo, canEXECUTE } from "../DboBuilder/DboBuilder";
6
7
  import { PostgresNotifListenManager } from "../PostgresNotifListenManager";
7
- import { addSync } from "./addSync";
8
- import { TableOrViewInfo, TableInfo, DBHandlerServer, DboBuilder, PRGLIOSocket, canEXECUTE } from "../DboBuilder/DboBuilder";
9
8
  import { DB, isSuperUser } from "../Prostgles";
9
+ import { addSync } from "./addSync";
10
10
  import { initPubSubManager } from "./initPubSubManager";
11
11
 
12
12
  import * as Bluebird from "bluebird";
13
13
  import * as pgPromise from 'pg-promise';
14
14
  import pg from 'pg-promise/typescript/pg-subset';
15
15
 
16
- import { SelectParams, FieldFilter, asName, WAL, AnyObject, SubscribeParams } from "prostgles-types";
16
+ import { AnyObject, FieldFilter, SelectParams, SubscribeParams, WAL } from "prostgles-types";
17
17
 
18
- import { syncData } from "../SyncReplication";
18
+ import { find, pickKeys, tryCatch } from "prostgles-types/dist/util";
19
+ import { LocalFuncs, getOnDataFunc, matchesLocalFuncs } from "../DboBuilder/subscribe";
20
+ import { EVENT_TRIGGER_TAGS } from "../Event_Trigger_Tags";
21
+ import { EventTypes } from "../Logging";
19
22
  import { TableRule } from "../PublishParser/PublishParser";
20
- import { find, getKeys, isObject, pickKeys, tryCatch } from "prostgles-types/dist/util";
21
- import { DB_OBJ_NAMES } from "./getInitQuery";
23
+ import { syncData } from "../SyncReplication";
22
24
  import { addSub } from "./addSub";
25
+ import { DB_OBJ_NAMES } from "./getInitQuery";
23
26
  import { notifListener } from "./notifListener";
24
27
  import { pushSubData } from "./pushSubData";
25
- import { getOnDataFunc, LocalFuncs, matchesLocalFuncs } from "../DboBuilder/subscribe";
26
- import { EVENT_TRIGGER_TAGS, EventTriggerTag } from "../Event_Trigger_Tags";
27
- import { EventTypes } from "../Logging";
28
28
 
29
29
  type PGP = pgPromise.IMain<{}, pg.IClient>;
30
30
  const pgp: PGP = pgPromise({
@@ -150,21 +150,6 @@ export type Subscription = Pick<SubscriptionParams,
150
150
  export class PubSubManager {
151
151
  static DELIMITER = '|$prstgls$|' as const;
152
152
 
153
- static SCHEMA_ALTERING_QUERIES = [
154
- 'CREATE TABLE',
155
- 'ALTER TABLE',
156
- 'DROP TABLE',
157
- 'CREATE VIEW',
158
- 'DROP VIEW',
159
- 'ALTER VIEW',
160
- 'CREATE TABLE AS',
161
- 'SELECT INTO',
162
- 'REVOKE',
163
- 'GRANT',
164
- 'CREATE POLICY',
165
- 'DROP POLICY',
166
- ] as const;
167
-
168
153
  static EXCLUDE_QUERY_FROM_SCHEMA_WATCH_ID = "prostgles internal query that should be excluded from schema watch " as const;
169
154
 
170
155
  public static canCreate = async (db: DB) => {
@@ -265,19 +250,6 @@ export class PubSubManager {
265
250
  if (watchSchema && !(await isSuperUser(this.db))) {
266
251
  console.warn("prostgles watchSchema requires superuser db user. Will not watch using event triggers")
267
252
  }
268
- let EVENT_TAG_LIST: typeof EVENT_TRIGGER_TAGS[number][] = ['COMMENT', 'CREATE TABLE', 'ALTER TABLE', 'DROP TABLE', 'CREATE VIEW', 'DROP VIEW', 'ALTER VIEW', 'CREATE TABLE AS', 'SELECT INTO', 'CREATE POLICY'];
269
- if(watchSchema === "*"){
270
- EVENT_TAG_LIST = EVENT_TRIGGER_TAGS.slice(0);
271
- } else if (isObject(watchSchema) && typeof watchSchema !== "function"){
272
- const watchSchemaKeys = getKeys(watchSchema);
273
- const isInclusive = Object.values(watchSchema).every(v => v);
274
- EVENT_TAG_LIST = EVENT_TRIGGER_TAGS
275
- .slice(0)
276
- .filter(v => {
277
- const matches = watchSchemaKeys.includes(v);
278
- return isInclusive? matches : !matches;
279
- });
280
- }
281
253
 
282
254
  try {
283
255
 
@@ -368,7 +340,7 @@ export class PubSubManager {
368
340
 
369
341
  DROP EVENT TRIGGER IF EXISTS ${DB_OBJ_NAMES.schema_watch_trigger};
370
342
  CREATE EVENT TRIGGER ${DB_OBJ_NAMES.schema_watch_trigger} ON ddl_command_end
371
- WHEN TAG IN (\${EVENT_TAG_LIST:csv})
343
+ WHEN TAG IN (\${EVENT_TRIGGER_TAGS:csv})
372
344
  EXECUTE PROCEDURE ${DB_OBJ_NAMES.schema_watch_func}();
373
345
 
374
346
  END IF;
@@ -379,7 +351,7 @@ export class PubSubManager {
379
351
 
380
352
 
381
353
  COMMIT;
382
- `, { EVENT_TAG_LIST });
354
+ `, { EVENT_TRIGGER_TAGS });
383
355
 
384
356
  await this.db.any(query)
385
357
  .catch(e => {
@@ -608,4 +580,4 @@ export class PubSubManager {
608
580
 
609
581
  export const parseCondition = (condition: string): string => condition && condition.trim().length ? condition : "TRUE"
610
582
 
611
- export { pickKeys, omitKeys } from "prostgles-types"
583
+ export { omitKeys, pickKeys } from "prostgles-types";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prostgles-server",
3
- "version": "4.2.34",
3
+ "version": "4.2.36",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -46,7 +46,7 @@
46
46
  "pg-cursor": "^2.10.3",
47
47
  "pg-promise": "^11.5.4",
48
48
  "prostgles-client": "^4.0.53",
49
- "prostgles-types": "^4.0.71"
49
+ "prostgles-types": "^4.0.73"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@types/bluebird": "^3.5.36",
@@ -0,0 +1,119 @@
1
+ import { renderHook, waitFor } from "@testing-library/react";
2
+ import { strict as assert } from "assert";
3
+ import { describe, test } from "node:test";
4
+ import type { DBHandlerClient } from "./index";
5
+ const jsdom = require("jsdom");
6
+ const { JSDOM } = jsdom;
7
+ const { window } = new JSDOM(`<!DOCTYPE html>`);
8
+ global.window = window;
9
+ global.document = window.document;
10
+
11
+ const expectValues = (result: { current: any; }, expectedValues: any[]) => {
12
+ let step = 0;
13
+ return waitFor(() => {
14
+ assert.deepStrictEqual(expectedValues[step], result.current);
15
+ const finished = step === expectedValues.length - 1;
16
+ step++;
17
+ if(!finished) throw new Error("Not finished");
18
+ });
19
+ }
20
+
21
+ export const clientHooks = async (db: DBHandlerClient) => {
22
+
23
+ await describe("Client hooks", async (t) => {
24
+ const defaultFilter = { name: "abc" };
25
+
26
+ await Promise.all([
27
+ "useFind",
28
+ "useSubscribe",
29
+ "useFindOne",
30
+ "useSubscribeOne",
31
+ ].map(async hookName => {
32
+ await test(hookName, async (t) => {
33
+ const expectsOne = hookName.includes("One");
34
+ const options = {
35
+ select: { added: "$Mon" },
36
+ limit: expectsOne? undefined : 1
37
+ };
38
+ const { result, rerender } = renderHook((filter = defaultFilter) => db.items4[hookName]!(filter, options));
39
+ const expectedData = expectsOne? { added: "Dec" } : [{ added: "Dec" }];
40
+ // Initial state
41
+ assert.deepStrictEqual(result.current, { data: undefined, isLoading: true, error: undefined });
42
+
43
+ // First fetch
44
+ await waitFor(() => {
45
+ assert.deepStrictEqual(
46
+ result.current,
47
+ {
48
+ data: expectedData, error: undefined, isLoading: false
49
+ }
50
+ );
51
+ });
52
+
53
+ // Rerender with different filter
54
+ rerender({ named: "error" });
55
+
56
+ // TODO fix first re-render result (useFetch setResult in async block might be affecting this)
57
+ // assert.deepStrictEqual(result.current, { data: undefined, isLoading: true, error: undefined });
58
+
59
+ await waitFor(() => {
60
+ assert.deepStrictEqual(
61
+ result.current,
62
+ {
63
+ data: undefined,
64
+ error: {
65
+ message: 'Table: items4 -> disallowed/inexistent columns in filter: named \n' +
66
+ ' Expecting one of: added, "id", "public", "name"',
67
+ },
68
+ isLoading: false
69
+ }
70
+ );
71
+ });
72
+ });
73
+ }));
74
+
75
+ await Promise.all([
76
+ {
77
+ hookName: "useCount",
78
+ result1: { data: 2, error: undefined, isLoading: false },
79
+ result2: [
80
+ { data: 2, error: undefined, isLoading: false },
81
+ { data: 0, error: undefined, isLoading: false },
82
+ ]
83
+ },
84
+ {
85
+ hookName: "useSize",
86
+ result1: { data: "93", error: undefined, isLoading: false },
87
+ result2: [
88
+ { data: "93", error: undefined, isLoading: false },
89
+ { data: "0", error: undefined, isLoading: false },
90
+ ]
91
+ },
92
+ ].map(async ({ hookName, result1, result2 }) => {
93
+ await test(hookName, async (t) => {
94
+ const { result, rerender } = renderHook((filter = defaultFilter) => db.items4[hookName](filter));
95
+ // Initial state
96
+ assert.deepStrictEqual(result.current, { data: undefined, isLoading: true, error: undefined });
97
+
98
+ // First fetch
99
+ await waitFor(() => {
100
+ assert.deepStrictEqual(
101
+ result.current,
102
+ result1
103
+ );
104
+ });
105
+
106
+ // Rerender with different filter
107
+ rerender({ id: -1 });
108
+
109
+ // New count
110
+ await expectValues(
111
+ result,
112
+ result2
113
+ );
114
+ });
115
+
116
+ }));
117
+
118
+ });
119
+ }
@@ -7,6 +7,7 @@ import { clientRestApi } from "../clientRestApi.spec";
7
7
  import { clientFileTests } from "../clientFileTests.spec";
8
8
  import { InitOptions } from "prostgles-client/dist/prostgles";
9
9
  export { DBHandlerClient, Auth } from "prostgles-client/dist/prostgles";
10
+ import { clientHooks } from "./hooks.spec";
10
11
 
11
12
  const start = Date.now();
12
13
  const log = (msgOrObj: any, extra?: any) => {
@@ -26,19 +27,19 @@ const tests: Record<string, ClientTestSpec> = {
26
27
  onRun: async (db, methods, tableSchema, auth) => {
27
28
  await isomorphicQueries(db, log);
28
29
  await clientOnlyQueries(db, auth, log, methods, tableSchema, TEST_NAME);
30
+ await clientHooks(db);
29
31
  },
30
32
  },
31
33
  files: {
32
34
  onRun: async (db, methods, tableSchema, auth) => {
33
35
  await clientFileTests(db, auth, log, methods, tableSchema)
34
36
  },
35
-
36
37
  },
37
38
  rest_api: {
38
39
  onRun: async (db, methods, tableSchema, auth) => {
39
40
  await clientRestApi(db, auth, log, methods, tableSchema, TEST_NAME);
40
41
  }
41
- },
42
+ }
42
43
  };
43
44
 
44
45
  const test = tests[TEST_NAME];