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.
- package/dist/DboBuilder/QueryBuilder/QueryBuilder.d.ts +2 -1
- package/dist/DboBuilder/QueryBuilder/QueryBuilder.d.ts.map +1 -1
- package/dist/DboBuilder/QueryBuilder/QueryBuilder.js +1 -2
- package/dist/DboBuilder/QueryBuilder/QueryBuilder.js.map +1 -1
- package/dist/DboBuilder/QueryBuilder/getNewQuery.d.ts +69 -69
- package/dist/DboBuilder/QueryBuilder/getNewQuery.d.ts.map +1 -1
- package/dist/DboBuilder/QueryBuilder/getNewQuery.js +4 -8
- package/dist/DboBuilder/QueryBuilder/getNewQuery.js.map +1 -1
- package/dist/DboBuilder/dboBuilderUtils.js +1 -1
- package/dist/DboBuilder/dboBuilderUtils.js.map +1 -1
- package/dist/DboBuilder/getCondition.d.ts.map +1 -1
- package/dist/DboBuilder/getCondition.js +12 -12
- package/dist/DboBuilder/getCondition.js.map +1 -1
- package/dist/DboBuilder/getSubscribeRelatedTables.d.ts +4 -8
- package/dist/DboBuilder/getSubscribeRelatedTables.d.ts.map +1 -1
- package/dist/DboBuilder/getSubscribeRelatedTables.js +5 -4
- package/dist/DboBuilder/getSubscribeRelatedTables.js.map +1 -1
- package/dist/DboBuilder/runSQL.d.ts +2 -2
- package/dist/DboBuilder/runSQL.d.ts.map +1 -1
- package/dist/DboBuilder/runSQL.js +3 -3
- package/dist/DboBuilder/runSQL.js.map +1 -1
- package/dist/DboBuilder/subscribe.d.ts +1 -1
- package/dist/DboBuilder/subscribe.d.ts.map +1 -1
- package/dist/DboBuilder/subscribe.js +10 -5
- package/dist/DboBuilder/subscribe.js.map +1 -1
- package/dist/Filtering.d.ts.map +1 -1
- package/dist/Filtering.js +0 -4
- package/dist/Filtering.js.map +1 -1
- package/dist/PubSubManager/PubSubManager.d.ts +4 -5
- package/dist/PubSubManager/PubSubManager.d.ts.map +1 -1
- package/dist/PubSubManager/PubSubManager.js +9 -37
- package/dist/PubSubManager/PubSubManager.js.map +1 -1
- package/lib/DboBuilder/QueryBuilder/QueryBuilder.ts +3 -3
- package/lib/DboBuilder/QueryBuilder/getNewQuery.ts +4 -10
- package/lib/DboBuilder/dboBuilderUtils.ts +1 -1
- package/lib/DboBuilder/getCondition.ts +28 -26
- package/lib/DboBuilder/getSubscribeRelatedTables.ts +6 -10
- package/lib/DboBuilder/runSQL.ts +5 -5
- package/lib/DboBuilder/subscribe.ts +14 -12
- package/lib/Filtering.ts +0 -4
- package/lib/PubSubManager/PubSubManager.ts +12 -40
- package/package.json +2 -2
- package/tests/client/hooks.spec.ts +119 -0
- package/tests/client/index.ts +3 -2
- package/tests/client/package-lock.json +1590 -159
- package/tests/client/package.json +4 -1
- package/tests/isomorphicQueries.spec.ts +0 -3
- 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
|
|
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
|
-
|
|
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)
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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 =
|
|
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
|
/**
|
package/lib/DboBuilder/runSQL.ts
CHANGED
|
@@ -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 (
|
|
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 {
|
|
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 {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
62
|
-
await
|
|
63
|
-
|
|
64
|
-
|
|
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 {
|
|
16
|
+
import { AnyObject, FieldFilter, SelectParams, SubscribeParams, WAL } from "prostgles-types";
|
|
17
17
|
|
|
18
|
-
import {
|
|
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 {
|
|
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 (\${
|
|
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
|
-
`, {
|
|
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 {
|
|
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.
|
|
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.
|
|
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
|
+
}
|
package/tests/client/index.ts
CHANGED
|
@@ -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];
|