document-drive 1.0.0-alpha.60 → 1.0.0-alpha.61

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/storage/prisma.ts +105 -28
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "document-drive",
3
- "version": "1.0.0-alpha.60",
3
+ "version": "1.0.0-alpha.61",
4
4
  "license": "AGPL-3.0-only",
5
5
  "type": "module",
6
6
  "module": "./src/index.ts",
@@ -6,6 +6,10 @@ import {
6
6
  DocumentDriveState
7
7
  } from 'document-model-libs/document-drive';
8
8
  import type {
9
+ Action,
10
+ AttachmentInput,
11
+ DocumentOperations,
12
+ FileRegistry,
9
13
  BaseAction,
10
14
  Document,
11
15
  DocumentHeader,
@@ -32,7 +36,9 @@ type Transaction =
32
36
  | ExtendedPrismaClient;
33
37
 
34
38
  function storageToOperation(
35
- op: Prisma.$OperationPayload['scalars']
39
+ op: Prisma.$OperationPayload['scalars'] & {
40
+ attachments?: AttachmentInput[];
41
+ }
36
42
  ): Operation {
37
43
  const operation: Operation = {
38
44
  skip: op.skip,
@@ -42,9 +48,10 @@ function storageToOperation(
42
48
  input: JSON.parse(op.input),
43
49
  type: op.type,
44
50
  scope: op.scope as OperationScope,
45
- resultingState: op.resultingState ? op.resultingState : undefined,
46
- id: op.opId || undefined
47
- // attachments: fileRegistry
51
+ resultingState: op.resultingState
52
+ ? op.resultingState.toString()
53
+ : undefined,
54
+ attachments: op.attachments
48
55
  };
49
56
  if (op.context) {
50
57
  operation.context = op.context as Prisma.JsonObject;
@@ -176,9 +183,8 @@ export class PrismaStorage implements IDriveStorage {
176
183
  branch: 'main',
177
184
  skip: op.skip,
178
185
  context: op.context,
179
- opId: op.id,
180
186
  resultingState: op.resultingState
181
- ? JSON.stringify(op.resultingState)
187
+ ? Buffer.from(JSON.stringify(op.resultingState))
182
188
  : undefined
183
189
  }))
184
190
  });
@@ -193,6 +199,31 @@ export class PrismaStorage implements IDriveStorage {
193
199
  revision: JSON.stringify(header.revision)
194
200
  }
195
201
  });
202
+
203
+ await Promise.all(
204
+ operations
205
+ .filter(o => o.attachments?.length)
206
+ .map(op => {
207
+ return tx.operation.update({
208
+ where: {
209
+ unique_operation: {
210
+ driveId: drive,
211
+ documentId: id,
212
+ index: op.index,
213
+ scope: op.scope,
214
+ branch: 'main'
215
+ }
216
+ },
217
+ data: {
218
+ attachments: {
219
+ createMany: {
220
+ data: op.attachments ?? []
221
+ }
222
+ }
223
+ }
224
+ });
225
+ })
226
+ );
196
227
  } catch (e) {
197
228
  // P2002: Unique constraint failed
198
229
  // Operation with existing index
@@ -219,6 +250,7 @@ export class PrismaStorage implements IDriveStorage {
219
250
  );
220
251
 
221
252
  if (!existingOperation || !conflictOp) {
253
+ console.error(e);
222
254
  throw e;
223
255
  } else {
224
256
  throw new ConflictOperationError(
@@ -316,22 +348,13 @@ export class PrismaStorage implements IDriveStorage {
316
348
  }
317
349
 
318
350
  async getDocument(driveId: string, id: string, tx?: Transaction) {
319
- const result = await (tx ?? this.db).document.findUnique({
351
+ const prisma = tx ?? this.db;
352
+ const result = await prisma.document.findUnique({
320
353
  where: {
321
354
  id_driveId: {
322
355
  driveId,
323
356
  id
324
357
  }
325
- },
326
- include: {
327
- operations: {
328
- orderBy: {
329
- index: 'asc'
330
- },
331
- include: {
332
- attachments: true
333
- }
334
- }
335
358
  }
336
359
  });
337
360
 
@@ -339,6 +362,69 @@ export class PrismaStorage implements IDriveStorage {
339
362
  throw new Error(`Document with id ${id} not found`);
340
363
  }
341
364
 
365
+ // retrieves operations with resulting state
366
+ // for the last operation of each scope
367
+ const operations = await prisma.$queryRaw<
368
+ Prisma.$OperationPayload['scalars'][]
369
+ >`
370
+ WITH ranked_operations AS (
371
+ SELECT
372
+ *,
373
+ ROW_NUMBER() OVER (PARTITION BY scope ORDER BY index DESC) AS rn
374
+ FROM "Operation"
375
+ )
376
+ SELECT
377
+ "id",
378
+ "opId",
379
+ "scope",
380
+ "branch",
381
+ "index",
382
+ "skip",
383
+ "hash",
384
+ "timestamp",
385
+ "input",
386
+ "type",
387
+ "context",
388
+ CASE
389
+ WHEN rn = 1 THEN "resultingState"
390
+ ELSE NULL
391
+ END AS "resultingState"
392
+ FROM ranked_operations
393
+ ORDER BY scope, index;
394
+ `;
395
+
396
+ const operationIds = operations.map(o => o.id);
397
+ const attachments = await prisma.attachment.findMany({
398
+ where: {
399
+ operationId: {
400
+ in: operationIds
401
+ }
402
+ }
403
+ });
404
+
405
+ const fileRegistry: FileRegistry = {};
406
+ const operationsByScope = operations.reduce<DocumentOperations<Action>>(
407
+ (acc, operation) => {
408
+ const scope = operation.scope as OperationScope;
409
+ if (!acc[scope]) {
410
+ acc[scope] = [];
411
+ }
412
+ const result = storageToOperation(operation);
413
+ result.attachments = attachments.filter(
414
+ a => a.operationId === operation.id
415
+ );
416
+ result.attachments.forEach(({ hash, ...file }) => {
417
+ fileRegistry[hash] = file;
418
+ });
419
+ acc[scope].push(result);
420
+ return acc;
421
+ },
422
+ {
423
+ global: [],
424
+ local: []
425
+ }
426
+ );
427
+
342
428
  const dbDoc = result;
343
429
  const doc: Document = {
344
430
  created: dbDoc.created.toISOString(),
@@ -350,17 +436,8 @@ export class PrismaStorage implements IDriveStorage {
350
436
  >,
351
437
  state: undefined,
352
438
  lastModified: new Date(dbDoc.lastModified).toISOString(),
353
- operations: {
354
- global: dbDoc.operations
355
- .filter(op => op.scope === 'global' && !op.clipboard)
356
- .map(storageToOperation),
357
- local: dbDoc.operations
358
- .filter(op => op.scope === 'local' && !op.clipboard)
359
- .map(storageToOperation)
360
- },
361
- clipboard: dbDoc.operations
362
- .filter(op => op.clipboard)
363
- .map(storageToOperation),
439
+ operations: operationsByScope,
440
+ clipboard: [],
364
441
  revision: JSON.parse(dbDoc.revision) as Record<
365
442
  OperationScope,
366
443
  number