document-drive 1.0.0-alpha.70 → 1.0.0-alpha.71

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "document-drive",
3
- "version": "1.0.0-alpha.70",
3
+ "version": "1.0.0-alpha.71",
4
4
  "license": "AGPL-3.0-only",
5
5
  "type": "module",
6
6
  "module": "./src/index.ts",
package/src/queue/base.ts CHANGED
@@ -1,8 +1,7 @@
1
- import { IJob, IJobQueue, IQueue, IQueueManager, IServerDelegate, JobId, OperationJob, QueueEvents } from "./types";
1
+ import { IJob, IJobQueue, IQueue, IQueueManager, IServerDelegate, JobId, Job, QueueEvents, isOperationJob } from "./types";
2
2
  import { generateUUID } from "../utils";
3
- import { IOperationResult } from "../server";
4
3
  import { createNanoEvents, Unsubscribe } from 'nanoevents';
5
- import { Operation } from "document-model/document";
4
+ import { Action } from "document-model/document";
6
5
  import { AddFileInput, DeleteNodeInput } from "document-model-libs/document-drive";
7
6
 
8
7
  export class MemoryQueue<T, R> implements IQueue<T, R> {
@@ -10,7 +9,7 @@ export class MemoryQueue<T, R> implements IQueue<T, R> {
10
9
  private blocked = false;
11
10
  private deleted = false;
12
11
  private items: IJob<T>[] = [];
13
- private dependencies = new Array<IJob<OperationJob>>();
12
+ private dependencies = new Array<IJob<Job>>();
14
13
 
15
14
  constructor(id: string) {
16
15
  this.id = id;
@@ -54,7 +53,7 @@ export class MemoryQueue<T, R> implements IQueue<T, R> {
54
53
  return this.items;
55
54
  }
56
55
 
57
- async addDependencies(job: IJob<OperationJob>) {
56
+ async addDependencies(job: IJob<Job>) {
58
57
  if (!this.dependencies.find(j => j.jobId === job.jobId)) {
59
58
  this.dependencies.push(job);
60
59
  }
@@ -63,7 +62,7 @@ export class MemoryQueue<T, R> implements IQueue<T, R> {
63
62
  }
64
63
  }
65
64
 
66
- async removeDependencies(job: IJob<OperationJob>) {
65
+ async removeDependencies(job: IJob<Job>) {
67
66
  this.dependencies = this.dependencies.filter((j) => j.jobId !== job.jobId && j.driveId !== job.driveId);
68
67
  if (this.dependencies.length === 0) {
69
68
  await this.setBlocked(false);
@@ -92,7 +91,7 @@ export class BaseQueueManager implements IQueueManager {
92
91
  return Promise.resolve()
93
92
  }
94
93
 
95
- async addJob(job: OperationJob): Promise<JobId> {
94
+ async addJob(job: Job): Promise<JobId> {
96
95
  if (!this.delegate) {
97
96
  throw new Error("No server delegate defined");
98
97
  }
@@ -115,7 +114,8 @@ export class BaseQueueManager implements IQueueManager {
115
114
  const driveQueue = this.getQueue(job.driveId);
116
115
  const jobs = await driveQueue.getJobs();
117
116
  for (const driveJob of jobs) {
118
- const op = driveJob.operations.find((j: Operation) => {
117
+ const actions = isOperationJob(driveJob) ? driveJob.operations : driveJob.actions;
118
+ const op = actions.find((j: Action) => {
119
119
  const input = j.input as AddFileInput;
120
120
  return j.type === "ADD_FILE" && input.id === job.documentId
121
121
  })
@@ -127,7 +127,8 @@ export class BaseQueueManager implements IQueueManager {
127
127
 
128
128
  // if it has ADD_FILE operations then adds the job as
129
129
  // a dependency to the corresponding document queues
130
- const addFileOps = job.operations.filter((j: Operation) => j.type === "ADD_FILE");
130
+ const actions = isOperationJob(job) ? job.operations : job.actions;
131
+ const addFileOps = actions.filter((j: Action) => j.type === "ADD_FILE");
131
132
  for (const addFileOp of addFileOps) {
132
133
  const input = addFileOp.input as AddFileInput;
133
134
  const q = this.getQueue(job.driveId, input.id)
@@ -135,7 +136,7 @@ export class BaseQueueManager implements IQueueManager {
135
136
  }
136
137
 
137
138
  // remove document if operations contains delete_node
138
- const removeFileOps = job.operations.filter((j: Operation) => j.type === "DELETE_NODE");
139
+ const removeFileOps = actions.filter((j: Action) => j.type === "DELETE_NODE");
139
140
  for (const removeFileOp of removeFileOps) {
140
141
  const input = removeFileOp.input as DeleteNodeInput;
141
142
  const queue = this.getQueue(job.driveId, input.id);
@@ -221,17 +222,17 @@ export class BaseQueueManager implements IQueueManager {
221
222
  }
222
223
 
223
224
  try {
224
- const result = await this.delegate.processOperationJob(nextJob);
225
+ const result = await this.delegate.processJob(nextJob);
225
226
 
226
227
  // unblock the document queues of each add_file operation
227
- const addFileOperations = nextJob.operations.filter((op) => op.type === "ADD_FILE");
228
- if (addFileOperations.length > 0) {
229
- addFileOperations.map(async (addFileOp) => {
230
- const documentQueue = this.getQueue(nextJob.driveId, (addFileOp.input as AddFileInput).id);
228
+ const actions = isOperationJob(nextJob) ? nextJob.operations : nextJob.actions;
229
+ const addFileActions = actions.filter((op) => op.type === "ADD_FILE");
230
+ if (addFileActions.length > 0) {
231
+ for (const addFile of addFileActions) {
232
+ const documentQueue = this.getQueue(nextJob.driveId, (addFile.input as AddFileInput).id);
231
233
  await documentQueue.removeDependencies(nextJob);
232
- });
234
+ };
233
235
  }
234
-
235
236
  this.emit("jobCompleted", nextJob, result);
236
237
  } catch (e) {
237
238
  this.emit("jobFailed", nextJob, e as Error);
@@ -20,9 +20,9 @@ export class RedisQueue<T, R> implements IQueue<T, R> {
20
20
  async getNextJob() {
21
21
  const job = await this.client.rPop(this.id + "-jobs");
22
22
  if (!job) {
23
- return null;
23
+ return undefined;
24
24
  }
25
- return JSON.parse(job);
25
+ return JSON.parse(job) as IJob<T>;
26
26
  }
27
27
 
28
28
  async amountOfJobs() {
@@ -52,7 +52,7 @@ export class RedisQueue<T, R> implements IQueue<T, R> {
52
52
 
53
53
  async getJobs() {
54
54
  const entries = await this.client.lRange(this.id + "-jobs", 0, -1)
55
- return entries.map(e => JSON.parse(e));
55
+ return entries.map(e => JSON.parse(e) as IJob<T>);
56
56
  }
57
57
 
58
58
  async addDependencies(job: IJob<OperationJob>) {
@@ -1,31 +1,42 @@
1
- import { Operation } from "document-model/document";
1
+ import { Action, Operation } from "document-model/document";
2
2
  import { IOperationResult } from "../server";
3
3
  import type { Unsubscribe } from "nanoevents";
4
4
 
5
- export type OperationJob = {
5
+ export interface BaseJob {
6
6
  driveId: string;
7
7
  documentId?: string
8
- operations: Operation[]
8
+ actions?: Action[]
9
9
  forceSync?: boolean
10
10
  }
11
11
 
12
+ export interface OperationJob extends BaseJob {
13
+ operations: Operation[]
14
+ }
15
+
16
+ export interface ActionJob extends BaseJob {
17
+ actions: Action[]
18
+ }
19
+
20
+ export type Job = OperationJob | ActionJob;
21
+
12
22
  export type JobId = string;
13
23
 
14
24
  export interface QueueEvents {
15
- jobCompleted: (job: IJob<OperationJob>, result: IOperationResult) => void;
16
- jobFailed: (job: IJob<OperationJob>, error: Error) => void;
25
+ jobCompleted: (job: IJob<Job>, result: IOperationResult) => void;
26
+ jobFailed: (job: IJob<Job>, error: Error) => void;
27
+ queueRemoved: (queueId: string) => void;
17
28
  }
18
29
 
19
30
  export interface IServerDelegate {
20
31
  checkDocumentExists: (driveId: string, documentId: string) => Promise<boolean>;
21
- processOperationJob: (job: OperationJob) => Promise<IOperationResult>;
22
- }
32
+ processJob: (job: Job) => Promise<IOperationResult>;
33
+ };
23
34
 
24
35
  export interface IQueueManager {
25
- addJob(job: OperationJob): Promise<JobId>;
26
- getQueue(driveId: string, document?: string): IQueue<OperationJob, IOperationResult>;
36
+ addJob(job: Job): Promise<JobId>;
37
+ getQueue(driveId: string, document?: string): IQueue<Job, IOperationResult>;
27
38
  removeQueue(driveId: string, documentId?: string): void;
28
- getQueueByIndex(index: number): IQueue<OperationJob, IOperationResult> | null;
39
+ getQueueByIndex(index: number): IQueue<Job, IOperationResult> | null;
29
40
  getQueues(): string[];
30
41
  init(delegate: IServerDelegate, onError: (error: Error) => void): Promise<void>;
31
42
  on<K extends keyof QueueEvents>(
@@ -47,8 +58,16 @@ export interface IQueue<T, R> {
47
58
  isDeleted(): Promise<boolean>;
48
59
  setDeleted(deleted: boolean): Promise<void>;
49
60
  getJobs(): Promise<IJob<T>[]>;
50
- addDependencies(job: IJob<OperationJob>): Promise<void>;
51
- removeDependencies(job: IJob<OperationJob>): Promise<void>;
61
+ addDependencies(job: IJob<Job>): Promise<void>;
62
+ removeDependencies(job: IJob<Job>): Promise<void>;
63
+ }
64
+
65
+ export type IJobQueue = IQueue<Job, IOperationResult>;
66
+
67
+ export function isOperationJob(job: Job): job is OperationJob {
68
+ return "operations" in job;
52
69
  }
53
70
 
54
- export type IJobQueue = IQueue<OperationJob, IOperationResult>;
71
+ export function isActionJob(job: Job): job is ActionJob {
72
+ return "actions" in job;
73
+ }
@@ -70,7 +70,7 @@ import {
70
70
  } from './types';
71
71
  import { filterOperationsByRevision } from './utils';
72
72
  import { BaseQueueManager } from '../queue/base';
73
- import { IQueueManager } from '../queue/types';
73
+ import { ActionJob, IQueueManager, isActionJob, isOperationJob, Job, OperationJob } from '../queue/types';
74
74
 
75
75
  export * from './listener';
76
76
  export type * from './types';
@@ -232,6 +232,25 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
232
232
  return this.triggerMap.delete(driveId);
233
233
  }
234
234
 
235
+ private queueDelegate = {
236
+ checkDocumentExists: (driveId: string, documentId: string): Promise<boolean> => this.storage.checkDocumentExists(driveId, documentId),
237
+ processOperationJob: async ({ driveId, documentId, operations, forceSync }: OperationJob) => {
238
+ return documentId ? this.addOperations(driveId, documentId, operations, forceSync) : this.addDriveOperations(driveId, operations as (Operation<DocumentDriveAction | BaseAction>)[], forceSync)
239
+ },
240
+ processActionJob: async ({ driveId, documentId, actions, forceSync }: ActionJob) => {
241
+ return documentId ? this.addActions(driveId, documentId, actions, forceSync) : this.addDriveActions(driveId, actions as (Operation<DocumentDriveAction | BaseAction>)[], forceSync)
242
+ },
243
+ processJob: async (job: Job) => {
244
+ if (isOperationJob(job)) {
245
+ return this.queueDelegate.processOperationJob(job);
246
+ } else if (isActionJob(job)) {
247
+ return this.queueDelegate.processActionJob(job);
248
+ } else {
249
+ throw new Error("Unknown job type", job);
250
+ }
251
+ }
252
+ };
253
+
235
254
  async initialize() {
236
255
  const errors: Error[] = [];
237
256
  const drives = await this.getDrives();
@@ -242,13 +261,7 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
242
261
  });
243
262
  }
244
263
 
245
- await this.queueManager.init({
246
- checkDocumentExists: (driveId: string, documentId: string): Promise<boolean> => this.storage.checkDocumentExists(driveId, documentId),
247
- processOperationJob: ({ driveId, documentId, operations, forceSync }) => documentId ?
248
- this.addOperations(driveId, documentId, operations, forceSync)
249
- : this.addDriveOperations(driveId, operations as Operation<DocumentDriveAction | BaseAction>[], forceSync)
250
-
251
- }, error => {
264
+ await this.queueManager.init(this.queueDelegate, error => {
252
265
  logger.error(`Error initializing queue manager`, error);
253
266
  errors.push(error);
254
267
  })
@@ -944,6 +957,61 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
944
957
  }
945
958
  }
946
959
 
960
+ async queueAction(drive: string, id: string, action: Action, forceSync?: boolean | undefined): Promise<IOperationResult> {
961
+ return this.queueActions(drive, id, [action], forceSync);
962
+ }
963
+
964
+ async queueActions(drive: string, id: string, actions: Action[], forceSync?: boolean | undefined): Promise<IOperationResult> {
965
+ try {
966
+ const jobId = await this.queueManager.addJob({ driveId: drive, documentId: id, actions, forceSync });
967
+
968
+ return new Promise<IOperationResult>((resolve, reject) => {
969
+ const unsubscribe = this.queueManager.on('jobCompleted', (job, result) => {
970
+ if (job.jobId === jobId) {
971
+ unsubscribe();
972
+ unsubscribeError();
973
+ resolve(result);
974
+ }
975
+ });
976
+ const unsubscribeError = this.queueManager.on('jobFailed', (job, error) => {
977
+ if (job.jobId === jobId) {
978
+ unsubscribe();
979
+ unsubscribeError();
980
+ reject(error);
981
+ }
982
+ });
983
+ })
984
+ } catch (error) {
985
+ logger.error('Error adding job', error);
986
+ throw error;
987
+ }
988
+ }
989
+
990
+ async queueDriveAction(drive: string, action: DocumentDriveAction | BaseAction, forceSync?: boolean | undefined): Promise<IOperationResult<DocumentDriveDocument>> {
991
+ return this.queueDriveActions(drive, [action], forceSync);
992
+ }
993
+
994
+ async queueDriveActions(drive: string, actions: (DocumentDriveAction | BaseAction)[], forceSync?: boolean | undefined): Promise<IOperationResult<DocumentDriveDocument>> {
995
+ const jobId = await this.queueManager.addJob({ driveId: drive, actions, forceSync });
996
+ return new Promise<IOperationResult<DocumentDriveDocument>>((resolve, reject) => {
997
+ const unsubscribe = this.queueManager.on('jobCompleted', (job, result) => {
998
+ if (job.jobId === jobId) {
999
+ unsubscribe();
1000
+ unsubscribeError();
1001
+ resolve(result as IOperationResult<DocumentDriveDocument>);
1002
+ }
1003
+ });
1004
+ const unsubscribeError = this.queueManager.on('jobFailed', (job, error) => {
1005
+ if (job.jobId === jobId) {
1006
+ unsubscribe();
1007
+ unsubscribeError();
1008
+ reject(error);
1009
+ }
1010
+ });
1011
+
1012
+ })
1013
+ }
1014
+
947
1015
  async addOperations(
948
1016
  drive: string,
949
1017
  id: string,
@@ -1277,35 +1345,39 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
1277
1345
  async addAction(
1278
1346
  drive: string,
1279
1347
  id: string,
1280
- action: Action
1348
+ action: Action,
1349
+ forceSync = true
1281
1350
  ): Promise<IOperationResult> {
1282
- return this.addActions(drive, id, [action]);
1351
+ return this.addActions(drive, id, [action], forceSync);
1283
1352
  }
1284
1353
 
1285
1354
  async addActions(
1286
1355
  drive: string,
1287
1356
  id: string,
1288
- actions: Action[]
1357
+ actions: Action[],
1358
+ forceSync = true
1289
1359
  ): Promise<IOperationResult> {
1290
1360
  const document = await this.getDocument(drive, id);
1291
1361
  const operations = this._buildOperations(document, actions);
1292
- return this.queueOperations(drive, id, operations);
1362
+ return this.addOperations(drive, id, operations, forceSync);
1293
1363
  }
1294
1364
 
1295
1365
  async addDriveAction(
1296
1366
  drive: string,
1297
- action: DocumentDriveAction | BaseAction
1367
+ action: DocumentDriveAction | BaseAction,
1368
+ forceSync = true
1298
1369
  ): Promise<IOperationResult<DocumentDriveDocument>> {
1299
- return this.addDriveActions(drive, [action]);
1370
+ return this.addDriveActions(drive, [action], forceSync);
1300
1371
  }
1301
1372
 
1302
1373
  async addDriveActions(
1303
1374
  drive: string,
1304
- actions: (DocumentDriveAction | BaseAction)[]
1375
+ actions: (DocumentDriveAction | BaseAction)[],
1376
+ forceSync = true
1305
1377
  ): Promise<IOperationResult<DocumentDriveDocument>> {
1306
1378
  const document = await this.getDrive(drive);
1307
1379
  const operations = this._buildOperations(document, actions);
1308
- const result = await this.queueDriveOperations(drive, operations);
1380
+ const result = await this.addDriveOperations(drive, operations, forceSync);
1309
1381
  return result;
1310
1382
  }
1311
1383
 
@@ -178,6 +178,7 @@ export abstract class BaseDocumentDriveServer {
178
178
  operation: Operation,
179
179
  forceSync?: boolean
180
180
  ): Promise<IOperationResult>;
181
+
181
182
  abstract addOperations(
182
183
  drive: string,
183
184
  id: string,
@@ -191,6 +192,7 @@ export abstract class BaseDocumentDriveServer {
191
192
  operation: Operation,
192
193
  forceSync?: boolean
193
194
  ): Promise<IOperationResult>;
195
+
194
196
  abstract queueOperations(
195
197
  drive: string,
196
198
  id: string,
@@ -198,6 +200,20 @@ export abstract class BaseDocumentDriveServer {
198
200
  forceSync?: boolean
199
201
  ): Promise<IOperationResult>;
200
202
 
203
+ abstract queueAction(
204
+ drive: string,
205
+ id: string,
206
+ action: Action,
207
+ forceSync?: boolean
208
+ ): Promise<IOperationResult>;
209
+
210
+ abstract queueActions(
211
+ drive: string,
212
+ id: string,
213
+ actions: Action[],
214
+ forceSync?: boolean
215
+ ): Promise<IOperationResult>;
216
+
201
217
  abstract addDriveOperation(
202
218
  drive: string,
203
219
  operation: Operation<DocumentDriveAction | BaseAction>,
@@ -220,24 +236,40 @@ export abstract class BaseDocumentDriveServer {
220
236
  forceSync?: boolean
221
237
  ): Promise<IOperationResult<DocumentDriveDocument>>;
222
238
 
239
+ abstract queueDriveAction(
240
+ drive: string,
241
+ action: DocumentDriveAction | BaseAction,
242
+ forceSync?: boolean
243
+ ): Promise<IOperationResult<DocumentDriveDocument>>;
244
+
245
+ abstract queueDriveActions(
246
+ drive: string,
247
+ actions: Array<DocumentDriveAction | BaseAction>,
248
+ forceSync?: boolean
249
+ ): Promise<IOperationResult<DocumentDriveDocument>>;
250
+
223
251
  abstract addAction(
224
252
  drive: string,
225
253
  id: string,
226
- action: Action
254
+ action: Action,
255
+ forceSync?: boolean
227
256
  ): Promise<IOperationResult>;
228
257
  abstract addActions(
229
258
  drive: string,
230
259
  id: string,
231
- actions: Action[]
260
+ actions: Action[],
261
+ forceSync?: boolean
232
262
  ): Promise<IOperationResult>;
233
263
 
234
264
  abstract addDriveAction(
235
265
  drive: string,
236
- action: DocumentDriveAction | BaseAction
266
+ action: DocumentDriveAction | BaseAction,
267
+ forceSync?: boolean
237
268
  ): Promise<IOperationResult<DocumentDriveDocument>>;
238
269
  abstract addDriveActions(
239
270
  drive: string,
240
- actions: (DocumentDriveAction | BaseAction)[]
271
+ actions: (DocumentDriveAction | BaseAction)[],
272
+ forceSync?: boolean
241
273
  ): Promise<IOperationResult<DocumentDriveDocument>>;
242
274
 
243
275
  abstract getSyncStatus(drive: string): SyncStatus;