document-drive 1.0.0-alpha.81 → 1.0.0-alpha.83

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.81",
3
+ "version": "1.0.0-alpha.83",
4
4
  "license": "AGPL-3.0-only",
5
5
  "type": "module",
6
6
  "module": "./src/index.ts",
@@ -1,14 +1,18 @@
1
1
  import { Document } from "document-model/document";
2
2
  import { ICache } from "./types";
3
3
  import type { RedisClientType } from "redis";
4
- import { logger } from "../utils/logger";
5
4
 
6
5
  class RedisCache implements ICache {
7
6
  private redis: RedisClientType;
7
+ private timeoutInSeconds: number;
8
8
 
9
- constructor(redis: RedisClientType) {
9
+ constructor(redis: RedisClientType, timeoutInSeconds: number | undefined = 5 * 60) {
10
10
  this.redis = redis;
11
- this.redis.flushAll().catch(logger.error);
11
+ this.timeoutInSeconds = timeoutInSeconds;
12
+ }
13
+
14
+ private static _getId(drive: string, id: string) {
15
+ return `cache:${drive}:${id}`;
12
16
  }
13
17
 
14
18
  async setDocument(drive: string, id: string, document: Document) {
@@ -21,17 +25,28 @@ class RedisCache implements ICache {
21
25
  return e;
22
26
  });
23
27
  const doc = { ...document, operations: { global, local } }
24
- return (await this.redis.hSet(drive, id, JSON.stringify(doc))) > 0;
28
+ const redisId = RedisCache._getId(drive, id);
29
+ const result = await this.redis.set(redisId, JSON.stringify(doc), {
30
+ EX: this.timeoutInSeconds ? this.timeoutInSeconds : undefined
31
+ });
32
+
33
+ if (result === 'OK') {
34
+ return true;
35
+ }
36
+
37
+ return false;
25
38
  }
26
39
 
27
40
  async getDocument(drive: string, id: string) {
28
- const doc = await this.redis.hGet(drive, id);
41
+ const redisId = RedisCache._getId(drive, id);
42
+ const doc = await this.redis.get(redisId);
29
43
 
30
44
  return doc ? JSON.parse(doc) as Document : undefined;
31
45
  }
32
46
 
33
47
  async deleteDocument(drive: string, id: string) {
34
- return (await this.redis.hDel(drive, id)) > 0;
48
+ const redisId = RedisCache._getId(drive, id);
49
+ return (await this.redis.del(redisId)) > 0;
35
50
  }
36
51
  }
37
52
 
package/src/queue/base.ts CHANGED
@@ -253,6 +253,6 @@ export class BaseQueueManager implements IQueueManager {
253
253
  }
254
254
 
255
255
  protected getQueueId(driveId: string, documentId?: string) {
256
- return `${driveId}${documentId ? `:${documentId}` : ''}`;
256
+ return `queue:${driveId}${documentId ? `:${documentId}` : ''}`;
257
257
  }
258
258
  }
@@ -22,6 +22,7 @@ import {
22
22
  Operation,
23
23
  OperationScope
24
24
  } from 'document-model/document';
25
+ import { ClientError } from 'graphql-request';
25
26
  import { createNanoEvents, Unsubscribe } from 'nanoevents';
26
27
  import { ICache } from '../cache';
27
28
  import InMemoryCache from '../cache/memory';
@@ -231,6 +232,16 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
231
232
  : 'ERROR',
232
233
  error
233
234
  );
235
+
236
+ if (error instanceof ClientError) {
237
+ this.emit(
238
+ 'clientStrandsError',
239
+ driveId,
240
+ trigger,
241
+ error.response.status,
242
+ error.message
243
+ );
244
+ }
234
245
  },
235
246
  revisions => {
236
247
  const errorRevision = revisions.filter(
@@ -686,6 +697,21 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
686
697
  });
687
698
  }
688
699
 
