prostgles-server 3.0.153 → 3.0.155
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/PubSubManager/PubSubManager.d.ts +21 -26
- package/dist/PubSubManager/PubSubManager.d.ts.map +1 -1
- package/dist/PubSubManager/PubSubManager.js +43 -393
- package/dist/PubSubManager/PubSubManager.js.map +1 -1
- package/dist/PubSubManager/addSub.d.ts +7 -0
- package/dist/PubSubManager/addSub.d.ts.map +1 -0
- package/dist/PubSubManager/addSub.js +153 -0
- package/dist/PubSubManager/addSub.js.map +1 -0
- package/dist/PubSubManager/addSync.d.ts +8 -0
- package/dist/PubSubManager/addSync.d.ts.map +1 -0
- package/dist/PubSubManager/addSync.js +110 -0
- package/dist/PubSubManager/addSync.js.map +1 -0
- package/dist/PubSubManager/notifListener.d.ts +5 -0
- package/dist/PubSubManager/notifListener.d.ts.map +1 -0
- package/dist/PubSubManager/notifListener.js +97 -0
- package/dist/PubSubManager/notifListener.js.map +1 -0
- package/lib/DboBuilder/TableHandler.d.ts +1 -5
- package/lib/DboBuilder/TableHandler.d.ts.map +1 -1
- package/lib/PubSubManager/PubSubManager.d.ts +20 -29
- package/lib/PubSubManager/PubSubManager.d.ts.map +1 -1
- package/lib/PubSubManager/PubSubManager.js +44 -397
- package/lib/PubSubManager/PubSubManager.ts +82 -508
- package/lib/PubSubManager/addSub.d.ts +7 -0
- package/lib/PubSubManager/addSub.d.ts.map +1 -0
- package/lib/PubSubManager/addSub.js +152 -0
- package/lib/PubSubManager/addSub.ts +178 -0
- package/lib/PubSubManager/addSync.d.ts +8 -0
- package/lib/PubSubManager/addSync.d.ts.map +1 -0
- package/lib/PubSubManager/addSync.js +109 -0
- package/lib/PubSubManager/addSync.ts +127 -0
- package/lib/PubSubManager/notifListener.d.ts +5 -0
- package/lib/PubSubManager/notifListener.d.ts.map +1 -0
- package/lib/PubSubManager/notifListener.js +96 -0
- package/lib/PubSubManager/notifListener.ts +122 -0
- package/package.json +2 -2
- package/tests/client/PID.txt +1 -1
- package/tests/client/tsconfig.json +2 -1
- package/tests/client_only_queries.js +1 -1
- package/tests/client_only_queries.ts +1 -1
- package/tests/isomorphic_queries.ts +1 -1
- package/tests/server/package-lock.json +1 -1
- package/tests/server/tsconfig.json +2 -1
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
5
|
|
|
6
6
|
import { PostgresNotifListenManager } from "../PostgresNotifListenManager";
|
|
7
|
-
import {
|
|
7
|
+
import { addSync } from "./addSync";
|
|
8
8
|
import { TableOrViewInfo, TableInfo, DBHandlerServer, DboBuilder, PRGLIOSocket, canEXECUTE } from "../DboBuilder";
|
|
9
9
|
import { DB, isSuperUser } from "../Prostgles";
|
|
10
10
|
import { initPubSubManager } from "./initPubSubManager";
|
|
@@ -13,13 +13,14 @@ 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,
|
|
16
|
+
import { SelectParams, FieldFilter, asName, WAL, AnyObject } from "prostgles-types";
|
|
17
17
|
|
|
18
18
|
import { ClientExpressData, syncData } from "../SyncReplication";
|
|
19
19
|
import { TableRule } from "../PublishParser";
|
|
20
|
-
import { find
|
|
20
|
+
import { find } from "prostgles-types/dist/util";
|
|
21
21
|
import { DB_OBJ_NAMES } from "./getInitQuery";
|
|
22
|
-
|
|
22
|
+
import { addSub } from "./addSub";
|
|
23
|
+
import { notifListener } from "./notifListener";
|
|
23
24
|
|
|
24
25
|
type PGP = pgPromise.IMain<{}, pg.IClient>;
|
|
25
26
|
const pgp: PGP = pgPromise({
|
|
@@ -57,7 +58,7 @@ export type SyncParams = {
|
|
|
57
58
|
is_syncing: boolean;
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
type AddSyncParams = {
|
|
61
|
+
export type AddSyncParams = {
|
|
61
62
|
socket: any;
|
|
62
63
|
table_info: TableInfo;
|
|
63
64
|
table_rules: TableRule;
|
|
@@ -88,11 +89,10 @@ export type ViewSubscriptionOptions = ({
|
|
|
88
89
|
}[];
|
|
89
90
|
}
|
|
90
91
|
|
|
91
|
-
type SubscriptionParams = {
|
|
92
|
+
export type SubscriptionParams = {
|
|
92
93
|
socket_id?: string;
|
|
93
94
|
channel_name: string;
|
|
94
|
-
table_name: string;
|
|
95
|
-
socket: PRGLIOSocket | undefined;
|
|
95
|
+
// table_name: string;
|
|
96
96
|
|
|
97
97
|
/**
|
|
98
98
|
* If this is a view then an array with all related tables will be
|
|
@@ -101,18 +101,19 @@ type SubscriptionParams = {
|
|
|
101
101
|
parentSubParams: Omit<SubscriptionParams, "parentSubParams"> | undefined;
|
|
102
102
|
|
|
103
103
|
table_info: TableOrViewInfo;
|
|
104
|
+
|
|
105
|
+
/* Used as input */
|
|
104
106
|
table_rules?: TableRule;
|
|
105
107
|
filter: object;
|
|
106
108
|
params: SelectParams;
|
|
107
|
-
|
|
109
|
+
|
|
110
|
+
func: undefined | ((data: any) => any);
|
|
111
|
+
socket: PRGLIOSocket | undefined;
|
|
112
|
+
|
|
108
113
|
throttle?: number;
|
|
109
114
|
last_throttled: number;
|
|
110
115
|
is_throttling?: any;
|
|
111
|
-
is_ready?: boolean;
|
|
112
|
-
// subOne?: boolean;
|
|
113
|
-
}
|
|
114
|
-
type AddSubscriptionParams = SubscriptionParams & {
|
|
115
|
-
condition: string;
|
|
116
|
+
is_ready?: boolean;
|
|
116
117
|
}
|
|
117
118
|
|
|
118
119
|
export type PubSubManagerOptions = {
|
|
@@ -122,6 +123,27 @@ export type PubSubManagerOptions = {
|
|
|
122
123
|
onSchemaChange?: (event: { command: string; query: string }) => void;
|
|
123
124
|
}
|
|
124
125
|
|
|
126
|
+
export type Subscription = Pick<SubscriptionParams,
|
|
127
|
+
| "throttle"
|
|
128
|
+
| "is_throttling"
|
|
129
|
+
| "last_throttled"
|
|
130
|
+
| "channel_name"
|
|
131
|
+
| "is_ready"
|
|
132
|
+
| "func"
|
|
133
|
+
| "socket"
|
|
134
|
+
| "socket_id"
|
|
135
|
+
| "table_info"
|
|
136
|
+
| "filter"
|
|
137
|
+
| "params"
|
|
138
|
+
| "table_rules"
|
|
139
|
+
> & {
|
|
140
|
+
triggers: {
|
|
141
|
+
table_name: string;
|
|
142
|
+
condition: string;
|
|
143
|
+
is_related: boolean;
|
|
144
|
+
}[];
|
|
145
|
+
}
|
|
146
|
+
|
|
125
147
|
export class PubSubManager {
|
|
126
148
|
static DELIMITER = '|$prstgls$|';
|
|
127
149
|
|
|
@@ -134,9 +156,10 @@ export class PubSubManager {
|
|
|
134
156
|
}
|
|
135
157
|
|
|
136
158
|
_triggers?: Record<string, string[]>;
|
|
137
|
-
sockets:
|
|
138
|
-
subs: { [ke: string]: { [ke: string]: { subs: SubscriptionParams[] } } };
|
|
139
|
-
|
|
159
|
+
sockets: AnyObject = {};
|
|
160
|
+
// subs: { [ke: string]: { [ke: string]: { subs: SubscriptionParams[] } } };
|
|
161
|
+
subs: Subscription[] = [];
|
|
162
|
+
syncs: SyncParams[] = [];
|
|
140
163
|
socketChannelPreffix: string;
|
|
141
164
|
onSchemaChange?: ((event: { command: string; query: string }) => void) = undefined;
|
|
142
165
|
|
|
@@ -150,10 +173,7 @@ export class PubSubManager {
|
|
|
150
173
|
|
|
151
174
|
this.onSchemaChange = onSchemaChange;
|
|
152
175
|
this.dboBuilder = dboBuilder;
|
|
153
|
-
|
|
154
|
-
this.sockets = {};
|
|
155
|
-
this.subs = {};
|
|
156
|
-
this.syncs = [];
|
|
176
|
+
|
|
157
177
|
this.socketChannelPreffix = wsChannelNamePrefix || "_psqlWS_";
|
|
158
178
|
|
|
159
179
|
log("Created PubSubManager");
|
|
@@ -215,7 +235,8 @@ export class PubSubManager {
|
|
|
215
235
|
if (this.appCheck) {
|
|
216
236
|
clearInterval(this.appCheck);
|
|
217
237
|
}
|
|
218
|
-
this.
|
|
238
|
+
this.subs = [];
|
|
239
|
+
this.syncs = [];
|
|
219
240
|
if (!this.postgresNotifListenManager) {
|
|
220
241
|
throw "this.postgresNotifListenManager missing"
|
|
221
242
|
}
|
|
@@ -364,32 +385,21 @@ export class PubSubManager {
|
|
|
364
385
|
return this.postgresNotifListenManager.isListening();
|
|
365
386
|
}
|
|
366
387
|
|
|
367
|
-
getSubs(table_name: string, condition: string):
|
|
368
|
-
const subs = this.subs
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
return subs.flatMap(s => {
|
|
375
|
-
/* Return parentSubs to ensure throttling works */
|
|
376
|
-
if(s.parentSubParams){
|
|
377
|
-
const parentSubs: SubscriptionParams[] = [];
|
|
378
|
-
const parentChannel = s.parentSubParams.channel_name;
|
|
379
|
-
for(const tableName in getKeys(this.subs)){
|
|
380
|
-
for(const condition in getKeys(this.subs[tableName]!)){
|
|
381
|
-
this.subs[tableName]![condition]!.subs.forEach(parentSub => {
|
|
382
|
-
if(!parentSub.parentSubParams && parentSub.channel_name === parentChannel){
|
|
383
|
-
parentSubs.push(parentSub)
|
|
384
|
-
}
|
|
385
|
-
});
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
return parentSubs ?? s;
|
|
389
|
-
}
|
|
388
|
+
getSubs(table_name: string, condition: string, client?: Pick<Subscription, "func" | "socket_id">): Subscription[] {
|
|
389
|
+
const subs = this.subs.filter(s => find(s.triggers, { table_name, condition }));
|
|
390
|
+
if(client){
|
|
391
|
+
return subs.filter(s => client.func && s.func === client.func || client.socket_id && s.socket_id === client.socket_id);
|
|
392
|
+
}
|
|
393
|
+
return subs;
|
|
394
|
+
}
|
|
390
395
|
|
|
391
|
-
|
|
392
|
-
|
|
396
|
+
removeLocalSub(tableName: string, conditionRaw: string, func: (items: object[]) => any) {
|
|
397
|
+
const condition = parseCondition(conditionRaw);
|
|
398
|
+
if (this.getSubs(tableName, condition, { func }).length) {
|
|
399
|
+
this.subs = this.subs.filter(s => s.func !== func && !find(s.triggers, { tableName, condition }))
|
|
400
|
+
} else {
|
|
401
|
+
console.error("Could not unsubscribe. Subscription might not have initialised yet", { tableName, condition })
|
|
402
|
+
}
|
|
393
403
|
}
|
|
394
404
|
|
|
395
405
|
getSyncs(table_name: string, condition: string) {
|
|
@@ -397,141 +407,13 @@ export class PubSubManager {
|
|
|
397
407
|
.filter((s: SyncParams) => s.table_name === table_name && s.condition === condition);
|
|
398
408
|
}
|
|
399
409
|
|
|
400
|
-
|
|
401
|
-
notifListener = async (data: { payload: string }) => {
|
|
402
|
-
const str = data.payload;
|
|
403
|
-
|
|
404
|
-
if (!str) {
|
|
405
|
-
console.error("Empty notif?")
|
|
406
|
-
return;
|
|
407
|
-
}
|
|
408
|
-
const dataArr = str.split(PubSubManager.DELIMITER),
|
|
409
|
-
notifType = dataArr[0];
|
|
410
|
-
|
|
411
|
-
log(str);
|
|
412
|
-
|
|
413
|
-
if (notifType === this.NOTIF_TYPE.schema) {
|
|
414
|
-
if (this.onSchemaChange) {
|
|
415
|
-
const command = dataArr[1]!,
|
|
416
|
-
event_type = dataArr[2],
|
|
417
|
-
query = dataArr[3];
|
|
418
|
-
|
|
419
|
-
if (query) {
|
|
420
|
-
this.onSchemaChange({ command, query })
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
return;
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
if (notifType !== this.NOTIF_TYPE.data) {
|
|
428
|
-
console.error("Unexpected notif type: ", notifType);
|
|
429
|
-
return;
|
|
430
|
-
}
|
|
431
|
-
if(dataArr.length < 3){
|
|
432
|
-
throw "dataArr length < 3"
|
|
433
|
-
}
|
|
434
|
-
const table_name = dataArr[1]!,
|
|
435
|
-
op_name = dataArr[2]!,
|
|
436
|
-
condition_ids_str = dataArr[3]!;
|
|
437
|
-
|
|
438
|
-
// const triggers = await this.db.any("SELECT * FROM prostgles.triggers WHERE table_name = $1 AND id IN ($2:csv)", [table_name, condition_ids_str.split(",").map(v => +v)]);
|
|
439
|
-
// const conditions: string[] = triggers.map(t => t.condition);
|
|
440
|
-
|
|
441
|
-
/**
|
|
442
|
-
* Trigger error
|
|
443
|
-
*/
|
|
444
|
-
log("PG Trigger ->", dataArr.join("__"))
|
|
445
|
-
if (
|
|
446
|
-
condition_ids_str && condition_ids_str.startsWith("error") &&
|
|
447
|
-
this._triggers &&
|
|
448
|
-
this._triggers[table_name]?.length
|
|
449
|
-
) {
|
|
450
|
-
const pref = "INTERNAL ERROR";
|
|
451
|
-
console.error(`${pref}: condition_ids_str: ${condition_ids_str}`)
|
|
452
|
-
this._triggers[table_name]!.map(c => {
|
|
453
|
-
const subs = this.getSubs(table_name, c);
|
|
454
|
-
subs.map(s => {
|
|
455
|
-
this.pushSubData(s, pref + ". Check server logs. Schema might have changed");
|
|
456
|
-
})
|
|
457
|
-
});
|
|
458
|
-
|
|
459
|
-
/**
|
|
460
|
-
* Trigger ok
|
|
461
|
-
*/
|
|
462
|
-
} else if (
|
|
463
|
-
condition_ids_str?.split(",").length &&
|
|
464
|
-
condition_ids_str?.split(",").every((c: string) => Number.isInteger(+c)) &&
|
|
465
|
-
this._triggers?.[table_name]?.length
|
|
466
|
-
) {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
const idxs = condition_ids_str.split(",").map(v => +v);
|
|
470
|
-
const conditions = this._triggers[table_name]!.filter((c, i) => idxs.includes(i))
|
|
410
|
+
notifListener = notifListener.bind(this);
|
|
471
411
|
|
|
472
|
-
log("PG Trigger -> ", { table_name, op_name, condition_ids_str, conditions }, this._triggers[table_name]);
|
|
473
|
-
|
|
474
|
-
conditions.map(condition => {
|
|
475
|
-
|
|
476
|
-
const subs = this.getSubs(table_name, condition);
|
|
477
|
-
const syncs = this.getSyncs(table_name, condition);
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
syncs.map((s) => {
|
|
481
|
-
this.syncData(s, undefined, "trigger");
|
|
482
|
-
});
|
|
483
|
-
|
|
484
|
-
if (!subs) {
|
|
485
|
-
|
|
486
|
-
// console.error(`sub missing for ${table_name} ${condition}`, this.triggers);
|
|
487
|
-
// console.log(this.subs)
|
|
488
|
-
return;
|
|
489
|
-
}
|
|
490
412
|
|
|
491
|
-
|
|
492
|
-
for (let i = 0; i < subs.length; i++) {
|
|
493
|
-
const sub = subs[i]!;
|
|
494
|
-
if (
|
|
495
|
-
this.dbo[sub.table_name] &&
|
|
496
|
-
sub.is_ready &&
|
|
497
|
-
(sub.socket_id && this.sockets[sub.socket_id]) || sub.func
|
|
498
|
-
) {
|
|
499
|
-
const throttle = sub.throttle || 0;
|
|
500
|
-
if (sub.last_throttled <= Date.now() - throttle) {
|
|
501
|
-
|
|
502
|
-
/* It is assumed the policy was checked before this point */
|
|
503
|
-
this.pushSubData(sub);
|
|
504
|
-
// sub.last_throttled = Date.now();
|
|
505
|
-
} else if (!sub.is_throttling) {
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
log("throttling sub")
|
|
509
|
-
sub.is_throttling = setTimeout(() => {
|
|
510
|
-
log("throttling finished. pushSubData...")
|
|
511
|
-
sub.is_throttling = null;
|
|
512
|
-
this.pushSubData(sub);
|
|
513
|
-
}, throttle);// sub.throttle);
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
});
|
|
518
|
-
|
|
519
|
-
} else {
|
|
520
|
-
|
|
521
|
-
// if(!this._triggers || !this._triggers[table_name] || !this._triggers[table_name].length){
|
|
522
|
-
// console.warn(190, "Trigger sub not found. DROPPING TRIGGER", table_name, condition_ids_str, this._triggers);
|
|
523
|
-
// this.dropTrigger(table_name);
|
|
524
|
-
// } else {
|
|
525
|
-
// }
|
|
526
|
-
console.warn(190, "Trigger sub issue: ", table_name, condition_ids_str, this._triggers);
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
pushSubData(sub: SubscriptionParams, err?: any) {
|
|
413
|
+
pushSubData(sub: Subscription, err?: any) {
|
|
532
414
|
if (!sub) throw "pushSubData: invalid sub";
|
|
533
|
-
const {
|
|
534
|
-
|
|
415
|
+
const { table_info, filter, params, table_rules, socket_id, channel_name, func } = sub; //, subOne = false
|
|
416
|
+
const { name: table_name } = table_info;
|
|
535
417
|
sub.last_throttled = Date.now();
|
|
536
418
|
|
|
537
419
|
if (err) {
|
|
@@ -579,7 +461,20 @@ export class PubSubManager {
|
|
|
579
461
|
upsertSocket(socket: any) {
|
|
580
462
|
if (socket && !this.sockets[socket.id]) {
|
|
581
463
|
this.sockets[socket.id] = socket;
|
|
582
|
-
socket.on("disconnect", () =>
|
|
464
|
+
socket.on("disconnect", () => {
|
|
465
|
+
|
|
466
|
+
this.subs = this.subs.filter(s => {
|
|
467
|
+
return !(s.socket && s.socket.id === socket.id);
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
this.syncs = this.syncs.filter(s => {
|
|
471
|
+
return !(s.socket_id && s.socket_id === socket.id);
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
delete this.sockets[socket.id];
|
|
475
|
+
|
|
476
|
+
return "ok";
|
|
477
|
+
});
|
|
583
478
|
}
|
|
584
479
|
}
|
|
585
480
|
|
|
@@ -588,275 +483,10 @@ export class PubSubManager {
|
|
|
588
483
|
return await syncData(this, sync, clientData, source);
|
|
589
484
|
}
|
|
590
485
|
|
|
591
|
-
|
|
592
|
-
* Returns a sync channel
|
|
593
|
-
* A sync channel is unique per socket for each filter
|
|
594
|
-
*/
|
|
595
|
-
async addSync(syncParams: AddSyncParams) {
|
|
596
|
-
const {
|
|
597
|
-
socket = null, table_info = null, table_rules, synced_field = null,
|
|
598
|
-
allow_delete = false, id_fields = [], filter = {},
|
|
599
|
-
params, condition = "", throttle = 0
|
|
600
|
-
} = syncParams || {};
|
|
601
|
-
|
|
602
|
-
const conditionParsed = parseCondition(condition);
|
|
603
|
-
if (!socket || !table_info) throw "socket or table_info missing";
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
const { name: table_name } = table_info,
|
|
607
|
-
channel_name = `${this.socketChannelPreffix}.${table_name}.${JSON.stringify(filter)}.sync`;
|
|
608
|
-
|
|
609
|
-
if (!synced_field) throw "synced_field missing from table_rules";
|
|
610
|
-
|
|
611
|
-
this.upsertSocket(socket);
|
|
612
|
-
|
|
613
|
-
const upsertSync = () => {
|
|
614
|
-
const newSync = {
|
|
615
|
-
channel_name,
|
|
616
|
-
table_name,
|
|
617
|
-
filter,
|
|
618
|
-
condition: conditionParsed,
|
|
619
|
-
synced_field,
|
|
620
|
-
id_fields,
|
|
621
|
-
allow_delete,
|
|
622
|
-
table_rules,
|
|
623
|
-
throttle: Math.max(throttle || 0, table_rules?.sync?.throttle || 0),
|
|
624
|
-
batch_size: get(table_rules, "sync.batch_size") || DEFAULT_SYNC_BATCH_SIZE,
|
|
625
|
-
last_throttled: 0,
|
|
626
|
-
socket_id: socket.id,
|
|
627
|
-
is_sync: true,
|
|
628
|
-
last_synced: 0,
|
|
629
|
-
lr: undefined,
|
|
630
|
-
table_info,
|
|
631
|
-
is_syncing: false,
|
|
632
|
-
wal: undefined,
|
|
633
|
-
socket,
|
|
634
|
-
params
|
|
635
|
-
};
|
|
636
|
-
|
|
637
|
-
/* Only a sync per socket per table per condition allowed */
|
|
638
|
-
this.syncs = this.syncs || [];
|
|
639
|
-
const existing = find(this.syncs, { socket_id: socket.id, channel_name });
|
|
640
|
-
if (!existing) {
|
|
641
|
-
this.syncs.push(newSync);
|
|
642
|
-
// console.log("Added SYNC");
|
|
643
|
-
|
|
644
|
-
socket.removeAllListeners(channel_name + "unsync");
|
|
645
|
-
socket.once(channel_name + "unsync", (_data: any, cb: BasicCallback) => {
|
|
646
|
-
this.onSocketDisconnected(socket, channel_name);
|
|
647
|
-
cb(null, { res: "ok" })
|
|
648
|
-
});
|
|
649
|
-
|
|
650
|
-
socket.removeAllListeners(channel_name);
|
|
651
|
-
socket.on(channel_name, (data: any, cb: BasicCallback) => {
|
|
652
|
-
|
|
653
|
-
if (!data) {
|
|
654
|
-
cb({ err: "Unexpected request. Need data or onSyncRequest" });
|
|
655
|
-
return;
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
/*
|
|
659
|
-
*/
|
|
660
|
-
|
|
661
|
-
/* Server will:
|
|
662
|
-
1. Ask for last_synced emit(onSyncRequest)
|
|
663
|
-
2. Ask for data >= server_synced emit(onPullRequest)
|
|
664
|
-
-> Upsert that data
|
|
665
|
-
2. Push data >= last_synced emit(data.data)
|
|
666
|
-
|
|
667
|
-
Client will:
|
|
668
|
-
1. Send last_synced on(onSyncRequest)
|
|
669
|
-
2. Send data >= server_synced on(onPullRequest)
|
|
670
|
-
3. Send data on CRUD emit(data.data | data.deleted)
|
|
671
|
-
4. Upsert data.data | deleted on(data.data | data.deleted)
|
|
672
|
-
*/
|
|
673
|
-
|
|
674
|
-
// if(data.data){
|
|
675
|
-
// console.error("THIS SHOUKD NEVER FIRE !! NEW DATA FROM SYNC");
|
|
676
|
-
// this.upsertClientData(newSync, data.data);
|
|
677
|
-
// } else
|
|
678
|
-
if (data.onSyncRequest) {
|
|
679
|
-
// console.log("syncData from socket")
|
|
680
|
-
this.syncData(newSync, data.onSyncRequest, "client");
|
|
681
|
-
|
|
682
|
-
// console.log("onSyncRequest ", socket._user)
|
|
683
|
-
} else {
|
|
684
|
-
console.error("Unexpected sync request data from client: ", data)
|
|
685
|
-
}
|
|
686
|
-
});
|
|
687
|
-
|
|
688
|
-
// socket.emit(channel_name, { onSyncRequest: true }, (response) => {
|
|
689
|
-
// console.log(response)
|
|
690
|
-
// });
|
|
691
|
-
} else {
|
|
692
|
-
console.warn("UNCLOSED DUPLICATE SYNC FOUND", existing.channel_name);
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
return newSync;
|
|
696
|
-
};
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
// const { min_id, max_id, count, max_synced } = params;
|
|
700
|
-
|
|
701
|
-
const _sync = upsertSync();
|
|
702
|
-
|
|
703
|
-
await this.addTrigger({ table_name, condition: conditionParsed });
|
|
704
|
-
|
|
705
|
-
return channel_name;
|
|
706
|
-
}
|
|
486
|
+
addSync = addSync.bind(this);
|
|
707
487
|
|
|
488
|
+
addSub = addSub.bind(this);
|
|
708
489
|
|
|
709
|
-
/* Must return a channel for socket */
|
|
710
|
-
/* The distinct list of channel names must have a corresponding trigger in the database */
|
|
711
|
-
async addSub(subscriptionParams: Omit<AddSubscriptionParams, "channel_name" | "parentSubParams">) {
|
|
712
|
-
const {
|
|
713
|
-
socket, func = null, table_info = null, table_rules, filter = {},
|
|
714
|
-
params = {}, condition = "", throttle = 0, //subOne = false,
|
|
715
|
-
viewOptions
|
|
716
|
-
} = subscriptionParams || {};
|
|
717
|
-
|
|
718
|
-
let validated_throttle = subscriptionParams.throttle || 10;
|
|
719
|
-
if ((!socket && !func) || !table_info) throw "socket/func or table_info missing";
|
|
720
|
-
|
|
721
|
-
const pubThrottle = get(table_rules, ["subscribe", "throttle"]) || 0;
|
|
722
|
-
if (pubThrottle && Number.isInteger(pubThrottle) && pubThrottle > 0) {
|
|
723
|
-
validated_throttle = pubThrottle;
|
|
724
|
-
}
|
|
725
|
-
if (throttle && Number.isInteger(throttle) && throttle >= pubThrottle) {
|
|
726
|
-
validated_throttle = throttle;
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
const channel_name = `${this.socketChannelPreffix}.${table_info.name}.${JSON.stringify(filter)}.${JSON.stringify(params)}.${"m"}.sub`;
|
|
730
|
-
|
|
731
|
-
this.upsertSocket(socket);
|
|
732
|
-
|
|
733
|
-
const upsertSub = (newSubData: { table_name: string; condition: string; is_ready: boolean; parentSubParams: SubscriptionParams["parentSubParams"] }, isReadyOverride: boolean | undefined) => {
|
|
734
|
-
const { table_name, condition: _cond, is_ready = false, parentSubParams } = newSubData,
|
|
735
|
-
condition = parseCondition(_cond),
|
|
736
|
-
newSub: SubscriptionParams = {
|
|
737
|
-
socket,
|
|
738
|
-
table_name: table_info.name,
|
|
739
|
-
table_info,
|
|
740
|
-
filter,
|
|
741
|
-
params,
|
|
742
|
-
table_rules,
|
|
743
|
-
channel_name,
|
|
744
|
-
parentSubParams,
|
|
745
|
-
func: func ?? undefined,
|
|
746
|
-
socket_id: socket?.id,
|
|
747
|
-
throttle: validated_throttle,
|
|
748
|
-
is_throttling: null,
|
|
749
|
-
last_throttled: 0,
|
|
750
|
-
is_ready,
|
|
751
|
-
};
|
|
752
|
-
|
|
753
|
-
this.subs[table_name] = this.subs[table_name] ?? {};
|
|
754
|
-
this.subs[table_name]![condition] = this.subs[table_name]![condition] ?? { subs: [] };
|
|
755
|
-
this.subs[table_name]![condition]!.subs = this.subs[table_name]![condition]!.subs ?? [];
|
|
756
|
-
|
|
757
|
-
// console.log("1034 upsertSub", this.subs)
|
|
758
|
-
const sub_idx = this.subs[table_name]![condition]!.subs.findIndex(s =>
|
|
759
|
-
s.channel_name === channel_name &&
|
|
760
|
-
(
|
|
761
|
-
socket && s.socket_id === socket.id ||
|
|
762
|
-
func && s.func === func
|
|
763
|
-
)
|
|
764
|
-
);
|
|
765
|
-
if (sub_idx < 0) {
|
|
766
|
-
this.subs[table_name]![condition]!.subs.push(newSub);
|
|
767
|
-
if (socket) {
|
|
768
|
-
const chnUnsub = channel_name + "unsubscribe";
|
|
769
|
-
socket.removeAllListeners(chnUnsub);
|
|
770
|
-
socket.once(chnUnsub, (_data: any, cb: BasicCallback) => {
|
|
771
|
-
const res = this.onSocketDisconnected(socket, channel_name);
|
|
772
|
-
cb(null, { res });
|
|
773
|
-
});
|
|
774
|
-
}
|
|
775
|
-
} else {
|
|
776
|
-
this.subs[table_name]![condition]!.subs[sub_idx] = newSub;
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
if (isReadyOverride ?? is_ready) {
|
|
780
|
-
this.pushSubData(newSub);
|
|
781
|
-
}
|
|
782
|
-
};
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
viewOptions?.relatedTables.map(async (relatedTable, relatedTableIdx) => {
|
|
787
|
-
|
|
788
|
-
const params: Omit<Parameters<typeof upsertSub>[0], "is_ready"> = {
|
|
789
|
-
table_name: relatedTable.tableName,
|
|
790
|
-
condition: relatedTable.condition,
|
|
791
|
-
parentSubParams: {
|
|
792
|
-
...subscriptionParams,
|
|
793
|
-
channel_name
|
|
794
|
-
},
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
upsertSub({
|
|
798
|
-
...params,
|
|
799
|
-
is_ready: false,
|
|
800
|
-
|
|
801
|
-
}, false);
|
|
802
|
-
|
|
803
|
-
await this.addTrigger(params, viewOptions);
|
|
804
|
-
|
|
805
|
-
/** Trigger pushSubData only on last related table (if it's a view) to prevent duplicate firings */
|
|
806
|
-
const isLast = relatedTableIdx === viewOptions.relatedTables.length - 1;
|
|
807
|
-
upsertSub({
|
|
808
|
-
...params,
|
|
809
|
-
is_ready: true
|
|
810
|
-
}, isLast && !table_info.is_view);
|
|
811
|
-
|
|
812
|
-
});
|
|
813
|
-
|
|
814
|
-
if (table_info.is_view) {
|
|
815
|
-
if(!viewOptions?.relatedTables.length){
|
|
816
|
-
throw "PubSubManager: view parent_tables missing";
|
|
817
|
-
}
|
|
818
|
-
return channel_name;
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
/* Just a table, add table + condition trigger */
|
|
822
|
-
// console.log(table_info, 202);
|
|
823
|
-
|
|
824
|
-
upsertSub({
|
|
825
|
-
table_name: table_info.name,
|
|
826
|
-
condition: parseCondition(condition),
|
|
827
|
-
parentSubParams: undefined,
|
|
828
|
-
is_ready: false
|
|
829
|
-
}, undefined);
|
|
830
|
-
|
|
831
|
-
await this.addTrigger({
|
|
832
|
-
table_name: table_info.name,
|
|
833
|
-
condition: parseCondition(condition),
|
|
834
|
-
});
|
|
835
|
-
|
|
836
|
-
upsertSub({
|
|
837
|
-
table_name: table_info.name,
|
|
838
|
-
condition: parseCondition(condition),
|
|
839
|
-
parentSubParams: undefined,
|
|
840
|
-
is_ready: true
|
|
841
|
-
}, undefined);
|
|
842
|
-
|
|
843
|
-
return channel_name;
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
removeLocalSub(table_name: string, condition: string, func: (items: object[]) => any) {
|
|
847
|
-
const cond = parseCondition(condition);
|
|
848
|
-
if (get(this.subs, [table_name, cond, "subs"])) {
|
|
849
|
-
this.subs[table_name]![cond]!.subs.map((sub, i) => {
|
|
850
|
-
if (
|
|
851
|
-
sub.func && sub.func === func
|
|
852
|
-
) {
|
|
853
|
-
this.subs[table_name]![cond]!.subs.splice(i, 1);
|
|
854
|
-
}
|
|
855
|
-
});
|
|
856
|
-
} else {
|
|
857
|
-
console.error("Could not unsubscribe. Subscription might not have initialised yet")
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
490
|
|
|
861
491
|
getActiveListeners = (): { table_name: string; condition: string }[] => {
|
|
862
492
|
const result: { table_name: string; condition: string }[] = [];
|
|
@@ -868,72 +498,16 @@ export class PubSubManager {
|
|
|
868
498
|
(this.syncs || []).map(s => {
|
|
869
499
|
upsert(s.table_name, s.condition)
|
|
870
500
|
});
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
}
|
|
501
|
+
|
|
502
|
+
this.subs.forEach(s => {
|
|
503
|
+
s.triggers.forEach(trg => {
|
|
504
|
+
upsert(trg.table_name, trg.condition);
|
|
876
505
|
});
|
|
877
506
|
});
|
|
878
507
|
|
|
879
508
|
return result;
|
|
880
509
|
}
|
|
881
510
|
|
|
882
|
-
onSocketDisconnected(socket?: PRGLIOSocket, channel_name?: string) {
|
|
883
|
-
// process.on('warning', e => {
|
|
884
|
-
// console.warn(e.stack)
|
|
885
|
-
// });
|
|
886
|
-
// console.log("onSocketDisconnected", channel_name, this.syncs)
|
|
887
|
-
if (this.subs) {
|
|
888
|
-
Object.keys(this.subs).map(table_name => {
|
|
889
|
-
Object.keys(this.subs[table_name]!).map(condition => {
|
|
890
|
-
this.subs[table_name]![condition]!.subs.map((sub, i) => {
|
|
891
|
-
|
|
892
|
-
/**
|
|
893
|
-
* If a channel name is specified then delete triggers
|
|
894
|
-
*/
|
|
895
|
-
if (
|
|
896
|
-
(socket && sub.socket_id === socket.id) &&
|
|
897
|
-
(!channel_name || sub.channel_name === channel_name)
|
|
898
|
-
) {
|
|
899
|
-
this.subs[table_name]![condition]!.subs.splice(i, 1);
|
|
900
|
-
if (!this.subs[table_name]![condition]!.subs.length) {
|
|
901
|
-
delete this.subs[table_name]![condition];
|
|
902
|
-
|
|
903
|
-
if (isEmpty(this.subs[table_name])) {
|
|
904
|
-
delete this.subs[table_name];
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
}
|
|
908
|
-
});
|
|
909
|
-
})
|
|
910
|
-
});
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
if (this.syncs) {
|
|
914
|
-
this.syncs = this.syncs.filter(s => {
|
|
915
|
-
const matchesSocket = Boolean(socket && s.socket_id !== socket.id)
|
|
916
|
-
if (channel_name) {
|
|
917
|
-
return matchesSocket || s.channel_name !== channel_name
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
return matchesSocket;
|
|
921
|
-
});
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
if (!socket) {
|
|
925
|
-
// Do nothing
|
|
926
|
-
} else if (!channel_name) {
|
|
927
|
-
delete this.sockets[socket.id];
|
|
928
|
-
} else {
|
|
929
|
-
socket.removeAllListeners(channel_name);
|
|
930
|
-
socket.removeAllListeners(channel_name + "unsync");
|
|
931
|
-
socket.removeAllListeners(channel_name + "unsubscribe");
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
return "ok";
|
|
935
|
-
}
|
|
936
|
-
|
|
937
511
|
|
|
938
512
|
checkIfTimescaleBug = async (table_name: string) => {
|
|
939
513
|
const schema = "_timescaledb_catalog",
|
|
@@ -1037,6 +611,6 @@ export class PubSubManager {
|
|
|
1037
611
|
}
|
|
1038
612
|
|
|
1039
613
|
|
|
1040
|
-
const parseCondition = (condition: string): string => condition && condition.trim().length ? condition : "TRUE"
|
|
614
|
+
export const parseCondition = (condition: string): string => condition && condition.trim().length ? condition : "TRUE"
|
|
1041
615
|
|
|
1042
616
|
export { pickKeys, omitKeys } from "prostgles-types"
|