@nymphjs/pubsub 1.0.0-beta.13 → 1.0.0-beta.130
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/CHANGELOG.md +534 -0
- package/README.md +12 -10
- package/dist/PubSub.d.ts +77 -14
- package/dist/PubSub.js +548 -213
- package/dist/PubSub.js.map +1 -1
- package/dist/PubSub.test.js +265 -81
- package/dist/PubSub.test.js.map +1 -1
- package/dist/PubSub.types.d.ts +11 -2
- package/dist/PubSub.types.js +1 -2
- package/dist/conf/defaults.d.ts +1 -1
- package/dist/conf/defaults.js +4 -4
- package/dist/conf/defaults.js.map +1 -1
- package/dist/conf/index.d.ts +31 -2
- package/dist/conf/index.js +2 -8
- package/dist/conf/index.js.map +1 -1
- package/dist/createServer.d.ts +2 -2
- package/dist/createServer.js +14 -15
- package/dist/createServer.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +7 -27
- package/dist/index.js.map +1 -1
- package/jest.config.js +31 -5
- package/package.json +37 -28
- package/src/PubSub.test.ts +343 -137
- package/src/PubSub.ts +796 -481
- package/src/PubSub.types.ts +11 -6
- package/src/conf/defaults.ts +2 -2
- package/src/conf/index.ts +31 -2
- package/src/createServer.ts +8 -8
- package/src/index.ts +4 -4
- package/tsconfig.json +6 -3
- package/typedoc.json +4 -0
- package/dist/conf/d.d.ts +0 -7
- package/dist/conf/d.js +0 -3
- package/dist/conf/d.js.map +0 -1
- package/src/conf/d.ts +0 -30
package/src/PubSub.test.ts
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
|
+
import ws from 'websocket';
|
|
2
3
|
import SQLite3Driver from '@nymphjs/driver-sqlite3';
|
|
3
4
|
import { Nymph as NymphServer } from '@nymphjs/nymph';
|
|
4
|
-
import {
|
|
5
|
+
import type { PubSubSubscription, PubSubUpdate } from '@nymphjs/client';
|
|
6
|
+
import { Nymph, PubSub } from '@nymphjs/client';
|
|
5
7
|
import createRestServer from '@nymphjs/server';
|
|
6
8
|
import {
|
|
7
9
|
EmployeeModel as EmployeeModelClass,
|
|
8
10
|
Employee as EmployeeClass,
|
|
9
11
|
EmployeeData,
|
|
12
|
+
RestrictedModel as RestrictedModelClass,
|
|
13
|
+
Restricted as RestrictedClass,
|
|
14
|
+
PubSubDisabledModel as PubSubDisabledModelClass,
|
|
15
|
+
PubSubDisabled as PubSubDisabledClass,
|
|
16
|
+
PubSubDisabledData,
|
|
10
17
|
} from '@nymphjs/server/dist/testArtifacts.js';
|
|
11
18
|
|
|
12
|
-
import createServer from './index';
|
|
13
|
-
import PubSubServer from './PubSub';
|
|
19
|
+
import createServer from './index.js';
|
|
20
|
+
import PubSubServer from './PubSub.js';
|
|
14
21
|
|
|
15
22
|
const sqliteConfig = {
|
|
16
23
|
filename: ':memory:',
|
|
@@ -18,28 +25,35 @@ const sqliteConfig = {
|
|
|
18
25
|
|
|
19
26
|
const pubSubConfig = {
|
|
20
27
|
originIsAllowed: () => true,
|
|
21
|
-
entries: ['ws://localhost:
|
|
28
|
+
entries: ['ws://localhost:5083/'],
|
|
22
29
|
logger: () => {},
|
|
23
30
|
};
|
|
24
31
|
|
|
25
32
|
const nymphServer = new NymphServer({}, new SQLite3Driver(sqliteConfig));
|
|
26
33
|
const EmployeeModel = nymphServer.addEntityClass(EmployeeModelClass);
|
|
27
|
-
|
|
34
|
+
const RestrictedModel = nymphServer.addEntityClass(RestrictedModelClass);
|
|
35
|
+
const PubSubDisabledModel = nymphServer.addEntityClass(
|
|
36
|
+
PubSubDisabledModelClass,
|
|
37
|
+
);
|
|
38
|
+
const removePublisher = PubSubServer.initPublisher(pubSubConfig, nymphServer);
|
|
28
39
|
|
|
29
40
|
const app = express();
|
|
30
41
|
app.use(createRestServer(nymphServer));
|
|
31
|
-
const server = app.listen(
|
|
42
|
+
const server = app.listen(5082);
|
|
32
43
|
|
|
33
|
-
const pubsubServer = createServer(
|
|
44
|
+
const pubsubServer = createServer(5083, pubSubConfig, nymphServer);
|
|
34
45
|
|
|
35
46
|
const nymphOptions = {
|
|
36
|
-
restUrl: 'http://localhost:
|
|
37
|
-
pubsubUrl: 'ws://localhost:
|
|
47
|
+
restUrl: 'http://localhost:5082/',
|
|
48
|
+
pubsubUrl: 'ws://localhost:5083/',
|
|
38
49
|
noConsole: true,
|
|
50
|
+
WebSocket: ws.w3cwebsocket as unknown as typeof WebSocket,
|
|
39
51
|
};
|
|
40
52
|
const nymph = new Nymph(nymphOptions);
|
|
41
53
|
const pubsub = new PubSub(nymphOptions, nymph);
|
|
42
54
|
const Employee = nymph.addEntityClass(EmployeeClass);
|
|
55
|
+
const Restricted = nymph.addEntityClass(RestrictedClass);
|
|
56
|
+
const PubSubDisabled = nymph.addEntityClass(PubSubDisabledClass);
|
|
43
57
|
|
|
44
58
|
describe('Nymph REST Server and Client', () => {
|
|
45
59
|
async function createJane() {
|
|
@@ -93,22 +107,22 @@ describe('Nymph REST Server and Client', () => {
|
|
|
93
107
|
it('notified of new match', async () => {
|
|
94
108
|
let jane: Promise<EmployeeClass>;
|
|
95
109
|
|
|
96
|
-
await new Promise((resolve) => {
|
|
110
|
+
await new Promise<void>((resolve) => {
|
|
97
111
|
let updated = false;
|
|
98
112
|
const subscription = pubsub.subscribeEntities(
|
|
99
113
|
{ class: Employee },
|
|
100
114
|
{
|
|
101
115
|
type: '&',
|
|
102
116
|
equal: ['name', 'Jane Doe'],
|
|
103
|
-
}
|
|
117
|
+
},
|
|
104
118
|
)(async (update) => {
|
|
105
119
|
if (updated) {
|
|
106
120
|
expect('added' in update && update.added).toEqual((await jane).guid);
|
|
107
121
|
expect('data' in update && update.data.guid).toEqual(
|
|
108
|
-
(await jane).guid
|
|
122
|
+
(await jane).guid,
|
|
109
123
|
);
|
|
110
124
|
subscription.unsubscribe();
|
|
111
|
-
resolve(
|
|
125
|
+
resolve();
|
|
112
126
|
} else {
|
|
113
127
|
expect(update).toEqual([]);
|
|
114
128
|
updated = true;
|
|
@@ -122,14 +136,14 @@ describe('Nymph REST Server and Client', () => {
|
|
|
122
136
|
let guid: string;
|
|
123
137
|
let committed = false;
|
|
124
138
|
|
|
125
|
-
await new Promise((resolve) => {
|
|
139
|
+
await new Promise<void>((resolve) => {
|
|
126
140
|
let updated = false;
|
|
127
141
|
const subscription = pubsub.subscribeEntities(
|
|
128
142
|
{ class: Employee },
|
|
129
143
|
{
|
|
130
144
|
type: '&',
|
|
131
145
|
equal: ['name', 'Steve Transaction'],
|
|
132
|
-
}
|
|
146
|
+
},
|
|
133
147
|
)(async (update) => {
|
|
134
148
|
if (updated) {
|
|
135
149
|
if (committed) {
|
|
@@ -139,14 +153,12 @@ describe('Nymph REST Server and Client', () => {
|
|
|
139
153
|
throw new Error('Update arrived before transaction committed.');
|
|
140
154
|
}
|
|
141
155
|
subscription.unsubscribe();
|
|
142
|
-
resolve(
|
|
156
|
+
resolve();
|
|
143
157
|
} else {
|
|
144
158
|
expect(update).toEqual([]);
|
|
145
159
|
updated = true;
|
|
146
160
|
const tnymph = await nymphServer.startTransaction('steve');
|
|
147
|
-
const Employee = tnymph.getEntityClass(
|
|
148
|
-
EmployeeModel.class
|
|
149
|
-
) as typeof EmployeeModel;
|
|
161
|
+
const Employee = tnymph.getEntityClass(EmployeeModel);
|
|
150
162
|
const steve = await Employee.factory();
|
|
151
163
|
steve.name = 'Steve Transaction';
|
|
152
164
|
steve.current = true;
|
|
@@ -157,7 +169,7 @@ describe('Nymph REST Server and Client', () => {
|
|
|
157
169
|
try {
|
|
158
170
|
await steve.$save();
|
|
159
171
|
guid = steve.guid as string;
|
|
160
|
-
await new Promise((res) => setTimeout(res, 1000));
|
|
172
|
+
await new Promise<void>((res) => setTimeout(res, 1000));
|
|
161
173
|
committed = true;
|
|
162
174
|
await tnymph.commit('steve');
|
|
163
175
|
} catch (e: any) {
|
|
@@ -172,14 +184,14 @@ describe('Nymph REST Server and Client', () => {
|
|
|
172
184
|
it('not notified of new match when transaction rolled back', async () => {
|
|
173
185
|
let guid: string;
|
|
174
186
|
|
|
175
|
-
await new Promise((resolve) => {
|
|
187
|
+
await new Promise<void>((resolve) => {
|
|
176
188
|
let updated = false;
|
|
177
189
|
const subscription = pubsub.subscribeEntities(
|
|
178
190
|
{ class: Employee },
|
|
179
191
|
{
|
|
180
192
|
type: '&',
|
|
181
193
|
equal: ['name', 'Steve Rollback'],
|
|
182
|
-
}
|
|
194
|
+
},
|
|
183
195
|
)(async (update) => {
|
|
184
196
|
if (updated) {
|
|
185
197
|
if ('added' in update && update.added === guid) {
|
|
@@ -190,9 +202,7 @@ describe('Nymph REST Server and Client', () => {
|
|
|
190
202
|
expect(update).toEqual([]);
|
|
191
203
|
updated = true;
|
|
192
204
|
const tnymph = await nymphServer.startTransaction('steve');
|
|
193
|
-
const Employee = tnymph.getEntityClass(
|
|
194
|
-
EmployeeModel.class
|
|
195
|
-
) as typeof EmployeeModel;
|
|
205
|
+
const Employee = tnymph.getEntityClass(EmployeeModel);
|
|
196
206
|
const steve = await Employee.factory();
|
|
197
207
|
steve.name = 'Steve Rollback';
|
|
198
208
|
steve.current = true;
|
|
@@ -203,11 +213,11 @@ describe('Nymph REST Server and Client', () => {
|
|
|
203
213
|
try {
|
|
204
214
|
await steve.$save();
|
|
205
215
|
guid = steve.guid as string;
|
|
206
|
-
await new Promise((res) => setTimeout(res, 600));
|
|
216
|
+
await new Promise<void>((res) => setTimeout(res, 600));
|
|
207
217
|
await tnymph.rollback('steve');
|
|
208
|
-
await new Promise((res) => setTimeout(res, 600));
|
|
218
|
+
await new Promise<void>((res) => setTimeout(res, 600));
|
|
209
219
|
subscription.unsubscribe();
|
|
210
|
-
resolve(
|
|
220
|
+
resolve();
|
|
211
221
|
} catch (e: any) {
|
|
212
222
|
console.error('Error creating entity: ', e);
|
|
213
223
|
throw e;
|
|
@@ -221,14 +231,14 @@ describe('Nymph REST Server and Client', () => {
|
|
|
221
231
|
let guid: string;
|
|
222
232
|
let committed = false;
|
|
223
233
|
|
|
224
|
-
await new Promise((resolve) => {
|
|
234
|
+
await new Promise<void>((resolve) => {
|
|
225
235
|
let updated = false;
|
|
226
236
|
const subscription = pubsub.subscribeEntities(
|
|
227
237
|
{ class: Employee },
|
|
228
238
|
{
|
|
229
239
|
type: '&',
|
|
230
240
|
equal: ['name', 'Steve Complex'],
|
|
231
|
-
}
|
|
241
|
+
},
|
|
232
242
|
)(async (update) => {
|
|
233
243
|
if (updated) {
|
|
234
244
|
if (committed) {
|
|
@@ -238,7 +248,7 @@ describe('Nymph REST Server and Client', () => {
|
|
|
238
248
|
throw new Error('Update arrived before transaction committed.');
|
|
239
249
|
}
|
|
240
250
|
subscription.unsubscribe();
|
|
241
|
-
resolve(
|
|
251
|
+
resolve();
|
|
242
252
|
} else {
|
|
243
253
|
expect(update).toEqual([]);
|
|
244
254
|
updated = true;
|
|
@@ -246,9 +256,7 @@ describe('Nymph REST Server and Client', () => {
|
|
|
246
256
|
|
|
247
257
|
// Start a transaction that ultimately gets rolled back.
|
|
248
258
|
const tnymphB = await tnymphTop.startTransaction('steve-b');
|
|
249
|
-
const EmployeeB = tnymphB.getEntityClass(
|
|
250
|
-
EmployeeModel.class
|
|
251
|
-
) as typeof EmployeeModel;
|
|
259
|
+
const EmployeeB = tnymphB.getEntityClass(EmployeeModel);
|
|
252
260
|
const badSteve = await EmployeeB.factory();
|
|
253
261
|
badSteve.name = 'Steve Complex';
|
|
254
262
|
badSteve.current = true;
|
|
@@ -258,9 +266,9 @@ describe('Nymph REST Server and Client', () => {
|
|
|
258
266
|
badSteve.title = 'Seniorer Person';
|
|
259
267
|
try {
|
|
260
268
|
await badSteve.$save();
|
|
261
|
-
await new Promise((res) => setTimeout(res, 200));
|
|
269
|
+
await new Promise<void>((res) => setTimeout(res, 200));
|
|
262
270
|
await tnymphB.rollback('steve-b');
|
|
263
|
-
await new Promise((res) => setTimeout(res, 200));
|
|
271
|
+
await new Promise<void>((res) => setTimeout(res, 200));
|
|
264
272
|
} catch (e: any) {
|
|
265
273
|
console.error('Error creating entity: ', e);
|
|
266
274
|
throw e;
|
|
@@ -268,9 +276,7 @@ describe('Nymph REST Server and Client', () => {
|
|
|
268
276
|
|
|
269
277
|
// Start a transaction that ultimately gets committed.
|
|
270
278
|
const tnymphA = await tnymphTop.startTransaction('steve-a');
|
|
271
|
-
const EmployeeA = tnymphA.getEntityClass(
|
|
272
|
-
EmployeeModel.class
|
|
273
|
-
) as typeof EmployeeModel;
|
|
279
|
+
const EmployeeA = tnymphA.getEntityClass(EmployeeModel);
|
|
274
280
|
const goodSteve = await EmployeeA.factory();
|
|
275
281
|
goodSteve.name = 'Steve Complex';
|
|
276
282
|
goodSteve.current = true;
|
|
@@ -281,9 +287,9 @@ describe('Nymph REST Server and Client', () => {
|
|
|
281
287
|
try {
|
|
282
288
|
await goodSteve.$save();
|
|
283
289
|
guid = goodSteve.guid as string;
|
|
284
|
-
await new Promise((res) => setTimeout(res, 200));
|
|
290
|
+
await new Promise<void>((res) => setTimeout(res, 200));
|
|
285
291
|
await tnymphA.commit('steve-a');
|
|
286
|
-
await new Promise((res) => setTimeout(res, 400));
|
|
292
|
+
await new Promise<void>((res) => setTimeout(res, 400));
|
|
287
293
|
committed = true;
|
|
288
294
|
await tnymphTop.commit('steve-top');
|
|
289
295
|
} catch (e: any) {
|
|
@@ -299,7 +305,7 @@ describe('Nymph REST Server and Client', () => {
|
|
|
299
305
|
let jane = await createJane();
|
|
300
306
|
let entities: (EmployeeClass & EmployeeData)[] = [];
|
|
301
307
|
|
|
302
|
-
await new Promise((resolve) => {
|
|
308
|
+
await new Promise<void>((resolve) => {
|
|
303
309
|
let mdate = 0;
|
|
304
310
|
if (jane.guid == null) {
|
|
305
311
|
throw new Error('Entity is null.');
|
|
@@ -309,13 +315,13 @@ describe('Nymph REST Server and Client', () => {
|
|
|
309
315
|
{
|
|
310
316
|
type: '&',
|
|
311
317
|
guid: jane.guid,
|
|
312
|
-
}
|
|
318
|
+
},
|
|
313
319
|
)(async (update) => {
|
|
314
320
|
pubsub.updateArray(entities, update);
|
|
315
321
|
|
|
316
322
|
if (mdate > 0 && (entities[0]?.mdate ?? -1) === mdate) {
|
|
317
323
|
subscription.unsubscribe();
|
|
318
|
-
resolve(
|
|
324
|
+
resolve();
|
|
319
325
|
} else if (Array.isArray(update)) {
|
|
320
326
|
expect(update.length).toEqual(1);
|
|
321
327
|
expect(entities[0].salary).toEqual(8000000);
|
|
@@ -334,7 +340,7 @@ describe('Nymph REST Server and Client', () => {
|
|
|
334
340
|
let [jane, john] = await createBossJane();
|
|
335
341
|
let entities: (EmployeeClass & EmployeeData)[] = [];
|
|
336
342
|
|
|
337
|
-
await new Promise((resolve) => {
|
|
343
|
+
await new Promise<void>((resolve) => {
|
|
338
344
|
let mdate = 0;
|
|
339
345
|
if (jane.guid == null || john.guid == null) {
|
|
340
346
|
throw new Error('Entity is null.');
|
|
@@ -347,13 +353,13 @@ describe('Nymph REST Server and Client', () => {
|
|
|
347
353
|
'subordinates',
|
|
348
354
|
[{ class: Employee }, { type: '&', guid: john.guid }],
|
|
349
355
|
],
|
|
350
|
-
}
|
|
356
|
+
},
|
|
351
357
|
)(async (update) => {
|
|
352
358
|
pubsub.updateArray(entities, update);
|
|
353
359
|
|
|
354
360
|
if (mdate > 0 && (entities[0]?.mdate ?? -1) === mdate) {
|
|
355
361
|
subscription.unsubscribe();
|
|
356
|
-
resolve(
|
|
362
|
+
resolve();
|
|
357
363
|
} else if (Array.isArray(update)) {
|
|
358
364
|
expect(update.length).toEqual(1);
|
|
359
365
|
expect(entities[0].salary).toEqual(8000000);
|
|
@@ -372,44 +378,150 @@ describe('Nymph REST Server and Client', () => {
|
|
|
372
378
|
let [jane, john] = await createBossJane();
|
|
373
379
|
let entities: (EmployeeClass & EmployeeData)[] = [];
|
|
374
380
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
],
|
|
381
|
+
// Create employees that matches qref to test when multiple things match.
|
|
382
|
+
let oldEmployee = await createJane();
|
|
383
|
+
oldEmployee.current = false;
|
|
384
|
+
await oldEmployee.$save();
|
|
385
|
+
let oldEmployee2 = await createJane();
|
|
386
|
+
oldEmployee2.current = false;
|
|
387
|
+
await oldEmployee2.$save();
|
|
388
|
+
|
|
389
|
+
expect(
|
|
390
|
+
await new Promise<boolean>((resolve) => {
|
|
391
|
+
if (jane.guid == null || john.guid == null) {
|
|
392
|
+
throw new Error('Entity is null.');
|
|
388
393
|
}
|
|
389
|
-
|
|
390
|
-
|
|
394
|
+
const subscription = pubsub.subscribeEntities(
|
|
395
|
+
{ class: Employee },
|
|
396
|
+
{
|
|
397
|
+
type: '&',
|
|
398
|
+
guid: jane.guid,
|
|
399
|
+
qref: [
|
|
400
|
+
'subordinates',
|
|
401
|
+
[{ class: Employee }, { type: '&', '!truthy': 'current' }],
|
|
402
|
+
],
|
|
403
|
+
},
|
|
404
|
+
)(async (update) => {
|
|
405
|
+
pubsub.updateArray(entities, update);
|
|
406
|
+
|
|
407
|
+
if (entities.length) {
|
|
408
|
+
subscription.unsubscribe();
|
|
409
|
+
resolve(true);
|
|
410
|
+
} else if (Array.isArray(update)) {
|
|
411
|
+
expect(update.length).toEqual(0);
|
|
391
412
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
413
|
+
// John gets fired.
|
|
414
|
+
john.current = false;
|
|
415
|
+
await john.$save();
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
}),
|
|
419
|
+
).toEqual(true);
|
|
397
420
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
421
|
+
expect(entities.length).toEqual(1);
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it('notified of new match for qref query in transaction', async () => {
|
|
425
|
+
let entities: (EmployeeClass & EmployeeData)[] = [];
|
|
426
|
+
|
|
427
|
+
const john = await EmployeeModel.factory();
|
|
428
|
+
john.name = 'John Der';
|
|
429
|
+
john.current = true;
|
|
430
|
+
john.salary = 8000000;
|
|
431
|
+
john.startDate = Date.now();
|
|
432
|
+
john.subordinates = [];
|
|
433
|
+
john.title = 'Junior Person';
|
|
434
|
+
try {
|
|
435
|
+
await john.$save();
|
|
436
|
+
} catch (e: any) {
|
|
437
|
+
console.error('Error creating entity: ', e);
|
|
438
|
+
throw e;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const jane = await EmployeeModel.factory();
|
|
442
|
+
jane.name = 'Jane Doe';
|
|
443
|
+
jane.current = true;
|
|
444
|
+
jane.salary = 8000000;
|
|
445
|
+
jane.startDate = Date.now();
|
|
446
|
+
jane.subordinates = [john];
|
|
447
|
+
jane.title = 'Seniorer Person';
|
|
448
|
+
try {
|
|
449
|
+
await jane.$save();
|
|
450
|
+
} catch (e: any) {
|
|
451
|
+
console.error('Error creating entity: ', e);
|
|
452
|
+
throw e;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
async function createNewBoss() {
|
|
456
|
+
// Create employee that matches qref.
|
|
457
|
+
const tnymph = await nymphServer.startTransaction('qref-test');
|
|
458
|
+
const tnymphDeep = await tnymph.startTransaction('qref-deep-test');
|
|
459
|
+
|
|
460
|
+
const TEmployeeModel = tnymph.getEntityClass(EmployeeModel);
|
|
461
|
+
const newBoss = await TEmployeeModel.factory();
|
|
462
|
+
newBoss.name = 'Jill Doe';
|
|
463
|
+
newBoss.current = false;
|
|
464
|
+
newBoss.salary = 8000000;
|
|
465
|
+
newBoss.startDate = Date.now();
|
|
466
|
+
newBoss.subordinates = [john];
|
|
467
|
+
newBoss.title = 'Seniorer Person';
|
|
468
|
+
try {
|
|
469
|
+
await newBoss.$save();
|
|
470
|
+
} catch (e: any) {
|
|
471
|
+
console.error('Error creating entity: ', e);
|
|
472
|
+
throw e;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
newBoss.current = true;
|
|
476
|
+
try {
|
|
477
|
+
await newBoss.$save();
|
|
478
|
+
} catch (e: any) {
|
|
479
|
+
console.error('Error creating entity: ', e);
|
|
480
|
+
throw e;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
await tnymphDeep.commit('qref-deep-test');
|
|
484
|
+
await tnymph.commit('qref-test');
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
expect(
|
|
488
|
+
await new Promise<boolean>((resolve) => {
|
|
489
|
+
if (jane.guid == null || john.guid == null) {
|
|
490
|
+
throw new Error('Entity is null.');
|
|
401
491
|
}
|
|
402
|
-
|
|
403
|
-
|
|
492
|
+
const subscription = pubsub.subscribeEntities(
|
|
493
|
+
{ class: Employee },
|
|
494
|
+
{
|
|
495
|
+
type: '&',
|
|
496
|
+
truthy: 'current',
|
|
497
|
+
qref: [
|
|
498
|
+
'subordinates',
|
|
499
|
+
[{ class: Employee }, { type: '&', guid: john.guid }],
|
|
500
|
+
],
|
|
501
|
+
},
|
|
502
|
+
)(async (update) => {
|
|
503
|
+
pubsub.updateArray(entities, update);
|
|
504
|
+
|
|
505
|
+
if (Array.isArray(update)) {
|
|
506
|
+
expect(entities.length).toEqual(1);
|
|
507
|
+
await createNewBoss();
|
|
508
|
+
} else {
|
|
509
|
+
expect(entities.length).toEqual(2);
|
|
510
|
+
subscription.unsubscribe();
|
|
511
|
+
resolve(true);
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
}),
|
|
515
|
+
).toEqual(true);
|
|
404
516
|
|
|
405
|
-
expect(entities.length).toEqual(
|
|
517
|
+
expect(entities.length).toEqual(2);
|
|
406
518
|
});
|
|
407
519
|
|
|
408
520
|
it('notified of removed match for qref query', async () => {
|
|
409
521
|
let [jane, john] = await createBossJane();
|
|
410
522
|
let entities: (EmployeeClass & EmployeeData)[] = [];
|
|
411
523
|
|
|
412
|
-
await new Promise((resolve) => {
|
|
524
|
+
await new Promise<void>((resolve) => {
|
|
413
525
|
if (jane.guid == null || john.guid == null) {
|
|
414
526
|
throw new Error('Entity is null.');
|
|
415
527
|
}
|
|
@@ -422,13 +534,13 @@ describe('Nymph REST Server and Client', () => {
|
|
|
422
534
|
'subordinates',
|
|
423
535
|
[{ class: Employee }, { type: '&', truthy: 'current' }],
|
|
424
536
|
],
|
|
425
|
-
}
|
|
537
|
+
},
|
|
426
538
|
)(async (update) => {
|
|
427
539
|
pubsub.updateArray(entities, update);
|
|
428
540
|
|
|
429
541
|
if (!entities.length) {
|
|
430
542
|
subscription.unsubscribe();
|
|
431
|
-
resolve(
|
|
543
|
+
resolve();
|
|
432
544
|
} else if (Array.isArray(update)) {
|
|
433
545
|
expect(update.length).toEqual(1);
|
|
434
546
|
|
|
@@ -447,9 +559,9 @@ describe('Nymph REST Server and Client', () => {
|
|
|
447
559
|
let entities: (EmployeeClass & EmployeeData)[] = [];
|
|
448
560
|
|
|
449
561
|
// Wait for change to propagate. (Only needed since we're not going across network.)
|
|
450
|
-
await new Promise((resolve) => setTimeout(() => resolve(
|
|
562
|
+
await new Promise<void>((resolve) => setTimeout(() => resolve(), 10));
|
|
451
563
|
|
|
452
|
-
await new Promise((resolve) => {
|
|
564
|
+
await new Promise<void>((resolve) => {
|
|
453
565
|
// Should only receive 1 update, since we waited.
|
|
454
566
|
let updated = false;
|
|
455
567
|
if (jane.guid == null) {
|
|
@@ -460,13 +572,13 @@ describe('Nymph REST Server and Client', () => {
|
|
|
460
572
|
{
|
|
461
573
|
type: '&',
|
|
462
574
|
guid: jane.guid,
|
|
463
|
-
}
|
|
575
|
+
},
|
|
464
576
|
)(async (update) => {
|
|
465
577
|
pubsub.updateArray(entities, update);
|
|
466
578
|
|
|
467
579
|
if (updated) {
|
|
468
580
|
subscription.unsubscribe();
|
|
469
|
-
resolve(
|
|
581
|
+
resolve();
|
|
470
582
|
} else if (Array.isArray(update)) {
|
|
471
583
|
expect(update.length).toEqual(1);
|
|
472
584
|
expect(entities[0].salary).toEqual(8000000);
|
|
@@ -482,40 +594,36 @@ describe('Nymph REST Server and Client', () => {
|
|
|
482
594
|
});
|
|
483
595
|
|
|
484
596
|
it('notified of entity delete', async () => {
|
|
485
|
-
let jane
|
|
597
|
+
let jane: EmployeeClass & EmployeeData;
|
|
486
598
|
let entities: (EmployeeClass & EmployeeData)[] = [];
|
|
487
599
|
|
|
488
|
-
|
|
489
|
-
await new Promise((resolve) =>
|
|
490
|
-
|
|
491
|
-
let length: number = -1;
|
|
492
|
-
await new Promise((resolve) => {
|
|
493
|
-
let updated = false;
|
|
494
|
-
if (jane.guid == null) {
|
|
495
|
-
throw new Error('Entity is null.');
|
|
496
|
-
}
|
|
600
|
+
let removed = false;
|
|
601
|
+
await new Promise<void>((resolve) => {
|
|
497
602
|
const subscription = pubsub.subscribeEntities(
|
|
498
603
|
{ class: Employee },
|
|
499
604
|
{
|
|
500
605
|
type: '&',
|
|
501
606
|
equal: ['name', 'Jane Doe'],
|
|
502
|
-
}
|
|
607
|
+
},
|
|
503
608
|
)(async (update) => {
|
|
504
609
|
pubsub.updateArray(entities, update);
|
|
505
610
|
|
|
506
|
-
if (
|
|
611
|
+
if (Array.isArray(update)) {
|
|
612
|
+
jane = await createJane();
|
|
613
|
+
if (jane.guid == null) {
|
|
614
|
+
throw new Error('Entity is null.');
|
|
615
|
+
}
|
|
616
|
+
} else if ('added' in update) {
|
|
617
|
+
await entities.find((e) => e.guid === update.added)?.$delete();
|
|
618
|
+
} else if ('removed' in update && update.removed === jane.guid) {
|
|
507
619
|
subscription.unsubscribe();
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
expect(update.length).toBeGreaterThan(0);
|
|
511
|
-
updated = true;
|
|
512
|
-
length = update.length;
|
|
513
|
-
await jane.$delete();
|
|
620
|
+
removed = true;
|
|
621
|
+
resolve();
|
|
514
622
|
}
|
|
515
623
|
});
|
|
516
624
|
});
|
|
517
625
|
|
|
518
|
-
expect(
|
|
626
|
+
expect(removed).toBeTruthy();
|
|
519
627
|
});
|
|
520
628
|
|
|
521
629
|
it('entire match is updated', async () => {
|
|
@@ -524,9 +632,9 @@ describe('Nymph REST Server and Client', () => {
|
|
|
524
632
|
await createJane();
|
|
525
633
|
|
|
526
634
|
// Wait for change to propagate. (Only needed since we're not going across network.)
|
|
527
|
-
await new Promise((resolve) => setTimeout(() => resolve(
|
|
635
|
+
await new Promise<void>((resolve) => setTimeout(() => resolve(), 10));
|
|
528
636
|
|
|
529
|
-
await new Promise((resolve) => {
|
|
637
|
+
await new Promise<void>((resolve) => {
|
|
530
638
|
let receivedRemove = false;
|
|
531
639
|
let receivedAdd = false;
|
|
532
640
|
const subscription = pubsub.subscribeEntities(
|
|
@@ -534,20 +642,26 @@ describe('Nymph REST Server and Client', () => {
|
|
|
534
642
|
{
|
|
535
643
|
type: '&',
|
|
536
644
|
equal: ['name', 'Jane Doe'],
|
|
537
|
-
}
|
|
645
|
+
},
|
|
538
646
|
)(async (update) => {
|
|
539
647
|
pubsub.updateArray(entities, update);
|
|
540
648
|
|
|
541
649
|
if (jane) {
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
650
|
+
const updates =
|
|
651
|
+
'multiple' in update && update.multiple
|
|
652
|
+
? update.messages
|
|
653
|
+
: [update];
|
|
654
|
+
for (let curUpdate of updates) {
|
|
655
|
+
if ('removed' in curUpdate) {
|
|
656
|
+
receivedRemove = true;
|
|
657
|
+
}
|
|
658
|
+
if ('added' in curUpdate) {
|
|
659
|
+
receivedAdd = true;
|
|
660
|
+
}
|
|
661
|
+
if (receivedAdd && receivedRemove) {
|
|
662
|
+
subscription.unsubscribe();
|
|
663
|
+
resolve();
|
|
664
|
+
}
|
|
551
665
|
}
|
|
552
666
|
} else if (Array.isArray(update)) {
|
|
553
667
|
expect(update.length).toEqual(1);
|
|
@@ -563,12 +677,13 @@ describe('Nymph REST Server and Client', () => {
|
|
|
563
677
|
it('entity subscription is updated', async () => {
|
|
564
678
|
let jane = await createJane();
|
|
565
679
|
|
|
566
|
-
await new Promise(async (resolve) => {
|
|
680
|
+
await new Promise<void>(async (resolve) => {
|
|
567
681
|
let mdate = 0;
|
|
568
682
|
const subscription = pubsub.subscribeWith(jane, async () => {
|
|
683
|
+
expect(jane.guid).not.toBeNull();
|
|
569
684
|
if (mdate > 0 && (jane.mdate ?? -1) === mdate) {
|
|
570
685
|
subscription.unsubscribe();
|
|
571
|
-
resolve(
|
|
686
|
+
resolve();
|
|
572
687
|
}
|
|
573
688
|
});
|
|
574
689
|
|
|
@@ -588,17 +703,86 @@ describe('Nymph REST Server and Client', () => {
|
|
|
588
703
|
expect(jane.salary).toEqual(9000000);
|
|
589
704
|
});
|
|
590
705
|
|
|
706
|
+
it("doesn't allow subscription of a restricted entity class", async () => {
|
|
707
|
+
let receivedBadUpdate = false;
|
|
708
|
+
let error = null;
|
|
709
|
+
|
|
710
|
+
await new Promise<void>((resolve) => {
|
|
711
|
+
pubsub.subscribeEntities(
|
|
712
|
+
{ class: Restricted },
|
|
713
|
+
{
|
|
714
|
+
type: '&',
|
|
715
|
+
equal: ['name', 'Jane Doe'],
|
|
716
|
+
},
|
|
717
|
+
)(
|
|
718
|
+
async () => {
|
|
719
|
+
receivedBadUpdate = true;
|
|
720
|
+
resolve();
|
|
721
|
+
},
|
|
722
|
+
(e: any) => {
|
|
723
|
+
error = e;
|
|
724
|
+
resolve();
|
|
725
|
+
},
|
|
726
|
+
);
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
expect(receivedBadUpdate).toEqual(false);
|
|
730
|
+
expect(error).toEqual('Not accessible.');
|
|
731
|
+
});
|
|
732
|
+
|
|
733
|
+
it("doesn't notify of new pubsub disabled entity class", async () => {
|
|
734
|
+
let receivedBadUpdate = false;
|
|
735
|
+
|
|
736
|
+
await new Promise<void>(async (resolve) => {
|
|
737
|
+
const subscription = await new Promise<
|
|
738
|
+
PubSubSubscription<
|
|
739
|
+
PubSubUpdate<(PubSubDisabledClass & PubSubDisabledData)[]>
|
|
740
|
+
>
|
|
741
|
+
>(async (resolve) => {
|
|
742
|
+
let updated = false;
|
|
743
|
+
const subscription = pubsub.subscribeEntities({
|
|
744
|
+
class: PubSubDisabled,
|
|
745
|
+
})(async (update) => {
|
|
746
|
+
if (updated) {
|
|
747
|
+
receivedBadUpdate = true;
|
|
748
|
+
} else {
|
|
749
|
+
expect(update).toEqual([]);
|
|
750
|
+
updated = true;
|
|
751
|
+
try {
|
|
752
|
+
const entity = await PubSubDisabled.factory();
|
|
753
|
+
entity.name = 'Someone';
|
|
754
|
+
try {
|
|
755
|
+
await entity.$save();
|
|
756
|
+
} catch (e: any) {
|
|
757
|
+
throw new Error("Couldn't save.");
|
|
758
|
+
}
|
|
759
|
+
resolve(subscription);
|
|
760
|
+
} catch (e: any) {
|
|
761
|
+
console.error('Error creating entity: ', e);
|
|
762
|
+
throw e;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
});
|
|
766
|
+
});
|
|
767
|
+
await new Promise<void>((resolve) => setTimeout(resolve, 200));
|
|
768
|
+
subscription.unsubscribe();
|
|
769
|
+
resolve();
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
expect(receivedBadUpdate).toEqual(false);
|
|
773
|
+
});
|
|
774
|
+
|
|
591
775
|
it('new uid', async () => {
|
|
592
|
-
await new Promise(async (resolve) => {
|
|
776
|
+
await new Promise<void>(async (resolve) => {
|
|
593
777
|
const subscription = pubsub.subscribeUID('testNewUID')(
|
|
594
778
|
async (value) => {
|
|
595
779
|
expect(value).toEqual(directValue);
|
|
596
780
|
subscription.unsubscribe();
|
|
597
|
-
resolve(
|
|
781
|
+
resolve();
|
|
598
782
|
},
|
|
599
783
|
(err) => {
|
|
600
784
|
expect(err.status).toEqual(404);
|
|
601
|
-
}
|
|
785
|
+
},
|
|
602
786
|
);
|
|
603
787
|
|
|
604
788
|
const directValue = await nymph.newUID('testNewUID');
|
|
@@ -606,49 +790,58 @@ describe('Nymph REST Server and Client', () => {
|
|
|
606
790
|
});
|
|
607
791
|
|
|
608
792
|
it('increasing uids', async () => {
|
|
609
|
-
await new Promise(async (
|
|
793
|
+
await new Promise<void>(async (finalResolve) => {
|
|
610
794
|
let receivedFirst = false;
|
|
611
795
|
let lastUpdate: number = 0;
|
|
796
|
+
|
|
797
|
+
// Don't go too fast.
|
|
798
|
+
let { promise, resolve } = Promise.withResolvers<void>();
|
|
799
|
+
|
|
612
800
|
const subscription = pubsub.subscribeUID('testIncUID')(
|
|
613
801
|
async (value) => {
|
|
614
802
|
if (!receivedFirst) {
|
|
615
803
|
receivedFirst = true;
|
|
616
804
|
expect(value).toBeNull();
|
|
805
|
+
resolve();
|
|
617
806
|
return;
|
|
618
807
|
}
|
|
619
808
|
|
|
620
809
|
expect(value).toEqual(lastUpdate + 1);
|
|
621
810
|
lastUpdate = value;
|
|
811
|
+
resolve();
|
|
812
|
+
|
|
622
813
|
if (value == 100) {
|
|
623
814
|
subscription.unsubscribe();
|
|
624
|
-
|
|
815
|
+
finalResolve();
|
|
625
816
|
}
|
|
626
817
|
},
|
|
627
818
|
(err) => {
|
|
628
819
|
expect(err.status).toEqual(404);
|
|
629
|
-
}
|
|
820
|
+
},
|
|
630
821
|
);
|
|
631
822
|
|
|
632
823
|
await nymph.deleteUID('testIncUID');
|
|
633
824
|
|
|
634
825
|
let directValue: number = -1;
|
|
635
826
|
while (directValue < 100) {
|
|
827
|
+
await promise;
|
|
828
|
+
({ promise, resolve } = Promise.withResolvers<void>());
|
|
636
829
|
directValue = await nymph.newUID('testIncUID');
|
|
637
830
|
}
|
|
638
831
|
});
|
|
639
832
|
});
|
|
640
833
|
|
|
641
834
|
it('set uid', async () => {
|
|
642
|
-
await new Promise(async (resolve) => {
|
|
835
|
+
await new Promise<void>(async (resolve) => {
|
|
643
836
|
const subscription = pubsub.subscribeUID('testSetUID')(
|
|
644
837
|
async (value) => {
|
|
645
838
|
expect(value).toEqual(123);
|
|
646
839
|
subscription.unsubscribe();
|
|
647
|
-
resolve(
|
|
840
|
+
resolve();
|
|
648
841
|
},
|
|
649
842
|
(err) => {
|
|
650
843
|
expect(err.status).toEqual(404);
|
|
651
|
-
}
|
|
844
|
+
},
|
|
652
845
|
);
|
|
653
846
|
|
|
654
847
|
await nymph.setUID('testSetUID', 123);
|
|
@@ -656,7 +849,7 @@ describe('Nymph REST Server and Client', () => {
|
|
|
656
849
|
});
|
|
657
850
|
|
|
658
851
|
it('rename uid from old name', async () => {
|
|
659
|
-
await new Promise(async (resolve) => {
|
|
852
|
+
await new Promise<void>(async (resolve) => {
|
|
660
853
|
let updated = false;
|
|
661
854
|
const subscription = pubsub.subscribeUID('testRenameUID')(
|
|
662
855
|
async (value, event) => {
|
|
@@ -664,15 +857,15 @@ describe('Nymph REST Server and Client', () => {
|
|
|
664
857
|
expect(event).toEqual('renameUID');
|
|
665
858
|
expect(value).toEqual(null);
|
|
666
859
|
subscription.unsubscribe();
|
|
667
|
-
resolve(
|
|
668
|
-
} else {
|
|
860
|
+
resolve();
|
|
861
|
+
} else if (event === 'setUID') {
|
|
669
862
|
expect(value).toEqual(456);
|
|
670
863
|
updated = true;
|
|
671
864
|
}
|
|
672
865
|
},
|
|
673
866
|
(err) => {
|
|
674
867
|
expect(err.status).toEqual(404);
|
|
675
|
-
}
|
|
868
|
+
},
|
|
676
869
|
);
|
|
677
870
|
|
|
678
871
|
await nymph.setUID('testRenameUID', 456);
|
|
@@ -681,17 +874,17 @@ describe('Nymph REST Server and Client', () => {
|
|
|
681
874
|
});
|
|
682
875
|
|
|
683
876
|
it('rename uid from new name', async () => {
|
|
684
|
-
await new Promise(async (resolve) => {
|
|
877
|
+
await new Promise<void>(async (resolve) => {
|
|
685
878
|
const subscription = pubsub.subscribeUID('newRename2UID')(
|
|
686
879
|
async (value, event) => {
|
|
687
880
|
expect(event).toEqual('setUID');
|
|
688
881
|
expect(value).toEqual(456);
|
|
689
882
|
subscription.unsubscribe();
|
|
690
|
-
resolve(
|
|
883
|
+
resolve();
|
|
691
884
|
},
|
|
692
885
|
(err) => {
|
|
693
886
|
expect(err.status).toEqual(404);
|
|
694
|
-
}
|
|
887
|
+
},
|
|
695
888
|
);
|
|
696
889
|
|
|
697
890
|
await nymph.setUID('testRename2UID', 456);
|
|
@@ -702,38 +895,51 @@ describe('Nymph REST Server and Client', () => {
|
|
|
702
895
|
it('delete uid', async () => {
|
|
703
896
|
await nymph.setUID('testDeleteUID', 789);
|
|
704
897
|
|
|
705
|
-
await new Promise(async (resolve) => {
|
|
898
|
+
await new Promise<void>(async (resolve) => {
|
|
706
899
|
let updated = false;
|
|
707
900
|
const subscription = pubsub.subscribeUID('testDeleteUID')(
|
|
708
901
|
async (value, event) => {
|
|
709
902
|
if (updated && event === 'deleteUID') {
|
|
710
903
|
expect(value).toEqual(null);
|
|
711
904
|
subscription.unsubscribe();
|
|
712
|
-
resolve(
|
|
905
|
+
resolve();
|
|
713
906
|
} else {
|
|
714
907
|
expect(value).toEqual(789);
|
|
715
908
|
updated = true;
|
|
909
|
+
await nymph.deleteUID('testDeleteUID');
|
|
716
910
|
}
|
|
717
911
|
},
|
|
718
912
|
(err) => {
|
|
719
913
|
expect(err.status).toEqual(404);
|
|
720
|
-
}
|
|
914
|
+
},
|
|
721
915
|
);
|
|
722
|
-
|
|
723
|
-
await nymph.deleteUID('testDeleteUID');
|
|
724
916
|
});
|
|
725
917
|
});
|
|
726
918
|
|
|
919
|
+
beforeEach(async () => {
|
|
920
|
+
pubsub.connect();
|
|
921
|
+
while (!pubsub.isConnectionOpen()) {
|
|
922
|
+
await new Promise<void>((resolve) => setTimeout(resolve, 20));
|
|
923
|
+
}
|
|
924
|
+
});
|
|
925
|
+
|
|
727
926
|
afterAll(async () => {
|
|
728
|
-
//
|
|
729
|
-
|
|
927
|
+
// Don't publish anything after the tests.
|
|
928
|
+
removePublisher();
|
|
929
|
+
|
|
930
|
+
// Avoid jest open handle errors.
|
|
931
|
+
const closed = new Promise<void>((resolve) => {
|
|
730
932
|
pubsub.on('disconnect', () => {
|
|
731
|
-
resolve(
|
|
933
|
+
resolve();
|
|
732
934
|
});
|
|
733
935
|
});
|
|
734
936
|
pubsub.close(); // close PubSub client.
|
|
735
937
|
await closed;
|
|
736
938
|
pubsubServer.close(); // close PubSub server.
|
|
737
939
|
server.close(); // close REST server.
|
|
940
|
+
|
|
941
|
+
// Wait for the next event loop to prevent websocket importing something
|
|
942
|
+
// after jest teardown.
|
|
943
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
738
944
|
});
|
|
739
945
|
});
|