document-drive 4.0.1 → 4.1.0-dev.1
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/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/prisma/schema.prisma +1 -2
- package/dist/src/drive-document-model/gen/document-model.js +3 -3
- package/dist/src/drive-document-model/gen/document-model.js.map +1 -1
- package/dist/src/drive-document-model/gen/node/creators.d.ts +6 -2
- package/dist/src/drive-document-model/gen/node/creators.d.ts.map +1 -1
- package/dist/src/drive-document-model/gen/node/creators.js +8 -1
- package/dist/src/drive-document-model/gen/node/creators.js.map +1 -1
- package/dist/src/drive-document-model/gen/schema/types.d.ts +1 -14
- package/dist/src/drive-document-model/gen/schema/types.d.ts.map +1 -1
- package/dist/src/drive-document-model/gen/schema/zod.d.ts +1 -5
- package/dist/src/drive-document-model/gen/schema/zod.d.ts.map +1 -1
- package/dist/src/drive-document-model/gen/schema/zod.js +0 -12
- package/dist/src/drive-document-model/gen/schema/zod.js.map +1 -1
- package/dist/src/drive-document-model/gen/types.d.ts +13 -4
- package/dist/src/drive-document-model/gen/types.d.ts.map +1 -1
- package/dist/src/drive-document-model/gen/types.js.map +1 -1
- package/dist/src/drive-document-model/module.d.ts +2 -1
- package/dist/src/drive-document-model/module.d.ts.map +1 -1
- package/dist/src/drive-document-model/src/reducers/node.d.ts.map +1 -1
- package/dist/src/drive-document-model/src/reducers/node.js +1 -25
- package/dist/src/drive-document-model/src/reducers/node.js.map +1 -1
- package/dist/src/drive-document-model/src/tests/actions.test.js +1 -23
- package/dist/src/drive-document-model/src/tests/actions.test.js.map +1 -1
- package/dist/src/drive-document-model/src/tests/base.test.js +0 -32
- package/dist/src/drive-document-model/src/tests/base.test.js.map +1 -1
- package/dist/src/drive-document-model/src/tests/utils.test.js +7 -37
- package/dist/src/drive-document-model/src/tests/utils.test.js.map +1 -1
- package/dist/src/drive-document-model/src/utils.d.ts +2 -8
- package/dist/src/drive-document-model/src/utils.d.ts.map +1 -1
- package/dist/src/drive-document-model/src/utils.js +6 -55
- package/dist/src/drive-document-model/src/utils.js.map +1 -1
- package/dist/src/queue/base.d.ts +6 -27
- package/dist/src/queue/base.d.ts.map +1 -1
- package/dist/src/queue/base.js +15 -203
- package/dist/src/queue/base.js.map +1 -1
- package/dist/src/queue/event.d.ts +42 -0
- package/dist/src/queue/event.d.ts.map +1 -0
- package/dist/src/queue/event.js +222 -0
- package/dist/src/queue/event.js.map +1 -0
- package/dist/src/queue/redis.d.ts +1 -27
- package/dist/src/queue/redis.d.ts.map +1 -1
- package/dist/src/queue/redis.js +122 -110
- package/dist/src/queue/redis.js.map +1 -1
- package/dist/src/queue/types.d.ts +18 -12
- package/dist/src/queue/types.d.ts.map +1 -1
- package/dist/src/queue/types.js +3 -0
- package/dist/src/queue/types.js.map +1 -1
- package/dist/src/server/base-server.d.ts +87 -11
- package/dist/src/server/base-server.d.ts.map +1 -1
- package/dist/src/server/base-server.js +549 -250
- package/dist/src/server/base-server.js.map +1 -1
- package/dist/src/server/builder.js +2 -2
- package/dist/src/server/builder.js.map +1 -1
- package/dist/src/server/error.d.ts +3 -3
- package/dist/src/server/error.d.ts.map +1 -1
- package/dist/src/server/error.js +2 -2
- package/dist/src/server/error.js.map +1 -1
- package/dist/src/server/listener/listener-manager.d.ts +5 -6
- package/dist/src/server/listener/listener-manager.d.ts.map +1 -1
- package/dist/src/server/listener/listener-manager.js +62 -79
- package/dist/src/server/listener/listener-manager.js.map +1 -1
- package/dist/src/server/listener/transmitter/internal.d.ts +1 -1
- package/dist/src/server/listener/transmitter/internal.d.ts.map +1 -1
- package/dist/src/server/listener/transmitter/internal.js +14 -12
- package/dist/src/server/listener/transmitter/internal.js.map +1 -1
- package/dist/src/server/listener/transmitter/pull-responder.d.ts.map +1 -1
- package/dist/src/server/listener/transmitter/pull-responder.js +14 -8
- package/dist/src/server/listener/transmitter/pull-responder.js.map +1 -1
- package/dist/src/server/listener/transmitter/switchboard-push.d.ts.map +1 -1
- package/dist/src/server/listener/transmitter/switchboard-push.js +13 -8
- package/dist/src/server/listener/transmitter/switchboard-push.js.map +1 -1
- package/dist/src/server/sync-manager.d.ts +8 -10
- package/dist/src/server/sync-manager.d.ts.map +1 -1
- package/dist/src/server/sync-manager.js +62 -147
- package/dist/src/server/sync-manager.js.map +1 -1
- package/dist/src/server/sync-unit-map.d.ts +137 -0
- package/dist/src/server/sync-unit-map.d.ts.map +1 -0
- package/dist/src/server/sync-unit-map.js +234 -0
- package/dist/src/server/sync-unit-map.js.map +1 -0
- package/dist/src/server/types.d.ts +114 -31
- package/dist/src/server/types.d.ts.map +1 -1
- package/dist/src/server/types.js.map +1 -1
- package/dist/src/server/utils.d.ts +10 -1
- package/dist/src/server/utils.d.ts.map +1 -1
- package/dist/src/server/utils.js +41 -0
- package/dist/src/server/utils.js.map +1 -1
- package/dist/src/storage/browser.d.ts +7 -2
- package/dist/src/storage/browser.d.ts.map +1 -1
- package/dist/src/storage/browser.js +80 -22
- package/dist/src/storage/browser.js.map +1 -1
- package/dist/src/storage/filesystem.d.ts +7 -2
- package/dist/src/storage/filesystem.d.ts.map +1 -1
- package/dist/src/storage/filesystem.js +79 -22
- package/dist/src/storage/filesystem.js.map +1 -1
- package/dist/src/storage/memory.d.ts +7 -2
- package/dist/src/storage/memory.d.ts.map +1 -1
- package/dist/src/storage/memory.js +76 -22
- package/dist/src/storage/memory.js.map +1 -1
- package/dist/src/storage/prisma/client/edge.js +5 -4
- package/dist/src/storage/prisma/client/index-browser.js +2 -1
- package/dist/src/storage/prisma/client/index.d.ts +58 -249
- package/dist/src/storage/prisma/client/index.js +5 -4
- package/dist/src/storage/prisma/client/package.json +1 -1
- package/dist/src/storage/prisma/client/schema.prisma +2 -3
- package/dist/src/storage/prisma/client/wasm.js +2 -1
- package/dist/src/storage/prisma/prisma.d.ts +9 -4
- package/dist/src/storage/prisma/prisma.d.ts.map +1 -1
- package/dist/src/storage/prisma/prisma.js +119 -51
- package/dist/src/storage/prisma/prisma.js.map +1 -1
- package/dist/src/storage/types.d.ts +43 -4
- package/dist/src/storage/types.d.ts.map +1 -1
- package/dist/src/storage/utils.d.ts +3 -0
- package/dist/src/storage/utils.d.ts.map +1 -1
- package/dist/src/storage/utils.js +14 -0
- package/dist/src/storage/utils.js.map +1 -1
- package/dist/src/utils/gql-transformations.d.ts +14 -5
- package/dist/src/utils/gql-transformations.d.ts.map +1 -1
- package/dist/src/utils/gql-transformations.js +1 -0
- package/dist/src/utils/gql-transformations.js.map +1 -1
- package/dist/src/utils/misc.d.ts +2 -0
- package/dist/src/utils/misc.d.ts.map +1 -1
- package/dist/src/utils/misc.js +4 -0
- package/dist/src/utils/misc.js.map +1 -1
- package/dist/tsconfig.lib.tsbuildinfo +1 -0
- package/package.json +4 -4
- package/dist/test/cache.test.d.ts +0 -2
- package/dist/test/cache.test.d.ts.map +0 -1
- package/dist/test/cache.test.js +0 -281
- package/dist/test/cache.test.js.map +0 -1
- package/dist/test/default-remote-drives.test.d.ts +0 -2
- package/dist/test/default-remote-drives.test.d.ts.map +0 -1
- package/dist/test/default-remote-drives.test.js +0 -446
- package/dist/test/default-remote-drives.test.js.map +0 -1
- package/dist/test/document-helpers/addUndo.test.d.ts +0 -2
- package/dist/test/document-helpers/addUndo.test.d.ts.map +0 -1
- package/dist/test/document-helpers/addUndo.test.js +0 -120
- package/dist/test/document-helpers/addUndo.test.js.map +0 -1
- package/dist/test/document-helpers/attachBranch.test.d.ts +0 -2
- package/dist/test/document-helpers/attachBranch.test.d.ts.map +0 -1
- package/dist/test/document-helpers/attachBranch.test.js +0 -333
- package/dist/test/document-helpers/attachBranch.test.js.map +0 -1
- package/dist/test/document-helpers/checkCleanedOperationsIntegrity.test.d.ts +0 -2
- package/dist/test/document-helpers/checkCleanedOperationsIntegrity.test.d.ts.map +0 -1
- package/dist/test/document-helpers/checkCleanedOperationsIntegrity.test.js +0 -252
- package/dist/test/document-helpers/checkCleanedOperationsIntegrity.test.js.map +0 -1
- package/dist/test/document-helpers/garbageCollect.test.d.ts +0 -2
- package/dist/test/document-helpers/garbageCollect.test.d.ts.map +0 -1
- package/dist/test/document-helpers/garbageCollect.test.js +0 -136
- package/dist/test/document-helpers/garbageCollect.test.js.map +0 -1
- package/dist/test/document-helpers/groupOperationsByScope.test.d.ts +0 -2
- package/dist/test/document-helpers/groupOperationsByScope.test.d.ts.map +0 -1
- package/dist/test/document-helpers/groupOperationsByScope.test.js +0 -98
- package/dist/test/document-helpers/groupOperationsByScope.test.js.map +0 -1
- package/dist/test/document-helpers/merge.test.d.ts +0 -2
- package/dist/test/document-helpers/merge.test.d.ts.map +0 -1
- package/dist/test/document-helpers/merge.test.js +0 -757
- package/dist/test/document-helpers/merge.test.js.map +0 -1
- package/dist/test/document-helpers/nextSkipNumber.test.d.ts +0 -2
- package/dist/test/document-helpers/nextSkipNumber.test.d.ts.map +0 -1
- package/dist/test/document-helpers/nextSkipNumber.test.js +0 -123
- package/dist/test/document-helpers/nextSkipNumber.test.js.map +0 -1
- package/dist/test/document-helpers/prepareOperations.test.d.ts +0 -2
- package/dist/test/document-helpers/prepareOperations.test.d.ts.map +0 -1
- package/dist/test/document-helpers/prepareOperations.test.js +0 -304
- package/dist/test/document-helpers/prepareOperations.test.js.map +0 -1
- package/dist/test/document-helpers/removeExistingOperations.test.d.ts +0 -2
- package/dist/test/document-helpers/removeExistingOperations.test.d.ts.map +0 -1
- package/dist/test/document-helpers/removeExistingOperations.test.js +0 -150
- package/dist/test/document-helpers/removeExistingOperations.test.js.map +0 -1
- package/dist/test/document-helpers/reshuffleByTimestamp.test.d.ts +0 -2
- package/dist/test/document-helpers/reshuffleByTimestamp.test.d.ts.map +0 -1
- package/dist/test/document-helpers/reshuffleByTimestamp.test.js +0 -148
- package/dist/test/document-helpers/reshuffleByTimestamp.test.js.map +0 -1
- package/dist/test/document-helpers/reshuffleByTimestampAndIndex.test.d.ts +0 -2
- package/dist/test/document-helpers/reshuffleByTimestampAndIndex.test.d.ts.map +0 -1
- package/dist/test/document-helpers/reshuffleByTimestampAndIndex.test.js +0 -200
- package/dist/test/document-helpers/reshuffleByTimestampAndIndex.test.js.map +0 -1
- package/dist/test/document-helpers/sortOperations.test.d.ts +0 -2
- package/dist/test/document-helpers/sortOperations.test.d.ts.map +0 -1
- package/dist/test/document-helpers/sortOperations.test.js +0 -101
- package/dist/test/document-helpers/sortOperations.test.js.map +0 -1
- package/dist/test/document-helpers/split.test.d.ts +0 -2
- package/dist/test/document-helpers/split.test.d.ts.map +0 -1
- package/dist/test/document-helpers/split.test.js +0 -121
- package/dist/test/document-helpers/split.test.js.map +0 -1
- package/dist/test/document-helpers/utils.d.ts +0 -8
- package/dist/test/document-helpers/utils.d.ts.map +0 -1
- package/dist/test/document-helpers/utils.js +0 -22
- package/dist/test/document-helpers/utils.js.map +0 -1
- package/dist/test/drive-operations.test.d.ts +0 -2
- package/dist/test/drive-operations.test.d.ts.map +0 -1
- package/dist/test/drive-operations.test.js +0 -145
- package/dist/test/drive-operations.test.js.map +0 -1
- package/dist/test/graphql.test.d.ts +0 -2
- package/dist/test/graphql.test.d.ts.map +0 -1
- package/dist/test/graphql.test.js +0 -10
- package/dist/test/graphql.test.js.map +0 -1
- package/dist/test/internal-listener.test.d.ts +0 -2
- package/dist/test/internal-listener.test.d.ts.map +0 -1
- package/dist/test/internal-listener.test.js +0 -277
- package/dist/test/internal-listener.test.js.map +0 -1
- package/dist/test/queue.test.d.ts +0 -2
- package/dist/test/queue.test.d.ts.map +0 -1
- package/dist/test/queue.test.js +0 -338
- package/dist/test/queue.test.js.map +0 -1
- package/dist/test/read-mode.test.d.ts +0 -2
- package/dist/test/read-mode.test.d.ts.map +0 -1
- package/dist/test/read-mode.test.js +0 -578
- package/dist/test/read-mode.test.js.map +0 -1
- package/dist/test/server/driveOperationsConflictResolution.test.d.ts +0 -2
- package/dist/test/server/driveOperationsConflictResolution.test.d.ts.map +0 -1
- package/dist/test/server/driveOperationsConflictResolution.test.js +0 -460
- package/dist/test/server/driveOperationsConflictResolution.test.js.map +0 -1
- package/dist/test/server/mergeOperations.test.d.ts +0 -2
- package/dist/test/server/mergeOperations.test.d.ts.map +0 -1
- package/dist/test/server/mergeOperations.test.js +0 -107
- package/dist/test/server/mergeOperations.test.js.map +0 -1
- package/dist/test/server/processOperations.test.d.ts +0 -2
- package/dist/test/server/processOperations.test.d.ts.map +0 -1
- package/dist/test/server/processOperations.test.js +0 -380
- package/dist/test/server/processOperations.test.js.map +0 -1
- package/dist/test/server.test.d.ts +0 -2
- package/dist/test/server.test.d.ts.map +0 -1
- package/dist/test/server.test.js +0 -899
- package/dist/test/server.test.js.map +0 -1
- package/dist/test/signature-migration.test.d.ts +0 -2
- package/dist/test/signature-migration.test.d.ts.map +0 -1
- package/dist/test/signature-migration.test.js +0 -239
- package/dist/test/signature-migration.test.js.map +0 -1
- package/dist/test/storage.test.d.ts +0 -2
- package/dist/test/storage.test.d.ts.map +0 -1
- package/dist/test/storage.test.js +0 -523
- package/dist/test/storage.test.js.map +0 -1
- package/dist/test/utils.d.ts +0 -48
- package/dist/test/utils.d.ts.map +0 -1
- package/dist/test/utils.js +0 -133
- package/dist/test/utils.js.map +0 -1
- package/dist/test/utils.test.d.ts +0 -2
- package/dist/test/utils.test.d.ts.map +0 -1
- package/dist/test/utils.test.js +0 -89
- package/dist/test/utils.test.js.map +0 -1
- package/dist/test/vitest-setup.d.ts +0 -2
- package/dist/test/vitest-setup.d.ts.map +0 -1
- package/dist/test/vitest-setup.js +0 -5
- package/dist/test/vitest-setup.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/dist/vitest.config.d.ts +0 -3
- package/dist/vitest.config.d.ts.map +0 -1
- package/dist/vitest.config.js +0 -27
- package/dist/vitest.config.js.map +0 -1
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { removeListener, removeTrigger, setSharingType, } from "#drive-document-model/gen/creators";
|
|
2
2
|
import { createDocument } from "#drive-document-model/gen/utils";
|
|
3
|
-
import { isActionJob, isOperationJob, } from "#queue/types";
|
|
3
|
+
import { isActionJob, isDocumentJob, isOperationJob, } from "#queue/types";
|
|
4
4
|
import { ReadModeServer } from "#read-mode/server";
|
|
5
5
|
import { DefaultDrivesManager, } from "#utils/default-drives-manager";
|
|
6
6
|
import { requestPublicDriveWithTokenFromReactor } from "#utils/graphql";
|
|
7
7
|
import { isDocumentDrive, runAsapAsync } from "#utils/misc";
|
|
8
8
|
import { RunAsap } from "#utils/run-asap";
|
|
9
|
-
import { childLogger, } from "document-drive";
|
|
9
|
+
import { DocumentAlreadyExistsError, childLogger, } from "document-drive";
|
|
10
10
|
import { attachBranch, createPresignedHeader, garbageCollect, garbageCollectDocumentOperations, groupOperationsByScope, merge, precedes, removeExistingOperations, replayDocument, reshuffleByTimestamp, skipHeaderOperations, sortOperations, validateHeader, } from "document-model";
|
|
11
11
|
import { ClientError } from "graphql-request";
|
|
12
12
|
import { ConflictOperationError, OperationError, } from "./error.js";
|
|
13
13
|
import { PullResponderTransmitter, } from "./listener/transmitter/pull-responder.js";
|
|
14
14
|
import { SwitchboardPushTransmitter } from "./listener/transmitter/switchboard-push.js";
|
|
15
15
|
import { DefaultListenerManagerOptions, } from "./types.js";
|
|
16
|
-
import { filterOperationsByRevision, isAtRevision } from "./utils.js";
|
|
16
|
+
import { filterOperationsByRevision, isAtRevision, resolveCreateDocumentInput, } from "./utils.js";
|
|
17
17
|
export class BaseDocumentDriveServer {
|
|
18
18
|
logger = childLogger(["BaseDocumentDriveServer"]);
|
|
19
19
|
// external dependencies
|
|
@@ -35,15 +35,47 @@ export class BaseDocumentDriveServer {
|
|
|
35
35
|
};
|
|
36
36
|
queueDelegate = {
|
|
37
37
|
exists: (documentId) => this.documentStorage.exists(documentId),
|
|
38
|
-
processOperationJob: async ({
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
processOperationJob: async ({ documentId, operations, options, }) => {
|
|
39
|
+
const document = await this.getDocument(documentId);
|
|
40
|
+
return isDocumentDrive(document)
|
|
41
|
+
? this.processDriveOperations(documentId, operations, options)
|
|
42
|
+
: this.processOperations(documentId, operations, options);
|
|
42
43
|
},
|
|
43
|
-
processActionJob: async ({
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
processActionJob: async ({ documentId, actions, options }) => {
|
|
45
|
+
const document = await this.getDocument(documentId);
|
|
46
|
+
return isDocumentDrive(document)
|
|
47
|
+
? this.processDriveActions(documentId, actions, options)
|
|
48
|
+
: this.processActions(documentId, actions, options);
|
|
49
|
+
},
|
|
50
|
+
processDocumentJob: async ({ documentId, documentType, initialState, options, }) => {
|
|
51
|
+
const documentModelModule = this.getDocumentModelModule(documentType);
|
|
52
|
+
const document = documentModelModule.utils.createDocument({
|
|
53
|
+
...initialState,
|
|
54
|
+
});
|
|
55
|
+
// TODO: header must be included
|
|
56
|
+
const header = createPresignedHeader(documentId, documentType);
|
|
57
|
+
document.header.id = documentId;
|
|
58
|
+
document.header.sig = header.sig;
|
|
59
|
+
document.header.documentType = documentType;
|
|
60
|
+
try {
|
|
61
|
+
const createdDocument = await this.createDocument({ document }, options?.source ?? { type: "local" }, initialState?.header.meta);
|
|
62
|
+
return {
|
|
63
|
+
status: "SUCCESS",
|
|
64
|
+
operations: [],
|
|
65
|
+
document: createdDocument,
|
|
66
|
+
signals: [],
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
const cause = error instanceof Error ? error : new Error(JSON.stringify(error));
|
|
71
|
+
return {
|
|
72
|
+
status: "ERROR",
|
|
73
|
+
error: new OperationError("ERROR", undefined, `Error creating document: ${cause.message}`, cause),
|
|
74
|
+
operations: [],
|
|
75
|
+
document: undefined,
|
|
76
|
+
signals: [],
|
|
77
|
+
};
|
|
78
|
+
}
|
|
47
79
|
},
|
|
48
80
|
processJob: async (job) => {
|
|
49
81
|
if (isOperationJob(job)) {
|
|
@@ -52,6 +84,9 @@ export class BaseDocumentDriveServer {
|
|
|
52
84
|
else if (isActionJob(job)) {
|
|
53
85
|
return this.queueDelegate.processActionJob(job);
|
|
54
86
|
}
|
|
87
|
+
else if (isDocumentJob(job)) {
|
|
88
|
+
return this.queueDelegate.processDocumentJob(job);
|
|
89
|
+
}
|
|
55
90
|
else {
|
|
56
91
|
throw new Error("Unknown job type", job);
|
|
57
92
|
}
|
|
@@ -98,7 +133,7 @@ export class BaseDocumentDriveServer {
|
|
|
98
133
|
return this.initializePromise;
|
|
99
134
|
}
|
|
100
135
|
async _initialize() {
|
|
101
|
-
await this.listenerManager.initialize(this.handleListenerError);
|
|
136
|
+
await this.listenerManager.initialize(this.handleListenerError.bind(this));
|
|
102
137
|
await this.queueManager.init(this.queueDelegate, (error) => {
|
|
103
138
|
this.logger.error(`Error initializing queue manager`, error);
|
|
104
139
|
errors.push(error);
|
|
@@ -153,7 +188,7 @@ export class BaseDocumentDriveServer {
|
|
|
153
188
|
}
|
|
154
189
|
async startSyncRemoteDrive(driveId) {
|
|
155
190
|
let driveTriggers = this.triggerMap.get(driveId);
|
|
156
|
-
const syncUnits = await this.getSynchronizationUnitsIds(driveId);
|
|
191
|
+
const syncUnits = await this.synchronizationManager.getSynchronizationUnitsIds(driveId);
|
|
157
192
|
const drive = await this.getDrive(driveId);
|
|
158
193
|
for (const trigger of drive.state.local.triggers) {
|
|
159
194
|
if (driveTriggers?.get(trigger.id)) {
|
|
@@ -166,7 +201,7 @@ export class BaseDocumentDriveServer {
|
|
|
166
201
|
pull: "SYNCING",
|
|
167
202
|
});
|
|
168
203
|
for (const syncUnit of syncUnits) {
|
|
169
|
-
this.synchronizationManager.updateSyncStatus(syncUnit
|
|
204
|
+
this.synchronizationManager.updateSyncStatus(syncUnit, {
|
|
170
205
|
pull: "SYNCING",
|
|
171
206
|
});
|
|
172
207
|
}
|
|
@@ -178,46 +213,43 @@ export class BaseDocumentDriveServer {
|
|
|
178
213
|
if (error instanceof ClientError) {
|
|
179
214
|
this.eventEmitter.emit("clientStrandsError", driveId, trigger, error.response.status, error.message);
|
|
180
215
|
}
|
|
181
|
-
}, (revisions) => {
|
|
182
|
-
const
|
|
183
|
-
if (
|
|
216
|
+
}, async (revisions) => {
|
|
217
|
+
const errorRevisions = revisions.filter((r) => r.status !== "SUCCESS");
|
|
218
|
+
if (errorRevisions.length < 1) {
|
|
184
219
|
this.synchronizationManager.updateSyncStatus(driveId, {
|
|
185
220
|
pull: "SUCCESS",
|
|
186
221
|
});
|
|
187
222
|
}
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
.
|
|
191
|
-
|
|
192
|
-
.then((revSyncUnits) => {
|
|
193
|
-
for (const syncUnit of revSyncUnits) {
|
|
194
|
-
const fileErrorRevision = errorRevision.find((r) => r.documentId === syncUnit.documentId);
|
|
195
|
-
if (fileErrorRevision) {
|
|
196
|
-
this.synchronizationManager.updateSyncStatus(syncUnit.syncId, { pull: fileErrorRevision.status }, fileErrorRevision.error);
|
|
197
|
-
}
|
|
198
|
-
else {
|
|
199
|
-
this.synchronizationManager.updateSyncStatus(syncUnit.syncId, {
|
|
200
|
-
pull: "SUCCESS",
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
})
|
|
205
|
-
.catch(console.error);
|
|
223
|
+
for (const revision of revisions) {
|
|
224
|
+
const { documentId, scope, branch, status, error } = revision;
|
|
225
|
+
this.synchronizationManager.updateSyncStatus({ documentId, scope, branch }, { pull: status }, error);
|
|
226
|
+
}
|
|
206
227
|
// if it is the first pull and returns empty
|
|
207
|
-
// then updates
|
|
228
|
+
// then updates drive documents to "SUCCESS" and
|
|
229
|
+
// updates corresponding push transmitter
|
|
208
230
|
if (firstPull) {
|
|
209
231
|
firstPull = false;
|
|
232
|
+
const syncUnitsIds = await this.synchronizationManager.getSynchronizationUnitsIds(driveId);
|
|
233
|
+
const unchangedSyncUnits = syncUnitsIds.filter((syncUnit) => {
|
|
234
|
+
return !revisions.find((revision) => {
|
|
235
|
+
return (revision.documentId === syncUnit.documentId &&
|
|
236
|
+
revision.scope === syncUnit.scope &&
|
|
237
|
+
revision.branch === syncUnit.branch);
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
unchangedSyncUnits.forEach((syncUnit) => {
|
|
241
|
+
this.synchronizationManager.updateSyncStatus(syncUnit, {
|
|
242
|
+
pull: "SUCCESS",
|
|
243
|
+
});
|
|
244
|
+
});
|
|
210
245
|
const pushListener = drive.state.local.listeners.find((listener) => trigger.data.url === listener.callInfo?.data);
|
|
211
246
|
if (pushListener) {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
}
|
|
219
|
-
})
|
|
220
|
-
.catch(this.logger.error);
|
|
247
|
+
for (const revision of revisions) {
|
|
248
|
+
const { documentId, scope, branch } = revision;
|
|
249
|
+
this.listenerManager
|
|
250
|
+
.updateListenerRevision(pushListener.listenerId, driveId, { documentId, scope, branch }, revision.revision)
|
|
251
|
+
.catch(this.logger.error);
|
|
252
|
+
}
|
|
221
253
|
}
|
|
222
254
|
}
|
|
223
255
|
}, undefined, this.listeners);
|
|
@@ -227,15 +259,12 @@ export class BaseDocumentDriveServer {
|
|
|
227
259
|
}
|
|
228
260
|
}
|
|
229
261
|
async stopSyncRemoteDrive(driveId) {
|
|
230
|
-
const syncUnits = await this.getSynchronizationUnitsIds(driveId);
|
|
231
|
-
const filesNodeSyncId = syncUnits
|
|
232
|
-
.filter((syncUnit) => syncUnit.documentId !== "")
|
|
233
|
-
.map((syncUnit) => syncUnit.syncId);
|
|
234
262
|
const triggers = this.triggerMap.get(driveId);
|
|
235
263
|
triggers?.forEach((cancel) => cancel());
|
|
236
264
|
this.synchronizationManager.updateSyncStatus(driveId, null);
|
|
237
|
-
|
|
238
|
-
|
|
265
|
+
const syncUnits = await this.synchronizationManager.getSynchronizationUnitsIds(driveId);
|
|
266
|
+
for (const syncUnit of syncUnits) {
|
|
267
|
+
this.synchronizationManager.updateSyncStatus(syncUnit, null);
|
|
239
268
|
}
|
|
240
269
|
return this.triggerMap.delete(driveId);
|
|
241
270
|
}
|
|
@@ -298,19 +327,6 @@ export class BaseDocumentDriveServer {
|
|
|
298
327
|
}
|
|
299
328
|
}
|
|
300
329
|
}
|
|
301
|
-
// Delegate synchronization methods to synchronizationManager
|
|
302
|
-
getSynchronizationUnits(driveId, documentId, scope, branch, documentType) {
|
|
303
|
-
return this.synchronizationManager.getSynchronizationUnits(driveId, documentId, scope, branch, documentType);
|
|
304
|
-
}
|
|
305
|
-
getSynchronizationUnitsIds(driveId, documentId, scope, branch, documentType) {
|
|
306
|
-
return this.synchronizationManager.getSynchronizationUnitsIds(driveId, documentId, scope, branch, documentType);
|
|
307
|
-
}
|
|
308
|
-
getOperationData(driveId, syncId, filter) {
|
|
309
|
-
return this.synchronizationManager.getOperationData(driveId, syncId, filter);
|
|
310
|
-
}
|
|
311
|
-
getSynchronizationUnitsRevision(driveId, syncUnitsQuery) {
|
|
312
|
-
return this.synchronizationManager.getSynchronizationUnitsRevision(driveId, syncUnitsQuery);
|
|
313
|
-
}
|
|
314
330
|
getDocumentModelModule(documentType) {
|
|
315
331
|
const documentModelModule = this.documentModelModules.find((module) => module.documentModel.id === documentType);
|
|
316
332
|
if (!documentModelModule) {
|
|
@@ -321,6 +337,9 @@ export class BaseDocumentDriveServer {
|
|
|
321
337
|
getDocumentModelModules() {
|
|
322
338
|
return [...this.documentModelModules];
|
|
323
339
|
}
|
|
340
|
+
addDocument(document, meta) {
|
|
341
|
+
return this.createDocument({ document }, { type: "local" }, meta);
|
|
342
|
+
}
|
|
324
343
|
async addDrive(input, preferredEditor) {
|
|
325
344
|
const document = createDocument({
|
|
326
345
|
state: {
|
|
@@ -428,6 +447,7 @@ export class BaseDocumentDriveServer {
|
|
|
428
447
|
}
|
|
429
448
|
else {
|
|
430
449
|
if (!options?.revisions) {
|
|
450
|
+
this.cache.setDocument(driveId, result).catch(this.logger.error);
|
|
431
451
|
this.cache.setDrive(driveId, result).catch(this.logger.error);
|
|
432
452
|
}
|
|
433
453
|
return result;
|
|
@@ -466,7 +486,12 @@ export class BaseDocumentDriveServer {
|
|
|
466
486
|
const driveStorage = await this.documentStorage.getBySlug(slug);
|
|
467
487
|
return driveStorage.header.id;
|
|
468
488
|
}
|
|
469
|
-
|
|
489
|
+
getDocument(driveId, documentId, options) {
|
|
490
|
+
const id = typeof documentId === "string" ? documentId : driveId;
|
|
491
|
+
const resolvedOptions = typeof documentId === "object" ? documentId : options;
|
|
492
|
+
return this._getDocument(id, resolvedOptions);
|
|
493
|
+
}
|
|
494
|
+
async _getDocument(documentId, options) {
|
|
470
495
|
let cachedDocument;
|
|
471
496
|
try {
|
|
472
497
|
cachedDocument = await this.cache.getDocument(documentId); // TODO support GetDocumentOptions
|
|
@@ -487,115 +512,166 @@ export class BaseDocumentDriveServer {
|
|
|
487
512
|
getDocuments(driveId) {
|
|
488
513
|
return this.documentStorage.getChildren(driveId);
|
|
489
514
|
}
|
|
490
|
-
async
|
|
515
|
+
async addChild(parentId, documentId) {
|
|
516
|
+
// TODO: check if document exists? Should that be a concern here?
|
|
517
|
+
try {
|
|
518
|
+
await this.documentStorage.addChild(parentId, documentId);
|
|
519
|
+
// TODO: update listener manager?
|
|
520
|
+
}
|
|
521
|
+
catch (e) {
|
|
522
|
+
this.logger.error("Error adding child document", e);
|
|
523
|
+
throw e;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
async removeChild(parentId, documentId) {
|
|
527
|
+
// TODO: check if document exists? Should that be a concern here?
|
|
528
|
+
// cleanup child sync units state from the parent listeners
|
|
529
|
+
try {
|
|
530
|
+
const childSynUnits = await this.synchronizationManager.getSynchronizationUnitsIds(parentId, [
|
|
531
|
+
documentId,
|
|
532
|
+
]);
|
|
533
|
+
await this.listenerManager.removeSyncUnits(parentId, childSynUnits);
|
|
534
|
+
}
|
|
535
|
+
catch (e) {
|
|
536
|
+
this.logger.warn("Error removing sync units of child", e);
|
|
537
|
+
}
|
|
538
|
+
// remove child relationship from storage
|
|
539
|
+
try {
|
|
540
|
+
await this.documentStorage.removeChild(parentId, documentId);
|
|
541
|
+
}
|
|
542
|
+
catch (e) {
|
|
543
|
+
this.logger.error("Error adding child document", e);
|
|
544
|
+
throw e;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
async createDocument(input, source, meta) {
|
|
548
|
+
const { documentType, document: inputDocument } = resolveCreateDocumentInput(input);
|
|
491
549
|
// if a document was provided then checks if it's valid
|
|
492
550
|
let state = undefined;
|
|
493
|
-
if (
|
|
494
|
-
if (
|
|
495
|
-
|
|
551
|
+
if (inputDocument) {
|
|
552
|
+
if ("documentType" in input &&
|
|
553
|
+
documentType !== inputDocument.header.documentType) {
|
|
554
|
+
throw new Error(`Provided document is not ${documentType}`);
|
|
496
555
|
}
|
|
497
|
-
const doc = this._buildDocument(
|
|
556
|
+
const doc = this._buildDocument(inputDocument);
|
|
498
557
|
state = doc.state;
|
|
499
558
|
}
|
|
500
559
|
// if no document was provided then create a new one
|
|
501
|
-
const document =
|
|
502
|
-
this.getDocumentModelModule(
|
|
560
|
+
const document = inputDocument ??
|
|
561
|
+
this.getDocumentModelModule(documentType).utils.createDocument({
|
|
562
|
+
state,
|
|
563
|
+
});
|
|
503
564
|
// get the header
|
|
504
565
|
let header;
|
|
505
566
|
// handle the legacy case where an id is provided
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
if (input.document) {
|
|
567
|
+
if ("id" in input && input.id) {
|
|
568
|
+
if (inputDocument) {
|
|
509
569
|
header = document.header;
|
|
510
|
-
// eslint-disable-next-line
|
|
511
570
|
document.header.id = input.id;
|
|
512
571
|
this.logger.warn("Assigning an id to a document is deprecated. Use the header field instead.");
|
|
513
572
|
}
|
|
514
573
|
else {
|
|
515
574
|
this.logger.warn("Creating a document with an id is deprecated. Use the header field instead.");
|
|
516
|
-
header = createPresignedHeader();
|
|
517
|
-
// eslint-disable-next-line
|
|
518
|
-
header.id = input.id;
|
|
519
|
-
header.documentType = input.documentType;
|
|
575
|
+
header = createPresignedHeader(input.id, documentType);
|
|
520
576
|
}
|
|
521
577
|
}
|
|
522
|
-
else if (input
|
|
578
|
+
else if ("header" in input) {
|
|
523
579
|
// validate the header passed in
|
|
524
580
|
await validateHeader(input.header);
|
|
525
581
|
header = input.header;
|
|
526
582
|
}
|
|
583
|
+
else if (inputDocument?.header) {
|
|
584
|
+
if (!inputDocument.header.id) {
|
|
585
|
+
throw new Error("Document header id is required");
|
|
586
|
+
}
|
|
587
|
+
if (!inputDocument.header.documentType) {
|
|
588
|
+
throw new Error("Document header documentType is required");
|
|
589
|
+
}
|
|
590
|
+
if (!inputDocument.header.createdAtUtcIso) {
|
|
591
|
+
throw new Error("Document header createdAtUtcIso is required");
|
|
592
|
+
}
|
|
593
|
+
if (!inputDocument.header.sig.nonce) {
|
|
594
|
+
this.logger.warn("Creating a document with an unsigned id is deprecated. Use createSignedHeaderForSigner.");
|
|
595
|
+
// throw new Error("Document header sig nonce is required"); TODO: uncomment when ready to enforce signed documents
|
|
596
|
+
}
|
|
597
|
+
else {
|
|
598
|
+
await validateHeader(inputDocument.header);
|
|
599
|
+
}
|
|
600
|
+
header = inputDocument.header;
|
|
601
|
+
}
|
|
527
602
|
else {
|
|
528
603
|
// otherwise, generate a header
|
|
529
|
-
header = createPresignedHeader();
|
|
530
|
-
|
|
604
|
+
header = createPresignedHeader(undefined, documentType);
|
|
605
|
+
}
|
|
606
|
+
if (meta) {
|
|
607
|
+
header.meta = { ...header.meta, ...meta };
|
|
531
608
|
}
|
|
532
609
|
// stores document information
|
|
533
610
|
const documentStorage = {
|
|
534
611
|
header,
|
|
535
612
|
history: document.history,
|
|
536
|
-
operations:
|
|
613
|
+
operations: { global: [], local: [] },
|
|
537
614
|
initialState: document.initialState,
|
|
538
615
|
clipboard: [],
|
|
616
|
+
attachments: document.attachments,
|
|
539
617
|
state: state ?? document.state,
|
|
540
618
|
};
|
|
541
619
|
await this.documentStorage.create(documentStorage);
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
//
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
push: this.listenerManager.driveHasListeners(driveId)
|
|
561
|
-
? "SUCCESS"
|
|
562
|
-
: undefined,
|
|
563
|
-
});
|
|
564
|
-
}
|
|
620
|
+
// TODO set initial state for document sync units
|
|
621
|
+
// if (source.type === "trigger") {
|
|
622
|
+
// for (const scope of Object.keys(document.state)) {
|
|
623
|
+
// this.synchronizationManager.updateSyncStatus(
|
|
624
|
+
// {
|
|
625
|
+
// documentId: document.id,
|
|
626
|
+
// scope,
|
|
627
|
+
// branch: "main" /* TODO handle branches */,
|
|
628
|
+
// },
|
|
629
|
+
// {
|
|
630
|
+
// pull: "INITIAL_SYNC",
|
|
631
|
+
// push: this.listenerManager.driveHasListeners(driveId)
|
|
632
|
+
// ? "SUCCESS"
|
|
633
|
+
// : undefined,
|
|
634
|
+
// },
|
|
635
|
+
// );
|
|
636
|
+
// }
|
|
637
|
+
// }
|
|
565
638
|
// if the document contains operations then
|
|
566
639
|
// stores the operations in the storage
|
|
567
640
|
const operations = Object.values(document.operations).flat();
|
|
568
641
|
if (operations.length) {
|
|
569
642
|
if (isDocumentDrive(document)) {
|
|
570
|
-
await this.legacyStorage.addDriveOperations(
|
|
643
|
+
await this.legacyStorage.addDriveOperations(header.id, operations, document);
|
|
571
644
|
}
|
|
572
645
|
else {
|
|
573
|
-
await this.legacyStorage.addDocumentOperations(
|
|
646
|
+
await this.legacyStorage.addDocumentOperations(header.id, operations, document);
|
|
574
647
|
}
|
|
575
648
|
}
|
|
576
649
|
return document;
|
|
577
650
|
}
|
|
578
|
-
async deleteDocument(
|
|
651
|
+
async deleteDocument(documentId) {
|
|
579
652
|
try {
|
|
580
|
-
const syncUnits = await this.getSynchronizationUnitsIds(
|
|
581
|
-
documentId,
|
|
582
|
-
]);
|
|
653
|
+
const syncUnits = await this.synchronizationManager.getSynchronizationUnitsIds(undefined, [documentId]);
|
|
583
654
|
// remove document sync units status when a document is deleted
|
|
584
655
|
for (const syncUnit of syncUnits) {
|
|
585
|
-
this.synchronizationManager.updateSyncStatus(syncUnit
|
|
656
|
+
this.synchronizationManager.updateSyncStatus(syncUnit, null);
|
|
657
|
+
}
|
|
658
|
+
const parents = await this.documentStorage.getParents(documentId);
|
|
659
|
+
for (const parent of parents) {
|
|
660
|
+
this.listenerManager
|
|
661
|
+
.removeSyncUnits(parent, syncUnits)
|
|
662
|
+
.catch(this.logger.warn);
|
|
586
663
|
}
|
|
587
|
-
await this.listenerManager.removeSyncUnits(driveId, syncUnits);
|
|
588
664
|
}
|
|
589
665
|
catch (error) {
|
|
590
666
|
this.logger.warn("Error deleting document", error);
|
|
591
667
|
}
|
|
592
668
|
await this.cache.deleteDocument(documentId);
|
|
593
|
-
|
|
669
|
+
await this.documentStorage.delete(documentId);
|
|
594
670
|
}
|
|
595
|
-
async _processOperations(
|
|
671
|
+
async _processOperations(documentId, documentStorage, operations) {
|
|
596
672
|
const operationsApplied = [];
|
|
597
673
|
const signals = [];
|
|
598
|
-
const documentStorageWithState = await this._addDocumentResultingStage(documentStorage,
|
|
674
|
+
const documentStorageWithState = await this._addDocumentResultingStage(documentStorage, documentId);
|
|
599
675
|
let document = this._buildDocument(documentStorageWithState);
|
|
600
676
|
let error; // TODO: replace with an array of errors/consistency issues
|
|
601
677
|
const operationsByScope = groupOperationsByScope(operations);
|
|
@@ -627,7 +703,7 @@ export class BaseDocumentDriveServer {
|
|
|
627
703
|
try {
|
|
628
704
|
// runs operation on next available tick, to avoid blocking the main thread
|
|
629
705
|
const taskQueueMethod = this.options.taskQueueMethod;
|
|
630
|
-
const task = () => this._performOperation(
|
|
706
|
+
const task = () => this._performOperation(documentId, document, nextOperation, skipHashValidation);
|
|
631
707
|
const appliedResult = await (taskQueueMethod
|
|
632
708
|
? runAsapAsync(task, taskQueueMethod)
|
|
633
709
|
: task());
|
|
@@ -653,7 +729,7 @@ export class BaseDocumentDriveServer {
|
|
|
653
729
|
error,
|
|
654
730
|
};
|
|
655
731
|
}
|
|
656
|
-
async _addDocumentResultingStage(document,
|
|
732
|
+
async _addDocumentResultingStage(document, documentId, options) {
|
|
657
733
|
// apply skip header operations to all scopes
|
|
658
734
|
const operations = options?.revisions !== undefined
|
|
659
735
|
? filterOperationsByRevision(document.operations, options.revisions)
|
|
@@ -664,9 +740,9 @@ export class BaseDocumentDriveServer {
|
|
|
664
740
|
// if the latest operation doesn't have a resulting state then tries
|
|
665
741
|
// to retrieve it from the db to avoid rerunning all the operations
|
|
666
742
|
if (lastRemainingOperation && !lastRemainingOperation.resultingState) {
|
|
667
|
-
lastRemainingOperation.resultingState = await (
|
|
668
|
-
? this.legacyStorage.getOperationResultingState?.(
|
|
669
|
-
: this.legacyStorage.getDriveOperationResultingState?.(
|
|
743
|
+
lastRemainingOperation.resultingState = await (isDocumentDrive(document)
|
|
744
|
+
? this.legacyStorage.getOperationResultingState?.(documentId, lastRemainingOperation.index, lastRemainingOperation.scope, "main")
|
|
745
|
+
: this.legacyStorage.getDriveOperationResultingState?.(documentId, lastRemainingOperation.index, lastRemainingOperation.scope, "main"));
|
|
670
746
|
}
|
|
671
747
|
}
|
|
672
748
|
return {
|
|
@@ -691,7 +767,7 @@ export class BaseDocumentDriveServer {
|
|
|
691
767
|
reuseOperationResultingState: options?.checkHashes ?? true,
|
|
692
768
|
});
|
|
693
769
|
}
|
|
694
|
-
async _performOperation(
|
|
770
|
+
async _performOperation(documentId, document, operation, skipHashValidation = false) {
|
|
695
771
|
const documentModelModule = this.getDocumentModelModule(document.header.documentType);
|
|
696
772
|
const signalResults = [];
|
|
697
773
|
let newDocument = document;
|
|
@@ -704,36 +780,22 @@ export class BaseDocumentDriveServer {
|
|
|
704
780
|
// if the latest operation doesn't have a resulting state then tries
|
|
705
781
|
// to retrieve it from the db to avoid rerunning all the operations
|
|
706
782
|
if (lastRemainingOperation && !lastRemainingOperation.resultingState) {
|
|
707
|
-
lastRemainingOperation.resultingState = await (
|
|
708
|
-
? this.legacyStorage.getOperationResultingState?.(
|
|
709
|
-
: this.legacyStorage.getDriveOperationResultingState?.(
|
|
783
|
+
lastRemainingOperation.resultingState = await (isDocumentDrive(document)
|
|
784
|
+
? this.legacyStorage.getOperationResultingState?.(documentId, lastRemainingOperation.index, lastRemainingOperation.scope, "main")
|
|
785
|
+
: this.legacyStorage.getDriveOperationResultingState?.(documentId, lastRemainingOperation.index, lastRemainingOperation.scope, "main"));
|
|
710
786
|
}
|
|
711
787
|
const operationSignals = [];
|
|
712
788
|
newDocument = documentModelModule.reducer(newDocument, operation, (signal) => {
|
|
713
789
|
let handler = undefined;
|
|
714
790
|
switch (signal.type) {
|
|
715
791
|
case "CREATE_CHILD_DOCUMENT":
|
|
716
|
-
handler = () => this.
|
|
792
|
+
handler = () => this.addChild(documentId, signal.input.id);
|
|
717
793
|
break;
|
|
718
794
|
case "DELETE_CHILD_DOCUMENT":
|
|
719
|
-
handler = () => this.
|
|
795
|
+
handler = () => this.removeChild(documentId, signal.input.id);
|
|
720
796
|
break;
|
|
721
797
|
case "COPY_CHILD_DOCUMENT":
|
|
722
|
-
handler = () => this.
|
|
723
|
-
const doc = {
|
|
724
|
-
...documentToCopy,
|
|
725
|
-
header: {
|
|
726
|
-
...documentToCopy.header,
|
|
727
|
-
slug: signal.input.newId,
|
|
728
|
-
},
|
|
729
|
-
};
|
|
730
|
-
return this.createDocument(driveId, {
|
|
731
|
-
id: signal.input.newId,
|
|
732
|
-
documentType: documentToCopy.header.documentType,
|
|
733
|
-
document: doc,
|
|
734
|
-
synchronizationUnits: signal.input.synchronizationUnits,
|
|
735
|
-
});
|
|
736
|
-
});
|
|
798
|
+
handler = () => this.addChild(documentId, signal.input.newId);
|
|
737
799
|
break;
|
|
738
800
|
}
|
|
739
801
|
if (handler) {
|
|
@@ -748,6 +810,7 @@ export class BaseDocumentDriveServer {
|
|
|
748
810
|
if (!appliedOperation.error &&
|
|
749
811
|
appliedOperation.hash !== operation.hash &&
|
|
750
812
|
!skipHashValidation) {
|
|
813
|
+
this.logger.warn(JSON.stringify(appliedOperation, null, 2));
|
|
751
814
|
throw new ConflictOperationError(operation, appliedOperation);
|
|
752
815
|
}
|
|
753
816
|
for (const signalHandler of operationSignals) {
|
|
@@ -760,28 +823,103 @@ export class BaseDocumentDriveServer {
|
|
|
760
823
|
operation: appliedOperation,
|
|
761
824
|
};
|
|
762
825
|
}
|
|
763
|
-
addOperation(
|
|
764
|
-
|
|
826
|
+
addOperation(driveIdOrDocumentId, documentIdOrOperation, operationOrOptions, maybeOptions) {
|
|
827
|
+
let documentId;
|
|
828
|
+
let operation;
|
|
829
|
+
let options;
|
|
830
|
+
if (typeof documentIdOrOperation === "string") {
|
|
831
|
+
// Deprecated overload: (driveId, documentId, operation, options)
|
|
832
|
+
documentId = documentIdOrOperation;
|
|
833
|
+
operation = operationOrOptions;
|
|
834
|
+
options = maybeOptions;
|
|
835
|
+
}
|
|
836
|
+
else {
|
|
837
|
+
// Standard overload: (documentId, operation, options)
|
|
838
|
+
documentId = driveIdOrDocumentId;
|
|
839
|
+
operation = documentIdOrOperation;
|
|
840
|
+
options = operationOrOptions;
|
|
841
|
+
}
|
|
842
|
+
return this.addOperations(documentId, [operation], options);
|
|
765
843
|
}
|
|
766
|
-
async _addOperations(
|
|
844
|
+
async _addOperations(documentId, callback) {
|
|
767
845
|
if (!this.legacyStorage.addDocumentOperationsWithTransaction) {
|
|
768
846
|
const documentStorage = await this.documentStorage.get(documentId);
|
|
769
847
|
const result = await callback(documentStorage);
|
|
770
848
|
// saves the applied operations to storage
|
|
771
849
|
if (result.operations.length > 0) {
|
|
772
|
-
await this.legacyStorage.addDocumentOperations(
|
|
850
|
+
await this.legacyStorage.addDocumentOperations(documentId, result.operations, result.document);
|
|
773
851
|
}
|
|
774
852
|
}
|
|
775
853
|
else {
|
|
776
|
-
await this.legacyStorage.addDocumentOperationsWithTransaction(
|
|
854
|
+
await this.legacyStorage.addDocumentOperationsWithTransaction(documentId, callback);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
async queueDocument(input, options) {
|
|
858
|
+
const { id, documentType, document } = resolveCreateDocumentInput(input);
|
|
859
|
+
if (!id) {
|
|
860
|
+
throw new Error("Document id is required", { cause: input });
|
|
861
|
+
}
|
|
862
|
+
if (!documentType) {
|
|
863
|
+
throw new Error("Document type is required", { cause: input });
|
|
864
|
+
}
|
|
865
|
+
const exists = await this.documentStorage.exists(id);
|
|
866
|
+
if (exists) {
|
|
867
|
+
throw new DocumentAlreadyExistsError(id);
|
|
868
|
+
}
|
|
869
|
+
// add listeners first
|
|
870
|
+
let jobId;
|
|
871
|
+
const promise = new Promise((resolve, reject) => {
|
|
872
|
+
const unsubscribe = this.queueManager.on("jobCompleted", (job, result) => {
|
|
873
|
+
if (job.jobId === jobId) {
|
|
874
|
+
unsubscribe();
|
|
875
|
+
unsubscribeError();
|
|
876
|
+
resolve(result);
|
|
877
|
+
}
|
|
878
|
+
});
|
|
879
|
+
const unsubscribeError = this.queueManager.on("jobFailed", (job, error) => {
|
|
880
|
+
if (job.jobId === jobId) {
|
|
881
|
+
unsubscribe();
|
|
882
|
+
unsubscribeError();
|
|
883
|
+
reject(error);
|
|
884
|
+
}
|
|
885
|
+
});
|
|
886
|
+
});
|
|
887
|
+
// now queue the job
|
|
888
|
+
try {
|
|
889
|
+
jobId = await this.queueManager.addJob({
|
|
890
|
+
documentId: id,
|
|
891
|
+
documentType,
|
|
892
|
+
initialState: document,
|
|
893
|
+
options,
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
catch (error) {
|
|
897
|
+
this.logger.error("Error adding job", error);
|
|
898
|
+
throw error;
|
|
777
899
|
}
|
|
900
|
+
return promise;
|
|
778
901
|
}
|
|
779
|
-
queueOperation(
|
|
780
|
-
|
|
902
|
+
queueOperation(driveIdOrDocumentId, documentIdOrOperation, operationOrOptions, maybeOptions) {
|
|
903
|
+
let documentId;
|
|
904
|
+
let operation;
|
|
905
|
+
let options;
|
|
906
|
+
if (typeof documentIdOrOperation === "string") {
|
|
907
|
+
// Deprecated overload: (driveId, documentId, operation, options)
|
|
908
|
+
documentId = documentIdOrOperation;
|
|
909
|
+
operation = operationOrOptions;
|
|
910
|
+
options = maybeOptions;
|
|
911
|
+
}
|
|
912
|
+
else {
|
|
913
|
+
// Standard overload: (documentId, operation, options)
|
|
914
|
+
documentId = driveIdOrDocumentId;
|
|
915
|
+
operation = documentIdOrOperation;
|
|
916
|
+
options = operationOrOptions;
|
|
917
|
+
}
|
|
918
|
+
return this._queueOperations(documentId, [operation], options);
|
|
781
919
|
}
|
|
782
|
-
async resultIfExistingOperations(
|
|
920
|
+
async resultIfExistingOperations(id, operations) {
|
|
783
921
|
try {
|
|
784
|
-
const document = await this.getDocument(
|
|
922
|
+
const document = await this.getDocument(id);
|
|
785
923
|
const newOperation = operations.find((op) => !op.id ||
|
|
786
924
|
!document.operations[op.scope].find((existingOp) => existingOp.id === op.id &&
|
|
787
925
|
existingOp.index === op.index &&
|
|
@@ -806,9 +944,27 @@ export class BaseDocumentDriveServer {
|
|
|
806
944
|
return undefined;
|
|
807
945
|
}
|
|
808
946
|
}
|
|
809
|
-
|
|
947
|
+
queueOperations(driveIdOrDocumentId, documentIdOrOperations, operationsOrOptions, maybeOptions) {
|
|
948
|
+
let documentId;
|
|
949
|
+
let operations;
|
|
950
|
+
let options;
|
|
951
|
+
if (typeof documentIdOrOperations === "string") {
|
|
952
|
+
// Deprecated overload: (driveId, documentId, operations, options)
|
|
953
|
+
documentId = documentIdOrOperations;
|
|
954
|
+
operations = operationsOrOptions;
|
|
955
|
+
options = maybeOptions;
|
|
956
|
+
}
|
|
957
|
+
else {
|
|
958
|
+
// Standard overload: (documentId, operations, options)
|
|
959
|
+
documentId = driveIdOrDocumentId;
|
|
960
|
+
operations = documentIdOrOperations;
|
|
961
|
+
options = operationsOrOptions;
|
|
962
|
+
}
|
|
963
|
+
return this._queueOperations(documentId, operations, options);
|
|
964
|
+
}
|
|
965
|
+
async _queueOperations(documentId, operations, options) {
|
|
810
966
|
// if operations are already stored then returns cached document
|
|
811
|
-
const result = await this.resultIfExistingOperations(
|
|
967
|
+
const result = await this.resultIfExistingOperations(documentId, operations);
|
|
812
968
|
if (result) {
|
|
813
969
|
return result;
|
|
814
970
|
}
|
|
@@ -833,8 +989,7 @@ export class BaseDocumentDriveServer {
|
|
|
833
989
|
// now queue the job
|
|
834
990
|
try {
|
|
835
991
|
jobId = await this.queueManager.addJob({
|
|
836
|
-
|
|
837
|
-
documentId: documentId,
|
|
992
|
+
documentId,
|
|
838
993
|
operations,
|
|
839
994
|
options,
|
|
840
995
|
});
|
|
@@ -845,14 +1000,46 @@ export class BaseDocumentDriveServer {
|
|
|
845
1000
|
}
|
|
846
1001
|
return promise;
|
|
847
1002
|
}
|
|
848
|
-
|
|
849
|
-
|
|
1003
|
+
queueAction(driveIdOrDocumentId, documentIdOrAction, actionOrOptions, maybeOptions) {
|
|
1004
|
+
let documentId;
|
|
1005
|
+
let action;
|
|
1006
|
+
let options;
|
|
1007
|
+
if (typeof documentIdOrAction === "string") {
|
|
1008
|
+
// Deprecated overload: (driveId, documentId, action, options)
|
|
1009
|
+
documentId = documentIdOrAction;
|
|
1010
|
+
action = actionOrOptions;
|
|
1011
|
+
options = maybeOptions;
|
|
1012
|
+
}
|
|
1013
|
+
else {
|
|
1014
|
+
// Standard overload: (documentId, action, options)
|
|
1015
|
+
documentId = driveIdOrDocumentId;
|
|
1016
|
+
action = documentIdOrAction;
|
|
1017
|
+
options = actionOrOptions;
|
|
1018
|
+
}
|
|
1019
|
+
return this._queueActions(documentId, [action], options);
|
|
1020
|
+
}
|
|
1021
|
+
queueActions(driveIdOrDocumentId, documentIdOrActions, actionsOrOptions, maybeOptions) {
|
|
1022
|
+
let documentId;
|
|
1023
|
+
let actions;
|
|
1024
|
+
let options;
|
|
1025
|
+
if (typeof documentIdOrActions === "string") {
|
|
1026
|
+
// Deprecated overload: (driveId, documentId, actions, options)
|
|
1027
|
+
documentId = documentIdOrActions;
|
|
1028
|
+
actions = actionsOrOptions;
|
|
1029
|
+
options = maybeOptions;
|
|
1030
|
+
}
|
|
1031
|
+
else {
|
|
1032
|
+
// Standard overload: (documentId, actions, options)
|
|
1033
|
+
documentId = driveIdOrDocumentId;
|
|
1034
|
+
actions = documentIdOrActions;
|
|
1035
|
+
options = actionsOrOptions;
|
|
1036
|
+
}
|
|
1037
|
+
return this._queueActions(documentId, actions, options);
|
|
850
1038
|
}
|
|
851
|
-
async
|
|
1039
|
+
async _queueActions(documentId, actions, options) {
|
|
852
1040
|
try {
|
|
853
1041
|
const jobId = await this.queueManager.addJob({
|
|
854
|
-
|
|
855
|
-
documentId: documentId,
|
|
1042
|
+
documentId,
|
|
856
1043
|
actions,
|
|
857
1044
|
options,
|
|
858
1045
|
});
|
|
@@ -878,13 +1065,19 @@ export class BaseDocumentDriveServer {
|
|
|
878
1065
|
throw error;
|
|
879
1066
|
}
|
|
880
1067
|
}
|
|
1068
|
+
/**
|
|
1069
|
+
* @deprecated Use the {@link queueAction} method instead.
|
|
1070
|
+
*/
|
|
881
1071
|
async queueDriveAction(driveId, action, options) {
|
|
882
1072
|
return this.queueDriveActions(driveId, [action], options);
|
|
883
1073
|
}
|
|
1074
|
+
/**
|
|
1075
|
+
* @deprecated Use the {@link queueActions} method instead.
|
|
1076
|
+
*/
|
|
884
1077
|
async queueDriveActions(driveId, actions, options) {
|
|
885
1078
|
try {
|
|
886
1079
|
const jobId = await this.queueManager.addJob({
|
|
887
|
-
|
|
1080
|
+
documentId: driveId,
|
|
888
1081
|
actions,
|
|
889
1082
|
options,
|
|
890
1083
|
});
|
|
@@ -910,12 +1103,27 @@ export class BaseDocumentDriveServer {
|
|
|
910
1103
|
throw error;
|
|
911
1104
|
}
|
|
912
1105
|
}
|
|
913
|
-
|
|
914
|
-
|
|
1106
|
+
addOperations(driveIdOrDocumentId, documentIdOrOperations, operationsOrOptions, maybeOptions) {
|
|
1107
|
+
let documentId;
|
|
1108
|
+
let operations;
|
|
1109
|
+
let options;
|
|
1110
|
+
if (typeof documentIdOrOperations === "string") {
|
|
1111
|
+
// Deprecated overload: (driveId, documentId, operations, options)
|
|
1112
|
+
documentId = documentIdOrOperations;
|
|
1113
|
+
operations = operationsOrOptions;
|
|
1114
|
+
options = maybeOptions;
|
|
1115
|
+
}
|
|
1116
|
+
else {
|
|
1117
|
+
// Standard overload: (documentId, operations, options)
|
|
1118
|
+
documentId = driveIdOrDocumentId;
|
|
1119
|
+
operations = documentIdOrOperations;
|
|
1120
|
+
options = operationsOrOptions;
|
|
1121
|
+
}
|
|
1122
|
+
return this._queueOperations(documentId, operations, options);
|
|
915
1123
|
}
|
|
916
|
-
async processOperations(
|
|
1124
|
+
async processOperations(documentId, operations, options) {
|
|
917
1125
|
// if operations are already stored then returns the result
|
|
918
|
-
const result = await this.resultIfExistingOperations(
|
|
1126
|
+
const result = await this.resultIfExistingOperations(documentId, operations);
|
|
919
1127
|
if (result) {
|
|
920
1128
|
return result;
|
|
921
1129
|
}
|
|
@@ -924,8 +1132,8 @@ export class BaseDocumentDriveServer {
|
|
|
924
1132
|
const signals = [];
|
|
925
1133
|
let error;
|
|
926
1134
|
try {
|
|
927
|
-
await this._addOperations(
|
|
928
|
-
const result = await this._processOperations(
|
|
1135
|
+
await this._addOperations(documentId, async (documentStorage) => {
|
|
1136
|
+
const result = await this._processOperations(documentId, documentStorage, operations);
|
|
929
1137
|
if (!result.document) {
|
|
930
1138
|
this.logger.error("Invalid document");
|
|
931
1139
|
throw result.error ?? new Error("Invalid document");
|
|
@@ -939,17 +1147,28 @@ export class BaseDocumentDriveServer {
|
|
|
939
1147
|
document: result.document,
|
|
940
1148
|
};
|
|
941
1149
|
});
|
|
1150
|
+
const syncUnits = new Array();
|
|
942
1151
|
if (document) {
|
|
943
1152
|
this.cache.setDocument(documentId, document).catch(this.logger.error);
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
1153
|
+
// creates array of unique sync units from the applied operations
|
|
1154
|
+
for (const operation of operationsApplied) {
|
|
1155
|
+
const syncUnit = {
|
|
1156
|
+
documentId,
|
|
1157
|
+
documentType: document.header.documentType,
|
|
1158
|
+
scope: operation.scope,
|
|
1159
|
+
branch: "main", // TODO: handle branches
|
|
1160
|
+
revision: operation.index + 1,
|
|
1161
|
+
lastUpdated: operation.timestamp,
|
|
1162
|
+
};
|
|
1163
|
+
// checks if this sync unit was already added
|
|
1164
|
+
const exists = syncUnits.some((unit) => unit.documentId === syncUnit.documentId &&
|
|
1165
|
+
unit.scope === syncUnit.scope &&
|
|
1166
|
+
unit.branch === syncUnit.branch);
|
|
1167
|
+
if (!exists) {
|
|
1168
|
+
syncUnits.push(syncUnit);
|
|
1169
|
+
}
|
|
949
1170
|
}
|
|
950
|
-
|
|
951
|
-
}, { scopes: [], branches: ["main"] });
|
|
952
|
-
const syncUnits = await this.getSynchronizationUnits(driveId, [documentId], scopes, branches);
|
|
1171
|
+
}
|
|
953
1172
|
// checks if any of the provided operations where reshufled
|
|
954
1173
|
const newOp = operationsApplied.find((appliedOp) => !operations.find((o) => o.id === appliedOp.id &&
|
|
955
1174
|
o.index === appliedOp.index &&
|
|
@@ -963,47 +1182,51 @@ export class BaseDocumentDriveServer {
|
|
|
963
1182
|
: (options?.source ?? { type: "local" });
|
|
964
1183
|
// update listener cache
|
|
965
1184
|
const operationSource = this.getOperationSource(source);
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
this.synchronizationManager.updateSyncStatus(syncUnit.syncId, {
|
|
1185
|
+
// TODO Decouple the operation processing from syncing it to the listeners?
|
|
1186
|
+
// Listener manager should be the one keeping the sync status since it depends on the listeners
|
|
1187
|
+
if (syncUnits.length) {
|
|
1188
|
+
this.listenerManager
|
|
1189
|
+
.updateSynchronizationRevisions(syncUnits, source, () => {
|
|
1190
|
+
this.synchronizationManager.updateSyncStatus(documentId, {
|
|
973
1191
|
[operationSource]: "SYNCING",
|
|
974
1192
|
});
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
}
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
this.synchronizationManager.updateSyncStatus(
|
|
1193
|
+
for (const syncUnit of syncUnits) {
|
|
1194
|
+
this.synchronizationManager.updateSyncStatus(syncUnit, {
|
|
1195
|
+
[operationSource]: "SYNCING",
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1198
|
+
}, this.handleListenerError.bind(this), options?.forceSync ?? source.type === "local")
|
|
1199
|
+
.then((updates) => {
|
|
1200
|
+
if (updates.length) {
|
|
1201
|
+
this.synchronizationManager.updateSyncStatus(documentId, {
|
|
1202
|
+
[operationSource]: "SUCCESS",
|
|
1203
|
+
});
|
|
1204
|
+
}
|
|
1205
|
+
for (const syncUnit of syncUnits) {
|
|
1206
|
+
this.synchronizationManager.updateSyncStatus(syncUnit, {
|
|
1207
|
+
[operationSource]: "SUCCESS",
|
|
1208
|
+
});
|
|
1209
|
+
}
|
|
1210
|
+
})
|
|
1211
|
+
.catch((error) => {
|
|
1212
|
+
this.logger.error("Non handled error updating sync revision", error);
|
|
1213
|
+
this.synchronizationManager.updateSyncStatus(documentId, {
|
|
996
1214
|
[operationSource]: "ERROR",
|
|
997
1215
|
}, error);
|
|
998
|
-
|
|
999
|
-
|
|
1216
|
+
for (const syncUnit of syncUnits) {
|
|
1217
|
+
this.synchronizationManager.updateSyncStatus(syncUnit, {
|
|
1218
|
+
[operationSource]: "ERROR",
|
|
1219
|
+
}, error);
|
|
1220
|
+
}
|
|
1221
|
+
});
|
|
1222
|
+
}
|
|
1000
1223
|
// after applying all the valid operations,throws
|
|
1001
1224
|
// an error if there was an invalid operation
|
|
1002
1225
|
if (error) {
|
|
1003
1226
|
throw error;
|
|
1004
1227
|
}
|
|
1005
|
-
this.eventEmitter.emit("documentOperationsAdded",
|
|
1006
|
-
this.eventEmitter.emit("operationsAdded",
|
|
1228
|
+
this.eventEmitter.emit("documentOperationsAdded", documentId, operations);
|
|
1229
|
+
this.eventEmitter.emit("operationsAdded", documentId, operations);
|
|
1007
1230
|
return {
|
|
1008
1231
|
status: "SUCCESS",
|
|
1009
1232
|
document,
|
|
@@ -1024,6 +1247,9 @@ export class BaseDocumentDriveServer {
|
|
|
1024
1247
|
};
|
|
1025
1248
|
}
|
|
1026
1249
|
}
|
|
1250
|
+
/**
|
|
1251
|
+
* @deprecated Use the {@link addOperation} method instead.
|
|
1252
|
+
*/
|
|
1027
1253
|
addDriveOperation(driveId, operation, options) {
|
|
1028
1254
|
return this.addDriveOperations(driveId, [operation], options);
|
|
1029
1255
|
}
|
|
@@ -1041,6 +1267,9 @@ export class BaseDocumentDriveServer {
|
|
|
1041
1267
|
return this.legacyStorage.addDriveOperationsWithTransaction(driveId, callback);
|
|
1042
1268
|
}
|
|
1043
1269
|
}
|
|
1270
|
+
/**
|
|
1271
|
+
* @deprecated Use the {@link queueOperation} method instead.
|
|
1272
|
+
*/
|
|
1044
1273
|
queueDriveOperation(driveId, operation, options) {
|
|
1045
1274
|
return this.queueDriveOperations(driveId, [operation], options);
|
|
1046
1275
|
}
|
|
@@ -1069,6 +1298,9 @@ export class BaseDocumentDriveServer {
|
|
|
1069
1298
|
return undefined;
|
|
1070
1299
|
}
|
|
1071
1300
|
}
|
|
1301
|
+
/**
|
|
1302
|
+
* @deprecated Use the {@link queueOperations} method instead.
|
|
1303
|
+
*/
|
|
1072
1304
|
async queueDriveOperations(driveId, operations, options) {
|
|
1073
1305
|
// if operations are already stored then returns cached document
|
|
1074
1306
|
const result = await this.resultIfExistingDriveOperations(driveId, operations);
|
|
@@ -1077,7 +1309,7 @@ export class BaseDocumentDriveServer {
|
|
|
1077
1309
|
}
|
|
1078
1310
|
try {
|
|
1079
1311
|
const jobId = await this.queueManager.addJob({
|
|
1080
|
-
|
|
1312
|
+
documentId: driveId,
|
|
1081
1313
|
operations,
|
|
1082
1314
|
options,
|
|
1083
1315
|
});
|
|
@@ -1103,6 +1335,9 @@ export class BaseDocumentDriveServer {
|
|
|
1103
1335
|
throw error;
|
|
1104
1336
|
}
|
|
1105
1337
|
}
|
|
1338
|
+
/**
|
|
1339
|
+
* @deprecated Use the {@link addOperations} method instead.
|
|
1340
|
+
*/
|
|
1106
1341
|
async addDriveOperations(driveId, operations, options) {
|
|
1107
1342
|
return this.queueDriveOperations(driveId, operations, options);
|
|
1108
1343
|
}
|
|
@@ -1118,7 +1353,7 @@ export class BaseDocumentDriveServer {
|
|
|
1118
1353
|
}
|
|
1119
1354
|
try {
|
|
1120
1355
|
await this._addDriveOperations(driveId, async (documentStorage) => {
|
|
1121
|
-
const result = await this._processOperations(driveId,
|
|
1356
|
+
const result = await this._processOperations(driveId, documentStorage, operations.slice());
|
|
1122
1357
|
document = result.document;
|
|
1123
1358
|
operationsApplied.push(...result.operationsApplied);
|
|
1124
1359
|
signals.push(...result.signals);
|
|
@@ -1131,6 +1366,7 @@ export class BaseDocumentDriveServer {
|
|
|
1131
1366
|
if (!document || !isDocumentDrive(document)) {
|
|
1132
1367
|
throw error ?? new Error("Invalid Document Drive document");
|
|
1133
1368
|
}
|
|
1369
|
+
this.cache.setDocument(driveId, document).catch(this.logger.error);
|
|
1134
1370
|
this.cache.setDrive(driveId, document).catch(this.logger.error);
|
|
1135
1371
|
// update listener cache
|
|
1136
1372
|
const lastOperation = operationsApplied
|
|
@@ -1151,13 +1387,12 @@ export class BaseDocumentDriveServer {
|
|
|
1151
1387
|
: (options?.source ?? { type: "local" });
|
|
1152
1388
|
const operationSource = this.getOperationSource(source);
|
|
1153
1389
|
this.listenerManager
|
|
1154
|
-
.updateSynchronizationRevisions(
|
|
1390
|
+
.updateSynchronizationRevisions([
|
|
1155
1391
|
{
|
|
1156
|
-
|
|
1157
|
-
|
|
1392
|
+
documentId: driveId,
|
|
1393
|
+
documentType: document.header.documentType,
|
|
1158
1394
|
scope: "global",
|
|
1159
1395
|
branch: "main",
|
|
1160
|
-
documentType: "powerhouse/document-drive",
|
|
1161
1396
|
lastUpdated: lastOperation.timestamp,
|
|
1162
1397
|
revision: lastOperation.index,
|
|
1163
1398
|
},
|
|
@@ -1181,10 +1416,10 @@ export class BaseDocumentDriveServer {
|
|
|
1181
1416
|
});
|
|
1182
1417
|
}
|
|
1183
1418
|
if (this.shouldSyncRemoteDrive(document)) {
|
|
1184
|
-
this.startSyncRemoteDrive(driveId);
|
|
1419
|
+
this.startSyncRemoteDrive(driveId).catch(this.logger.error);
|
|
1185
1420
|
}
|
|
1186
1421
|
else {
|
|
1187
|
-
this.stopSyncRemoteDrive(driveId);
|
|
1422
|
+
this.stopSyncRemoteDrive(driveId).catch(this.logger.error);
|
|
1188
1423
|
}
|
|
1189
1424
|
// after applying all the valid operations,throws
|
|
1190
1425
|
// an error if there was an invalid operation
|
|
@@ -1192,7 +1427,7 @@ export class BaseDocumentDriveServer {
|
|
|
1192
1427
|
throw error;
|
|
1193
1428
|
}
|
|
1194
1429
|
this.eventEmitter.emit("driveOperationsAdded", driveId, operations);
|
|
1195
|
-
this.eventEmitter.emit("operationsAdded", driveId,
|
|
1430
|
+
this.eventEmitter.emit("operationsAdded", driveId, operations);
|
|
1196
1431
|
return {
|
|
1197
1432
|
status: "SUCCESS",
|
|
1198
1433
|
document,
|
|
@@ -1226,20 +1461,81 @@ export class BaseDocumentDriveServer {
|
|
|
1226
1461
|
}
|
|
1227
1462
|
return operations;
|
|
1228
1463
|
}
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1464
|
+
addAction(driveIdOrDocumentId, documentIdOrAction, actionOrOptions, maybeOptions) {
|
|
1465
|
+
let documentId;
|
|
1466
|
+
let action;
|
|
1467
|
+
let options;
|
|
1468
|
+
if (typeof documentIdOrAction === "string") {
|
|
1469
|
+
// Deprecated overload: (driveId, documentId, action, options)
|
|
1470
|
+
documentId = documentIdOrAction;
|
|
1471
|
+
action = actionOrOptions;
|
|
1472
|
+
options = maybeOptions;
|
|
1473
|
+
}
|
|
1474
|
+
else {
|
|
1475
|
+
// Standard overload: (documentId, action, options)
|
|
1476
|
+
documentId = driveIdOrDocumentId;
|
|
1477
|
+
action = documentIdOrAction;
|
|
1478
|
+
options = actionOrOptions;
|
|
1479
|
+
}
|
|
1480
|
+
return this._addAction(documentId, action, options);
|
|
1481
|
+
}
|
|
1482
|
+
async _addAction(documentId, action, options) {
|
|
1483
|
+
return this.addActions(documentId, [action], options);
|
|
1484
|
+
}
|
|
1485
|
+
addActions(driveIdOrDocumentId, documentIdOrActions, actionsOrOptions, maybeOptions) {
|
|
1486
|
+
let documentId;
|
|
1487
|
+
let actions;
|
|
1488
|
+
let options;
|
|
1489
|
+
if (typeof documentIdOrActions === "string") {
|
|
1490
|
+
// Deprecated overload: (driveId, documentId, actions, options)
|
|
1491
|
+
documentId = documentIdOrActions;
|
|
1492
|
+
actions = actionsOrOptions;
|
|
1493
|
+
options = maybeOptions;
|
|
1494
|
+
}
|
|
1495
|
+
else {
|
|
1496
|
+
// Standard overload: (documentId, actions, options)
|
|
1497
|
+
documentId = driveIdOrDocumentId;
|
|
1498
|
+
actions = documentIdOrActions;
|
|
1499
|
+
options = actionsOrOptions;
|
|
1500
|
+
}
|
|
1501
|
+
return this._queueActions(documentId, actions, options);
|
|
1234
1502
|
}
|
|
1235
|
-
async processActions(
|
|
1236
|
-
const document = await this.getDocument(
|
|
1503
|
+
async processActions(documentId, actions, options) {
|
|
1504
|
+
const document = await this.getDocument(documentId);
|
|
1237
1505
|
const operations = this._buildOperations(document, actions);
|
|
1238
|
-
return this.processOperations(
|
|
1506
|
+
return this.processOperations(documentId, operations, options);
|
|
1239
1507
|
}
|
|
1508
|
+
/**
|
|
1509
|
+
* @deprecated Use the {@link addAction} method instead.
|
|
1510
|
+
*/
|
|
1240
1511
|
async addDriveAction(driveId, action, options) {
|
|
1512
|
+
if ("synchronizationUnits" in action.input) {
|
|
1513
|
+
return this._legacyAddFileAction(driveId, action, options);
|
|
1514
|
+
}
|
|
1241
1515
|
return this.addDriveActions(driveId, [action], options);
|
|
1242
1516
|
}
|
|
1517
|
+
async _legacyAddFileAction(driveId, action, options) {
|
|
1518
|
+
// create document before adding it to the drive
|
|
1519
|
+
const document = this.getDocumentModelModule(action.input.documentType).utils.createDocument({ ...action.input.document });
|
|
1520
|
+
document.header.id = action.input.id;
|
|
1521
|
+
document.header.name = action.input.name;
|
|
1522
|
+
document.header.documentType = action.input.documentType;
|
|
1523
|
+
await this.queueDocument({ document }, { source: options?.source || { type: "local" } });
|
|
1524
|
+
// create updated version of the ADD_FILE action
|
|
1525
|
+
const newAction = {
|
|
1526
|
+
...action,
|
|
1527
|
+
input: {
|
|
1528
|
+
id: action.input.id,
|
|
1529
|
+
documentType: document.header.documentType,
|
|
1530
|
+
name: action.input.name,
|
|
1531
|
+
parentFolder: action.input.parentFolder,
|
|
1532
|
+
},
|
|
1533
|
+
};
|
|
1534
|
+
return (await this.addAction(driveId, newAction, options));
|
|
1535
|
+
}
|
|
1536
|
+
/**
|
|
1537
|
+
* @deprecated Use the {@link addActions} method instead.
|
|
1538
|
+
*/
|
|
1243
1539
|
async addDriveActions(driveId, actions, options) {
|
|
1244
1540
|
return this.queueDriveActions(driveId, actions, options);
|
|
1245
1541
|
}
|
|
@@ -1260,8 +1556,8 @@ export class BaseDocumentDriveServer {
|
|
|
1260
1556
|
}
|
|
1261
1557
|
await this.addDriveAction(driveId, setSharingType({ type: "LOCAL" }));
|
|
1262
1558
|
}
|
|
1263
|
-
getSyncStatus(
|
|
1264
|
-
return this.synchronizationManager.getSyncStatus(
|
|
1559
|
+
getSyncStatus(documentId, scope, branch) {
|
|
1560
|
+
return this.synchronizationManager.getSyncStatus(documentId, scope, branch);
|
|
1265
1561
|
}
|
|
1266
1562
|
on(event, cb) {
|
|
1267
1563
|
return this.eventEmitter.on(event, cb);
|
|
@@ -1269,9 +1565,6 @@ export class BaseDocumentDriveServer {
|
|
|
1269
1565
|
emit(event, ...args) {
|
|
1270
1566
|
return this.eventEmitter.emit(event, ...args);
|
|
1271
1567
|
}
|
|
1272
|
-
getSynchronizationUnit(driveId, syncId) {
|
|
1273
|
-
return this.synchronizationManager.getSynchronizationUnit(driveId, syncId);
|
|
1274
|
-
}
|
|
1275
1568
|
// Add delegated methods to properly implement ISynchronizationManager
|
|
1276
1569
|
updateSyncStatus(syncUnitId, status, error) {
|
|
1277
1570
|
this.synchronizationManager.updateSyncStatus(syncUnitId, status, error);
|
|
@@ -1284,15 +1577,24 @@ export class BaseDocumentDriveServer {
|
|
|
1284
1577
|
}
|
|
1285
1578
|
// Add back the saveStrand method that was accidentally removed
|
|
1286
1579
|
async saveStrand(strand, source) {
|
|
1580
|
+
const isNewDocument = !(await this.documentStorage.exists(strand.documentId));
|
|
1581
|
+
let result = undefined;
|
|
1582
|
+
if (isNewDocument) {
|
|
1583
|
+
result = await this.queueDocument({
|
|
1584
|
+
id: strand.documentId,
|
|
1585
|
+
documentType: strand.documentType,
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1287
1588
|
const operations = strand.operations.map((op) => ({
|
|
1288
1589
|
...op,
|
|
1289
1590
|
scope: strand.scope,
|
|
1290
1591
|
branch: strand.branch,
|
|
1291
1592
|
}));
|
|
1292
|
-
|
|
1293
|
-
|
|
1593
|
+
// if document already existed or queueDocument
|
|
1594
|
+
// was successful, queues the operations
|
|
1595
|
+
if ((!isNewDocument || result?.status === "SUCCESS") && operations.length) {
|
|
1294
1596
|
try {
|
|
1295
|
-
result = await this.queueOperations(strand.
|
|
1597
|
+
result = await this.queueOperations(strand.documentId, operations, {
|
|
1296
1598
|
source,
|
|
1297
1599
|
});
|
|
1298
1600
|
}
|
|
@@ -1301,25 +1603,22 @@ export class BaseDocumentDriveServer {
|
|
|
1301
1603
|
throw error;
|
|
1302
1604
|
}
|
|
1303
1605
|
}
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
throw error;
|
|
1313
|
-
}
|
|
1606
|
+
if (!result) {
|
|
1607
|
+
this.logger.debug(`Document ${strand.documentId} already exists`);
|
|
1608
|
+
return {
|
|
1609
|
+
status: "SUCCESS",
|
|
1610
|
+
document: await this.getDocument(strand.documentId),
|
|
1611
|
+
operations: [],
|
|
1612
|
+
signals: [],
|
|
1613
|
+
};
|
|
1314
1614
|
}
|
|
1315
1615
|
if (result.status === "ERROR") {
|
|
1316
|
-
const syncUnits = strand.documentId !== ""
|
|
1317
|
-
? (await this.getSynchronizationUnitsIds(strand.driveId, [strand.documentId], [strand.scope], [strand.branch])).map((s) => s.syncId)
|
|
1318
|
-
: [strand.driveId];
|
|
1319
1616
|
const operationSource = this.getOperationSource(source);
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1617
|
+
this.synchronizationManager.updateSyncStatus({
|
|
1618
|
+
documentId: strand.documentId || strand.driveId,
|
|
1619
|
+
scope: strand.scope,
|
|
1620
|
+
branch: strand.branch,
|
|
1621
|
+
}, { [operationSource]: result.status }, result.error);
|
|
1323
1622
|
}
|
|
1324
1623
|
this.eventEmitter.emit("strandUpdate", strand);
|
|
1325
1624
|
return result;
|