700
+ public async registerPullResponderTrigger(
701
+ id: string,
702
+ url: string,
703
+ options: Pick<RemoteDriveOptions, 'pullFilter' | 'pullInterval'>
704
+ ) {
705
+ const pullTrigger =
706
+ await PullResponderTransmitter.createPullResponderTrigger(
707
+ id,
708
+ url,
709
+ options
710
+ );
711
+
712
+ return pullTrigger;
713
+ }
714
+
689
715
  async deleteDrive(id: string) {
690
716
  const result = await Promise.allSettled([
691
717
  this.stopSyncRemoteDrive(id),
@@ -4,7 +4,8 @@ import type {
4
4
  DocumentDriveLocalState,
5
5
  DocumentDriveState,
6
6
  ListenerCallInfo,
7
- ListenerFilter
7
+ ListenerFilter,
8
+ Trigger
8
9
  } from 'document-model-libs/document-drive';
9
10
  import type {
10
11
  Action,
@@ -20,7 +21,11 @@ import type {
20
21
  } from 'document-model/document';
21
22
  import { Unsubscribe } from 'nanoevents';
22
23
  import { OperationError } from './error';
23
- import { ITransmitter, StrandUpdateSource } from './listener/transmitter/types';
24
+ import {
25
+ ITransmitter,
26
+ PullResponderTrigger,
27
+ StrandUpdateSource
28
+ } from './listener/transmitter/types';
24
29
 
25
30
  export type DriveInput = State<
26
31
  Omit<DocumentDriveState, '__typename' | 'id' | 'nodes'> & { id?: string },
@@ -138,6 +143,12 @@ export type SyncStatus = 'SYNCING' | UpdateStatus;
138
143
  export interface DriveEvents {
139
144
  syncStatus: (driveId: string, status: SyncStatus, error?: Error) => void;
140
145
  strandUpdate: (update: StrandUpdate) => void;
146
+ clientStrandsError: (
147
+ driveId: string,
148
+ trigger: Trigger,
149
+ status: number,
150
+ errorMessage: string
151
+ ) => void;
141
152
  }
142
153
 
143
154
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -342,6 +353,12 @@ export abstract class BaseDocumentDriveServer {
342
353
  ): Promise<ITransmitter | undefined>;
343
354
 
344
355
  abstract clearStorage(): Promise<void>;
356
+
357
+ abstract registerPullResponderTrigger(
358
+ id: string,
359
+ url: string,
360
+ options: Pick<RemoteDriveOptions, 'pullFilter' | 'pullInterval'>
361
+ ): Promise<PullResponderTrigger>;
345
362
  }
346
363
 
347
364
  export abstract class BaseListenerManager {
@@ -31,14 +31,14 @@ import {
31
31
 
32
32
  type Transaction =
33
33
  | Omit<
34
- PrismaClient<Prisma.PrismaClientOptions, never>,
35
- | '$connect'
36
- | '$disconnect'
37
- | '$on'
38
- | '$transaction'
39
- | '$use'
40
- | '$extends'
41
- >
34
+ PrismaClient<Prisma.PrismaClientOptions, never>,
35
+ | '$connect'
36
+ | '$disconnect'
37
+ | '$on'
38
+ | '$transaction'
39
+ | '$use'
40
+ | '$extends'
41
+ >
42
42
  | ExtendedPrismaClient;
43
43
 
44
44
  function storageToOperation(
@@ -495,21 +495,21 @@ export class PrismaStorage implements IDriveStorage {
495
495
  }
496
496
 
497
497
  async deleteDocument(drive: string, id: string) {
498
- await this.db.document.delete({
499
- where: {
500
- id_driveId: {
498
+ try {
499
+ await this.db.document.deleteMany({
500
+ where: {
501
501
  driveId: drive,
502
502
  id: id
503
503
  }
504
- },
505
- include: {
506
- operations: {
507
- include: {
508
- attachments: true
509
- }
510
- }
504
+ });
505
+ } catch (e: any) {
506
+ // Ignore Error: P2025: An operation failed because it depends on one or more records that were required but not found.
507
+ if ((e.code && e.code === "P2025") || (e.message && e.message.includes("An operation failed because it depends on one or more records that were required but not found."))) {
508
+ return;
511
509
  }
512
- });
510
+
511
+ throw e;
512
+ }
513
513
  }
514
514
 
515
515
  async getDrives() {
@@ -541,13 +541,6 @@ export class PrismaStorage implements IDriveStorage {
541
541
  }
542
542
 
543
543
  async deleteDrive(id: string) {
544
- // delete drive documents and operations
545
- await this.db.document.deleteMany({
546
- where: {
547
- driveId: id
548
- }
549
- });
550
-
551
544
  // delete drive and associated slug
552
545
  await this.db.drive.deleteMany({
553
546
  where: {
@@ -555,7 +548,7 @@ export class PrismaStorage implements IDriveStorage {
555
548
  }
556
549
  });
557
550
 
558
- // delete drive itself
551
+ // delete drive document and its operations
559
552
  await this.deleteDocument('drives', id);
560
553
  }
561
554