document-drive 1.0.0-alpha.80 → 1.0.0-alpha.81
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/package.json +1 -1
- package/src/server/index.ts +93 -16
- package/src/server/listener/manager.ts +87 -70
- package/src/server/types.ts +6 -3
- package/src/storage/prisma.ts +1 -1
package/package.json
CHANGED
package/src/server/index.ts
CHANGED
|
@@ -194,7 +194,14 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
194
194
|
const drive = await this.getDrive(driveId);
|
|
195
195
|
let driveTriggers = this.triggerMap.get(driveId);
|
|
196
196
|
|
|
197
|
-
const syncUnits = await this.getSynchronizationUnitsIds(
|
|
197
|
+
const syncUnits = await this.getSynchronizationUnitsIds(
|
|
198
|
+
driveId,
|
|
199
|
+
undefined,
|
|
200
|
+
undefined,
|
|
201
|
+
undefined,
|
|
202
|
+
undefined,
|
|
203
|
+
drive
|
|
204
|
+
);
|
|
198
205
|
|
|
199
206
|
for (const trigger of drive.state.local.triggers) {
|
|
200
207
|
if (driveTriggers?.get(trigger.id)) {
|
|
@@ -374,9 +381,10 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
374
381
|
documentId?: string[],
|
|
375
382
|
scope?: string[],
|
|
376
383
|
branch?: string[],
|
|
377
|
-
documentType?: string[]
|
|
384
|
+
documentType?: string[],
|
|
385
|
+
loadedDrive?: DocumentDriveDocument
|
|
378
386
|
) {
|
|
379
|
-
const drive = await this.getDrive(driveId);
|
|
387
|
+
const drive = loadedDrive || (await this.getDrive(driveId));
|
|
380
388
|
|
|
381
389
|
const synchronizationUnitsQuery = await this.getSynchronizationUnitsIds(
|
|
382
390
|
driveId,
|
|
@@ -490,9 +498,10 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
490
498
|
|
|
491
499
|
public async getSynchronizationUnitIdInfo(
|
|
492
500
|
driveId: string,
|
|
493
|
-
syncId: string
|
|
501
|
+
syncId: string,
|
|
502
|
+
loadedDrive?: DocumentDriveDocument
|
|
494
503
|
): Promise<SynchronizationUnitQuery | undefined> {
|
|
495
|
-
const drive = await this.getDrive(driveId);
|
|
504
|
+
const drive = loadedDrive || (await this.getDrive(driveId));
|
|
496
505
|
const node = drive.state.global.nodes.find(
|
|
497
506
|
node =>
|
|
498
507
|
isFileNode(node) &&
|
|
@@ -523,11 +532,13 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
523
532
|
|
|
524
533
|
public async getSynchronizationUnit(
|
|
525
534
|
driveId: string,
|
|
526
|
-
syncId: string
|
|
535
|
+
syncId: string,
|
|
536
|
+
loadedDrive?: DocumentDriveDocument
|
|
527
537
|
): Promise<SynchronizationUnit | undefined> {
|
|
528
538
|
const syncUnit = await this.getSynchronizationUnitIdInfo(
|
|
529
539
|
driveId,
|
|
530
|
-
syncId
|
|
540
|
+
syncId,
|
|
541
|
+
loadedDrive
|
|
531
542
|
);
|
|
532
543
|
|
|
533
544
|
if (!syncUnit) {
|
|
@@ -559,12 +570,17 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
559
570
|
filter: {
|
|
560
571
|
since?: string | undefined;
|
|
561
572
|
fromRevision?: number | undefined;
|
|
562
|
-
}
|
|
573
|
+
},
|
|
574
|
+
loadedDrive?: DocumentDriveDocument
|
|
563
575
|
): Promise<OperationUpdate[]> {
|
|
564
576
|
const syncUnit =
|
|
565
577
|
syncId === '0'
|
|
566
578
|
? { documentId: '', scope: 'global' }
|
|
567
|
-
: await this.getSynchronizationUnitIdInfo(
|
|
579
|
+
: await this.getSynchronizationUnitIdInfo(
|
|
580
|
+
driveId,
|
|
581
|
+
syncId,
|
|
582
|
+
loadedDrive
|
|
583
|
+
);
|
|
568
584
|
|
|
569
585
|
if (!syncUnit) {
|
|
570
586
|
throw new Error(`Invalid Sync Id ${syncId} in drive ${driveId}`);
|
|
@@ -572,7 +588,7 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
572
588
|
|
|
573
589
|
const document =
|
|
574
590
|
syncId === '0'
|
|
575
|
-
? await this.getDrive(driveId)
|
|
591
|
+
? loadedDrive || (await this.getDrive(driveId))
|
|
576
592
|
: await this.getDocument(driveId, syncUnit.documentId); // TODO replace with getDocumentOperations
|
|
577
593
|
|
|
578
594
|
const operations =
|
|
@@ -830,19 +846,24 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
830
846
|
async _processOperations<T extends Document, A extends Action>(
|
|
831
847
|
drive: string,
|
|
832
848
|
documentId: string | undefined,
|
|
833
|
-
|
|
849
|
+
documentStorage: DocumentStorage<T>,
|
|
834
850
|
operations: Operation<A | BaseAction>[]
|
|
835
851
|
) {
|
|
836
852
|
const operationsApplied: Operation<A | BaseAction>[] = [];
|
|
837
853
|
const signals: SignalResult[] = [];
|
|
838
|
-
|
|
854
|
+
const documentStorageWithState = await this._addDocumentResultingStage(
|
|
855
|
+
documentStorage,
|
|
856
|
+
drive,
|
|
857
|
+
documentId
|
|
858
|
+
);
|
|
839
859
|
|
|
860
|
+
let document: T = this._buildDocument(documentStorageWithState);
|
|
840
861
|
let error: OperationError | undefined; // TODO: replace with an array of errors/consistency issues
|
|
841
862
|
const operationsByScope = groupOperationsByScope(operations);
|
|
842
863
|
|
|
843
864
|
for (const scope of Object.keys(operationsByScope)) {
|
|
844
865
|
const storageDocumentOperations =
|
|
845
|
-
|
|
866
|
+
documentStorage.operations[scope as OperationScope];
|
|
846
867
|
|
|
847
868
|
// TODO two equal operations done by two clients will be considered the same, ie: { type: "INCREMENT" }
|
|
848
869
|
const branch = removeExistingOperations(
|
|
@@ -922,6 +943,57 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
922
943
|
} as const;
|
|
923
944
|
}
|
|
924
945
|
|
|
946
|
+
private async _addDocumentResultingStage<T extends Document>(
|
|
947
|
+
document: DocumentStorage<T>,
|
|
948
|
+
drive: string,
|
|
949
|
+
documentId?: string,
|
|
950
|
+
options?: GetDocumentOptions
|
|
951
|
+
): Promise<DocumentStorage<T>> {
|
|
952
|
+
// apply skip header operations to all scopes
|
|
953
|
+
const operations =
|
|
954
|
+
options?.revisions !== undefined
|
|
955
|
+
? filterOperationsByRevision(
|
|
956
|
+
document.operations,
|
|
957
|
+
options.revisions
|
|
958
|
+
)
|
|
959
|
+
: document.operations;
|
|
960
|
+
const documentOperations =
|
|
961
|
+
DocumentUtils.documentHelpers.garbageCollectDocumentOperations(
|
|
962
|
+
operations
|
|
963
|
+
);
|
|
964
|
+
|
|
965
|
+
for (const scope of Object.keys(documentOperations)) {
|
|
966
|
+
const lastRemainingOperation =
|
|
967
|
+
documentOperations[scope as OperationScope].at(-1);
|
|
968
|
+
// if the latest operation doesn't have a resulting state then tries
|
|
969
|
+
// to retrieve it from the db to avoid rerunning all the operations
|
|
970
|
+
if (
|
|
971
|
+
lastRemainingOperation &&
|
|
972
|
+
!lastRemainingOperation.resultingState
|
|
973
|
+
) {
|
|
974
|
+
lastRemainingOperation.resultingState = await (documentId
|
|
975
|
+
? this.storage.getOperationResultingState?.(
|
|
976
|
+
drive,
|
|
977
|
+
documentId,
|
|
978
|
+
lastRemainingOperation.index,
|
|
979
|
+
lastRemainingOperation.scope,
|
|
980
|
+
'main'
|
|
981
|
+
)
|
|
982
|
+
: this.storage.getDriveOperationResultingState?.(
|
|
983
|
+
drive,
|
|
984
|
+
lastRemainingOperation.index,
|
|
985
|
+
lastRemainingOperation.scope,
|
|
986
|
+
'main'
|
|
987
|
+
));
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
return {
|
|
992
|
+
...document,
|
|
993
|
+
operations: documentOperations
|
|
994
|
+
};
|
|
995
|
+
}
|
|
996
|
+
|
|
925
997
|
private _buildDocument<T extends Document>(
|
|
926
998
|
documentStorage: DocumentStorage<T>,
|
|
927
999
|
options?: GetDocumentOptions
|
|
@@ -1614,14 +1686,12 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
1614
1686
|
}
|
|
1615
1687
|
|
|
1616
1688
|
const prevSyncUnits = await this.getSynchronizationUnitsIds(drive);
|
|
1617
|
-
|
|
1618
1689
|
try {
|
|
1619
1690
|
await this._addDriveOperations(drive, async documentStorage => {
|
|
1620
1691
|
const result = await this._processOperations<
|
|
1621
1692
|
DocumentDriveDocument,
|
|
1622
1693
|
DocumentDriveAction
|
|
1623
1694
|
>(drive, undefined, documentStorage, operations.slice());
|
|
1624
|
-
|
|
1625
1695
|
document = result.document;
|
|
1626
1696
|
operationsApplied.push(...result.operationsApplied);
|
|
1627
1697
|
signals.push(...result.signals);
|
|
@@ -1654,7 +1724,14 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
1654
1724
|
}
|
|
1655
1725
|
}
|
|
1656
1726
|
|
|
1657
|
-
const syncUnits = await this.getSynchronizationUnitsIds(
|
|
1727
|
+
const syncUnits = await this.getSynchronizationUnitsIds(
|
|
1728
|
+
drive,
|
|
1729
|
+
undefined,
|
|
1730
|
+
undefined,
|
|
1731
|
+
undefined,
|
|
1732
|
+
undefined,
|
|
1733
|
+
document
|
|
1734
|
+
);
|
|
1658
1735
|
|
|
1659
1736
|
const prevSyncUnitsIds = prevSyncUnits.map(unit => unit.syncId);
|
|
1660
1737
|
const syncUnitsIds = syncUnits.map(unit => unit.syncId);
|
|
@@ -239,43 +239,47 @@ export class ListenerManager extends BaseListenerManager {
|
|
|
239
239
|
);
|
|
240
240
|
|
|
241
241
|
const strandUpdates: StrandUpdate[] = [];
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
unitState &&
|
|
247
|
-
unitState.listenerRev >= syncUnit.revision
|
|
248
|
-
) {
|
|
249
|
-
continue;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
const opData: OperationUpdate[] = [];
|
|
253
|
-
try {
|
|
254
|
-
const data = await this.drive.getOperationData(
|
|
255
|
-
// TODO - join queries, DEAL WITH INVALID SYNC ID ERROR
|
|
256
|
-
driveId,
|
|
257
|
-
syncUnit.syncId,
|
|
258
|
-
{
|
|
259
|
-
fromRevision: unitState?.listenerRev
|
|
260
|
-
}
|
|
242
|
+
await Promise.all(
|
|
243
|
+
syncUnits.map(async syncUnit => {
|
|
244
|
+
const unitState = listener.syncUnits.get(
|
|
245
|
+
syncUnit.syncId
|
|
261
246
|
);
|
|
262
|
-
opData.push(...data);
|
|
263
|
-
} catch (e) {
|
|
264
|
-
logger.error(e);
|
|
265
|
-
}
|
|
266
247
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
248
|
+
if (
|
|
249
|
+
unitState &&
|
|
250
|
+
unitState.listenerRev >= syncUnit.revision
|
|
251
|
+
) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
270
254
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
255
|
+
const opData: OperationUpdate[] = [];
|
|
256
|
+
try {
|
|
257
|
+
const data = await this.drive.getOperationData(
|
|
258
|
+
// TODO - join queries, DEAL WITH INVALID SYNC ID ERROR
|
|
259
|
+
driveId,
|
|
260
|
+
syncUnit.syncId,
|
|
261
|
+
{
|
|
262
|
+
fromRevision: unitState?.listenerRev
|
|
263
|
+
}
|
|
264
|
+
);
|
|
265
|
+
opData.push(...data);
|
|
266
|
+
} catch (e) {
|
|
267
|
+
logger.error(e);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (!opData.length) {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
strandUpdates.push({
|
|
275
|
+
driveId,
|
|
276
|
+
documentId: syncUnit.documentId,
|
|
277
|
+
branch: syncUnit.branch,
|
|
278
|
+
operations: opData,
|
|
279
|
+
scope: syncUnit.scope as OperationScope
|
|
280
|
+
});
|
|
281
|
+
})
|
|
282
|
+
);
|
|
279
283
|
|
|
280
284
|
if (strandUpdates.length == 0) {
|
|
281
285
|
continue;
|
|
@@ -369,7 +373,11 @@ export class ListenerManager extends BaseListenerManager {
|
|
|
369
373
|
return false;
|
|
370
374
|
}
|
|
371
375
|
|
|
372
|
-
getListenerSyncUnits(
|
|
376
|
+
getListenerSyncUnits(
|
|
377
|
+
driveId: string,
|
|
378
|
+
listenerId: string,
|
|
379
|
+
loadedDrive?: DocumentDriveDocument
|
|
380
|
+
) {
|
|
373
381
|
const listener = this.listenerState.get(driveId)?.get(listenerId);
|
|
374
382
|
if (!listener) {
|
|
375
383
|
return [];
|
|
@@ -380,7 +388,8 @@ export class ListenerManager extends BaseListenerManager {
|
|
|
380
388
|
filter.documentId ?? ['*'],
|
|
381
389
|
filter.scope ?? ['*'],
|
|
382
390
|
filter.branch ?? ['*'],
|
|
383
|
-
filter.documentType ?? ['*']
|
|
391
|
+
filter.documentType ?? ['*'],
|
|
392
|
+
loadedDrive
|
|
384
393
|
);
|
|
385
394
|
}
|
|
386
395
|
|
|
@@ -453,45 +462,53 @@ export class ListenerManager extends BaseListenerManager {
|
|
|
453
462
|
// fetch operations from drive and prepare strands
|
|
454
463
|
const strands: StrandUpdate[] = [];
|
|
455
464
|
|
|
456
|
-
const
|
|
465
|
+
const drive = await this.drive.getDrive(driveId);
|
|
466
|
+
const syncUnits = await this.getListenerSyncUnits(
|
|
467
|
+
driveId,
|
|
468
|
+
listenerId,
|
|
469
|
+
drive
|
|
470
|
+
);
|
|
457
471
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
472
|
+
await Promise.all(
|
|
473
|
+
syncUnits.map(async syncUnit => {
|
|
474
|
+
if (syncUnit.revision < 0) {
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
const entry = listener.syncUnits.get(syncUnit.syncId);
|
|
478
|
+
if (entry && entry.listenerRev >= syncUnit.revision) {
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
466
481
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
482
|
+
const { documentId, driveId, scope, branch } = syncUnit;
|
|
483
|
+
try {
|
|
484
|
+
const operations = await this.drive.getOperationData(
|
|
485
|
+
// DEAL WITH INVALID SYNC ID ERROR
|
|
486
|
+
driveId,
|
|
487
|
+
syncUnit.syncId,
|
|
488
|
+
{
|
|
489
|
+
since,
|
|
490
|
+
fromRevision: entry?.listenerRev
|
|
491
|
+
},
|
|
492
|
+
drive
|
|
493
|
+
);
|
|
494
|
+
|
|
495
|
+
if (!operations.length) {
|
|
496
|
+
return;
|
|
476
497
|
}
|
|
477
|
-
);
|
|
478
498
|
|
|
479
|
-
|
|
480
|
-
|
|
499
|
+
strands.push({
|
|
500
|
+
driveId,
|
|
501
|
+
documentId,
|
|
502
|
+
scope: scope as OperationScope,
|
|
503
|
+
branch,
|
|
504
|
+
operations
|
|
505
|
+
});
|
|
506
|
+
} catch (error) {
|
|
507
|
+
logger.error(error);
|
|
508
|
+
return;
|
|
481
509
|
}
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
driveId,
|
|
485
|
-
documentId,
|
|
486
|
-
scope: scope as OperationScope,
|
|
487
|
-
branch,
|
|
488
|
-
operations
|
|
489
|
-
});
|
|
490
|
-
} catch (error) {
|
|
491
|
-
logger.error(error);
|
|
492
|
-
continue;
|
|
493
|
-
}
|
|
494
|
-
}
|
|
510
|
+
})
|
|
511
|
+
);
|
|
495
512
|
|
|
496
513
|
return strands;
|
|
497
514
|
}
|
package/src/server/types.ts
CHANGED
|
@@ -289,12 +289,14 @@ export abstract class BaseDocumentDriveServer {
|
|
|
289
289
|
documentId?: string[],
|
|
290
290
|
scope?: string[],
|
|
291
291
|
branch?: string[],
|
|
292
|
-
documentType?: string[]
|
|
292
|
+
documentType?: string[],
|
|
293
|
+
loadedDrive?: DocumentDriveDocument
|
|
293
294
|
): Promise<SynchronizationUnit[]>;
|
|
294
295
|
|
|
295
296
|
abstract getSynchronizationUnit(
|
|
296
297
|
driveId: string,
|
|
297
|
-
syncId: string
|
|
298
|
+
syncId: string,
|
|
299
|
+
loadedDrive?: DocumentDriveDocument
|
|
298
300
|
): Promise<SynchronizationUnit | undefined>;
|
|
299
301
|
|
|
300
302
|
abstract getSynchronizationUnitsIds(
|
|
@@ -311,7 +313,8 @@ export abstract class BaseDocumentDriveServer {
|
|
|
311
313
|
filter: {
|
|
312
314
|
since?: string;
|
|
313
315
|
fromRevision?: number;
|
|
314
|
-
}
|
|
316
|
+
},
|
|
317
|
+
loadedDrive?: DocumentDriveDocument
|
|
315
318
|
): Promise<OperationUpdate[]>;
|
|
316
319
|
|
|
317
320
|
/** Internal methods **/
|
package/src/storage/prisma.ts
CHANGED
|
@@ -309,7 +309,7 @@ export class PrismaStorage implements IDriveStorage {
|
|
|
309
309
|
header
|
|
310
310
|
);
|
|
311
311
|
},
|
|
312
|
-
{ isolationLevel: 'Serializable' }
|
|
312
|
+
{ isolationLevel: 'Serializable', maxWait: 10000, timeout: 20000 }
|
|
313
313
|
);
|
|
314
314
|
|
|
315
315
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|