document-drive 1.29.0-dev.0 → 1.29.0-dev.2
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/prisma/schema.prisma +6 -10
- package/dist/src/cache/memory.d.ts +14 -4
- package/dist/src/cache/memory.d.ts.map +1 -1
- package/dist/src/cache/memory.js +57 -19
- package/dist/src/cache/redis.d.ts +14 -4
- package/dist/src/cache/redis.d.ts.map +1 -1
- package/dist/src/cache/redis.js +71 -19
- package/dist/src/cache/types.d.ts +10 -3
- package/dist/src/cache/types.d.ts.map +1 -1
- package/dist/src/cache/util.d.ts +3 -0
- package/dist/src/cache/util.d.ts.map +1 -0
- package/dist/src/cache/util.js +13 -0
- package/dist/src/server/base-server.d.ts +1 -1
- package/dist/src/server/base-server.d.ts.map +1 -1
- package/dist/src/server/base-server.js +13 -28
- package/dist/src/server/listener/listener-manager.js +2 -2
- package/dist/src/server/listener/transmitter/pull-responder.d.ts +1 -1
- package/dist/src/server/listener/transmitter/pull-responder.d.ts.map +1 -1
- package/dist/src/server/listener/transmitter/pull-responder.js +2 -3
- package/dist/src/server/sync-manager.d.ts.map +1 -1
- package/dist/src/server/sync-manager.js +7 -18
- package/dist/src/server/types.d.ts +0 -1
- package/dist/src/server/types.d.ts.map +1 -1
- package/dist/src/storage/browser.d.ts +6 -4
- package/dist/src/storage/browser.d.ts.map +1 -1
- package/dist/src/storage/browser.js +83 -22
- package/dist/src/storage/filesystem.d.ts +5 -3
- package/dist/src/storage/filesystem.d.ts.map +1 -1
- package/dist/src/storage/filesystem.js +83 -29
- package/dist/src/storage/ipfs.d.ts +5 -1
- package/dist/src/storage/ipfs.d.ts.map +1 -1
- package/dist/src/storage/ipfs.js +74 -21
- package/dist/src/storage/memory.d.ts +5 -5
- package/dist/src/storage/memory.d.ts.map +1 -1
- package/dist/src/storage/memory.js +78 -51
- package/dist/src/storage/prisma/factory.d.ts +4 -3
- package/dist/src/storage/prisma/factory.d.ts.map +1 -1
- package/dist/src/storage/prisma/factory.js +5 -5
- package/dist/src/storage/prisma/index.d.ts +11 -6
- package/dist/src/storage/prisma/index.d.ts.map +1 -1
- package/dist/src/storage/prisma/index.js +233 -190
- package/dist/src/storage/types.d.ts +6 -6
- package/dist/src/storage/types.d.ts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library";
|
|
2
2
|
import { backOff } from "exponential-backoff";
|
|
3
3
|
import { ConflictOperationError } from "../../server/error.js";
|
|
4
|
-
import { logger } from "../../utils/logger.js";
|
|
4
|
+
import { childLogger, logger } from "../../utils/logger.js";
|
|
5
5
|
export * from "./factory.js";
|
|
6
6
|
function storageToOperation(op) {
|
|
7
7
|
const operation = {
|
|
@@ -45,10 +45,12 @@ function getRetryTransactionsClient(prisma, backOffOptions) {
|
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
47
|
export class PrismaStorage {
|
|
48
|
+
logger = childLogger(["PrismaStorage"]);
|
|
48
49
|
db;
|
|
49
|
-
|
|
50
|
-
constructor(db, options) {
|
|
50
|
+
cache;
|
|
51
|
+
constructor(db, cache, options) {
|
|
51
52
|
const backOffOptions = options?.transactionRetryBackoff;
|
|
53
|
+
this.cache = cache;
|
|
52
54
|
this.db = getRetryTransactionsClient(db, {
|
|
53
55
|
...backOffOptions,
|
|
54
56
|
jitter: backOffOptions?.jitter ?? "full",
|
|
@@ -82,60 +84,240 @@ export class PrismaStorage {
|
|
|
82
84
|
id: documentId,
|
|
83
85
|
},
|
|
84
86
|
});
|
|
87
|
+
// temporary -- but we need to create drives automatically for documents
|
|
88
|
+
// of the correct type
|
|
89
|
+
if (document.documentType === "powerhouse/document-drive") {
|
|
90
|
+
const drive = document;
|
|
91
|
+
try {
|
|
92
|
+
await this.db.drive.create({
|
|
93
|
+
data: {
|
|
94
|
+
id: documentId,
|
|
95
|
+
slug: drive.initialState.state.global.slug ?? documentId,
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
if (e instanceof PrismaClientKnownRequestError && e.code === "P2002") {
|
|
101
|
+
throw new Error(`Drive with slug ${drive.initialState.state.global.slug ?? documentId} already exists`);
|
|
102
|
+
}
|
|
103
|
+
throw e;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
85
106
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
async createDrive(id, drive) {
|
|
93
|
-
const doc = {
|
|
94
|
-
name: drive.name,
|
|
95
|
-
documentType: drive.documentType,
|
|
96
|
-
isDrive: true,
|
|
97
|
-
initialState: JSON.stringify(drive.initialState),
|
|
98
|
-
lastModified: drive.lastModified,
|
|
99
|
-
revision: JSON.stringify(drive.revision),
|
|
100
|
-
meta: drive.meta ? JSON.stringify(drive.meta) : undefined,
|
|
101
|
-
id,
|
|
107
|
+
async get(documentId, tx) {
|
|
108
|
+
const prisma = tx ?? this.db;
|
|
109
|
+
const query = {
|
|
110
|
+
where: {
|
|
111
|
+
id: documentId,
|
|
112
|
+
},
|
|
102
113
|
};
|
|
103
|
-
await
|
|
114
|
+
const result = await prisma.document.findUnique(query);
|
|
115
|
+
if (result === null) {
|
|
116
|
+
throw new Error(`Document with id ${documentId} not found`);
|
|
117
|
+
}
|
|
118
|
+
let cachedOperations = {
|
|
119
|
+
global: [],
|
|
120
|
+
local: [],
|
|
121
|
+
};
|
|
122
|
+
const cachedDocument = await this.cache.getDocument(documentId);
|
|
123
|
+
if (cachedDocument) {
|
|
124
|
+
cachedOperations = cachedDocument.operations;
|
|
125
|
+
}
|
|
126
|
+
const scopeIndex = Object.keys(cachedOperations).reduceRight((acc, value) => {
|
|
127
|
+
const scope = value;
|
|
128
|
+
const lastIndex = cachedOperations[scope].at(-1)?.index ?? -1;
|
|
129
|
+
acc[scope] = lastIndex;
|
|
130
|
+
return acc;
|
|
131
|
+
}, { global: -1, local: -1 });
|
|
132
|
+
const conditions = Object.entries(scopeIndex).map(([scope, index]) => `("scope" = '${scope}' AND "index" > ${index})`);
|
|
133
|
+
conditions.push(`("scope" NOT IN (${Object.keys(cachedOperations)
|
|
134
|
+
.map((s) => `'${s}'`)
|
|
135
|
+
.join(", ")}))`);
|
|
136
|
+
// retrieves operations with resulting state
|
|
137
|
+
// for the last operation of each scope
|
|
138
|
+
// TODO prevent SQL injection
|
|
139
|
+
const queryOperations = await prisma.$queryRawUnsafe(`WITH ranked_operations AS (
|
|
140
|
+
SELECT
|
|
141
|
+
*,
|
|
142
|
+
ROW_NUMBER() OVER (PARTITION BY scope ORDER BY index DESC) AS rn
|
|
143
|
+
FROM "Operation"
|
|
144
|
+
)
|
|
145
|
+
SELECT
|
|
146
|
+
"id",
|
|
147
|
+
"opId",
|
|
148
|
+
"scope",
|
|
149
|
+
"branch",
|
|
150
|
+
"index",
|
|
151
|
+
"skip",
|
|
152
|
+
"hash",
|
|
153
|
+
"timestamp",
|
|
154
|
+
"input",
|
|
155
|
+
"type",
|
|
156
|
+
"context",
|
|
157
|
+
CASE
|
|
158
|
+
WHEN rn = 1 THEN "resultingState"
|
|
159
|
+
ELSE NULL
|
|
160
|
+
END AS "resultingState"
|
|
161
|
+
FROM ranked_operations
|
|
162
|
+
WHERE "documentId" = $1
|
|
163
|
+
AND (${conditions.join(" OR ")})
|
|
164
|
+
ORDER BY scope, index;
|
|
165
|
+
`, documentId);
|
|
166
|
+
const operationIds = queryOperations.map((o) => o.id);
|
|
167
|
+
const attachments = await prisma.attachment.findMany({
|
|
104
168
|
where: {
|
|
105
|
-
|
|
169
|
+
operationId: {
|
|
170
|
+
in: operationIds,
|
|
171
|
+
},
|
|
106
172
|
},
|
|
107
|
-
update: doc,
|
|
108
|
-
create: doc,
|
|
109
173
|
});
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
|
|
174
|
+
// TODO add attachments from cached operations
|
|
175
|
+
const fileRegistry = {};
|
|
176
|
+
const operationsByScope = queryOperations.reduce((acc, operation) => {
|
|
177
|
+
const scope = operation.scope;
|
|
178
|
+
if (!acc[scope]) {
|
|
179
|
+
acc[scope] = [];
|
|
180
|
+
}
|
|
181
|
+
const result = storageToOperation(operation);
|
|
182
|
+
result.attachments = attachments.filter((a) => a.operationId === operation.id);
|
|
183
|
+
result.attachments.forEach(({ hash, ...file }) => {
|
|
184
|
+
fileRegistry[hash] = file;
|
|
185
|
+
});
|
|
186
|
+
acc[scope].push(result);
|
|
187
|
+
return acc;
|
|
188
|
+
}, cachedOperations);
|
|
189
|
+
const dbDoc = result;
|
|
190
|
+
const doc = {
|
|
191
|
+
created: dbDoc.created.toISOString(),
|
|
192
|
+
name: dbDoc.name ? dbDoc.name : "",
|
|
193
|
+
documentType: dbDoc.documentType,
|
|
194
|
+
initialState: JSON.parse(dbDoc.initialState),
|
|
195
|
+
state: undefined,
|
|
196
|
+
lastModified: new Date(dbDoc.lastModified).toISOString(),
|
|
197
|
+
operations: operationsByScope,
|
|
198
|
+
clipboard: [],
|
|
199
|
+
revision: JSON.parse(dbDoc.revision),
|
|
200
|
+
meta: dbDoc.meta ? JSON.parse(dbDoc.meta) : undefined,
|
|
201
|
+
attachments: {},
|
|
202
|
+
};
|
|
203
|
+
return doc;
|
|
204
|
+
}
|
|
205
|
+
async delete(documentId) {
|
|
206
|
+
try {
|
|
207
|
+
// delete out of drives
|
|
208
|
+
await this.db.drive.deleteMany({
|
|
113
209
|
where: {
|
|
114
|
-
|
|
210
|
+
driveDocuments: {
|
|
211
|
+
none: {
|
|
212
|
+
documentId,
|
|
213
|
+
},
|
|
214
|
+
},
|
|
115
215
|
},
|
|
116
216
|
});
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
217
|
+
}
|
|
218
|
+
catch (e) {
|
|
219
|
+
this.logger.error("Error deleting document from drives, could not delete DriveDocument links", e);
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
try {
|
|
223
|
+
// delete document
|
|
224
|
+
const result = await this.db.document.deleteMany({
|
|
225
|
+
where: {
|
|
226
|
+
id: documentId,
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
return result.count > 0;
|
|
230
|
+
}
|
|
231
|
+
catch (e) {
|
|
232
|
+
this.logger.error("Error deleting document from drives, could not delete Document", e);
|
|
233
|
+
const prismaError = e;
|
|
234
|
+
// Ignore Error: P2025: An operation failed because it depends on one or more records that were required but not found.
|
|
235
|
+
if ((prismaError.code && prismaError.code === "P2025") ||
|
|
236
|
+
prismaError.message?.includes("An operation failed because it depends on one or more records that were required but not found.")) {
|
|
237
|
+
return false;
|
|
126
238
|
}
|
|
239
|
+
throw e;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
async addChild(parentId, childId) {
|
|
243
|
+
if (parentId === childId) {
|
|
244
|
+
throw new Error("Cannot associate a document with itself");
|
|
127
245
|
}
|
|
128
|
-
|
|
246
|
+
// check if the child is a parent of the parent
|
|
247
|
+
const children = await this.getChildren(childId);
|
|
248
|
+
if (children.includes(parentId)) {
|
|
249
|
+
throw new Error("Cannot associate a document with its child");
|
|
250
|
+
}
|
|
251
|
+
// create the many-to-many relation
|
|
252
|
+
await this.db.document.update({
|
|
129
253
|
where: {
|
|
130
|
-
id,
|
|
254
|
+
id: childId,
|
|
131
255
|
},
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
256
|
+
data: {
|
|
257
|
+
driveDocuments: { create: { driveId: parentId } },
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
async removeChild(parentId, childId) {
|
|
262
|
+
try {
|
|
263
|
+
await this.db.driveDocument.delete({
|
|
264
|
+
where: {
|
|
265
|
+
// use unique constraint so it either deletes or throws
|
|
266
|
+
driveId_documentId: {
|
|
267
|
+
driveId: parentId,
|
|
268
|
+
documentId: childId,
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
});
|
|
272
|
+
return true;
|
|
273
|
+
}
|
|
274
|
+
catch (e) {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
async getChildren(parentId) {
|
|
279
|
+
const docs = await this.db.document.findMany({
|
|
280
|
+
select: {
|
|
281
|
+
id: true,
|
|
282
|
+
},
|
|
283
|
+
where: {
|
|
284
|
+
driveDocuments: {
|
|
285
|
+
some: {
|
|
286
|
+
driveId: parentId,
|
|
287
|
+
},
|
|
288
|
+
},
|
|
135
289
|
},
|
|
136
|
-
|
|
290
|
+
});
|
|
291
|
+
return docs.map((doc) => doc.id);
|
|
292
|
+
}
|
|
293
|
+
////////////////////////////////
|
|
294
|
+
// IDriveStorage
|
|
295
|
+
////////////////////////////////
|
|
296
|
+
async createDrive(id, drive) {
|
|
297
|
+
try {
|
|
298
|
+
await this.db.drive.create({
|
|
299
|
+
data: {
|
|
300
|
+
id,
|
|
301
|
+
slug: drive.initialState.state.global.slug ?? id,
|
|
302
|
+
},
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
catch (e) {
|
|
306
|
+
if (e instanceof PrismaClientKnownRequestError && e.code === "P2002") {
|
|
307
|
+
throw new Error(`Drive with slug ${drive.initialState.state.global.slug ?? id} already exists`);
|
|
308
|
+
}
|
|
309
|
+
throw e;
|
|
310
|
+
}
|
|
311
|
+
await this.db.document.create({
|
|
312
|
+
data: {
|
|
313
|
+
name: drive.name,
|
|
314
|
+
documentType: drive.documentType,
|
|
315
|
+
isDrive: true,
|
|
316
|
+
initialState: JSON.stringify(drive.initialState),
|
|
317
|
+
lastModified: drive.lastModified,
|
|
318
|
+
revision: JSON.stringify(drive.revision),
|
|
319
|
+
meta: drive.meta ? JSON.stringify(drive.meta) : undefined,
|
|
137
320
|
id,
|
|
138
|
-
slug: drive.initialState.state.global.slug ?? id,
|
|
139
321
|
},
|
|
140
322
|
});
|
|
141
323
|
}
|
|
@@ -161,7 +343,6 @@ export class PrismaStorage {
|
|
|
161
343
|
try {
|
|
162
344
|
await tx.operation.createMany({
|
|
163
345
|
data: operations.map((op) => ({
|
|
164
|
-
driveId: drive,
|
|
165
346
|
documentId: id,
|
|
166
347
|
hash: op.hash,
|
|
167
348
|
index: op.index,
|
|
@@ -288,141 +469,11 @@ export class PrismaStorage {
|
|
|
288
469
|
});
|
|
289
470
|
return count > 0;
|
|
290
471
|
}
|
|
291
|
-
async getDocument(driveId,
|
|
292
|
-
|
|
293
|
-
const query = {
|
|
294
|
-
where: {
|
|
295
|
-
id,
|
|
296
|
-
},
|
|
297
|
-
};
|
|
298
|
-
if (driveId !== "drives") {
|
|
299
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
300
|
-
query.where.driveDocuments = {
|
|
301
|
-
some: {
|
|
302
|
-
driveId,
|
|
303
|
-
},
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
const result = await prisma.document.findUnique(query);
|
|
307
|
-
if (result === null) {
|
|
308
|
-
throw new Error(`Document with id ${id} not found`);
|
|
309
|
-
}
|
|
310
|
-
const cachedOperations = (await this.delegate?.getCachedOperations(driveId, id)) ?? {
|
|
311
|
-
global: [],
|
|
312
|
-
local: [],
|
|
313
|
-
};
|
|
314
|
-
const scopeIndex = Object.keys(cachedOperations).reduceRight((acc, value) => {
|
|
315
|
-
const scope = value;
|
|
316
|
-
const lastIndex = cachedOperations[scope].at(-1)?.index ?? -1;
|
|
317
|
-
acc[scope] = lastIndex;
|
|
318
|
-
return acc;
|
|
319
|
-
}, { global: -1, local: -1 });
|
|
320
|
-
const conditions = Object.entries(scopeIndex).map(([scope, index]) => `("scope" = '${scope}' AND "index" > ${index})`);
|
|
321
|
-
conditions.push(`("scope" NOT IN (${Object.keys(cachedOperations)
|
|
322
|
-
.map((s) => `'${s}'`)
|
|
323
|
-
.join(", ")}))`);
|
|
324
|
-
// retrieves operations with resulting state
|
|
325
|
-
// for the last operation of each scope
|
|
326
|
-
// TODO prevent SQL injection
|
|
327
|
-
const queryOperations = await prisma.$queryRawUnsafe(`WITH ranked_operations AS (
|
|
328
|
-
SELECT
|
|
329
|
-
*,
|
|
330
|
-
ROW_NUMBER() OVER (PARTITION BY scope ORDER BY index DESC) AS rn
|
|
331
|
-
FROM "Operation"
|
|
332
|
-
)
|
|
333
|
-
SELECT
|
|
334
|
-
"id",
|
|
335
|
-
"opId",
|
|
336
|
-
"scope",
|
|
337
|
-
"branch",
|
|
338
|
-
"index",
|
|
339
|
-
"skip",
|
|
340
|
-
"hash",
|
|
341
|
-
"timestamp",
|
|
342
|
-
"input",
|
|
343
|
-
"type",
|
|
344
|
-
"context",
|
|
345
|
-
CASE
|
|
346
|
-
WHEN rn = 1 THEN "resultingState"
|
|
347
|
-
ELSE NULL
|
|
348
|
-
END AS "resultingState"
|
|
349
|
-
FROM ranked_operations
|
|
350
|
-
WHERE "driveId" = $1 AND "documentId" = $2
|
|
351
|
-
AND (${conditions.join(" OR ")})
|
|
352
|
-
ORDER BY scope, index;
|
|
353
|
-
`, driveId, id);
|
|
354
|
-
const operationIds = queryOperations.map((o) => o.id);
|
|
355
|
-
const attachments = await prisma.attachment.findMany({
|
|
356
|
-
where: {
|
|
357
|
-
operationId: {
|
|
358
|
-
in: operationIds,
|
|
359
|
-
},
|
|
360
|
-
},
|
|
361
|
-
});
|
|
362
|
-
// TODO add attachments from cached operations
|
|
363
|
-
const fileRegistry = {};
|
|
364
|
-
const operationsByScope = queryOperations.reduce((acc, operation) => {
|
|
365
|
-
const scope = operation.scope;
|
|
366
|
-
if (!acc[scope]) {
|
|
367
|
-
acc[scope] = [];
|
|
368
|
-
}
|
|
369
|
-
const result = storageToOperation(operation);
|
|
370
|
-
result.attachments = attachments.filter((a) => a.operationId === operation.id);
|
|
371
|
-
result.attachments.forEach(({ hash, ...file }) => {
|
|
372
|
-
fileRegistry[hash] = file;
|
|
373
|
-
});
|
|
374
|
-
acc[scope].push(result);
|
|
375
|
-
return acc;
|
|
376
|
-
}, cachedOperations);
|
|
377
|
-
const dbDoc = result;
|
|
378
|
-
const doc = {
|
|
379
|
-
created: dbDoc.created.toISOString(),
|
|
380
|
-
name: dbDoc.name ? dbDoc.name : "",
|
|
381
|
-
documentType: dbDoc.documentType,
|
|
382
|
-
initialState: JSON.parse(dbDoc.initialState),
|
|
383
|
-
state: undefined,
|
|
384
|
-
lastModified: new Date(dbDoc.lastModified).toISOString(),
|
|
385
|
-
operations: operationsByScope,
|
|
386
|
-
clipboard: [],
|
|
387
|
-
revision: JSON.parse(dbDoc.revision),
|
|
388
|
-
meta: dbDoc.meta ? JSON.parse(dbDoc.meta) : undefined,
|
|
389
|
-
attachments: {},
|
|
390
|
-
};
|
|
391
|
-
return doc;
|
|
472
|
+
async getDocument(driveId, documentId, tx) {
|
|
473
|
+
return this.get(documentId, tx);
|
|
392
474
|
}
|
|
393
475
|
async deleteDocument(drive, id) {
|
|
394
|
-
|
|
395
|
-
// delete out of drives
|
|
396
|
-
await this.db.drive.deleteMany({
|
|
397
|
-
where: {
|
|
398
|
-
driveDocuments: {
|
|
399
|
-
none: {
|
|
400
|
-
documentId: id,
|
|
401
|
-
},
|
|
402
|
-
},
|
|
403
|
-
},
|
|
404
|
-
});
|
|
405
|
-
// delete document
|
|
406
|
-
await this.db.document.deleteMany({
|
|
407
|
-
where: {
|
|
408
|
-
driveDocuments: {
|
|
409
|
-
some: {
|
|
410
|
-
driveId: drive,
|
|
411
|
-
},
|
|
412
|
-
},
|
|
413
|
-
id,
|
|
414
|
-
},
|
|
415
|
-
});
|
|
416
|
-
}
|
|
417
|
-
catch (e) {
|
|
418
|
-
const prismaError = e;
|
|
419
|
-
// Ignore Error: P2025: An operation failed because it depends on one or more records that were required but not found.
|
|
420
|
-
if ((prismaError.code && prismaError.code === "P2025") ||
|
|
421
|
-
prismaError.message?.includes("An operation failed because it depends on one or more records that were required but not found.")) {
|
|
422
|
-
return;
|
|
423
|
-
}
|
|
424
|
-
throw e;
|
|
425
|
-
}
|
|
476
|
+
await this.delete(id);
|
|
426
477
|
}
|
|
427
478
|
async getDrives() {
|
|
428
479
|
const drives = await this.db.drive.findMany({
|
|
@@ -474,7 +525,6 @@ export class PrismaStorage {
|
|
|
474
525
|
const operation = await this.db.operation.findUnique({
|
|
475
526
|
where: {
|
|
476
527
|
unique_operation: {
|
|
477
|
-
driveId,
|
|
478
528
|
documentId,
|
|
479
529
|
index,
|
|
480
530
|
scope,
|
|
@@ -491,26 +541,19 @@ export class PrismaStorage {
|
|
|
491
541
|
// TODO add branch condition
|
|
492
542
|
const whereClauses = units
|
|
493
543
|
.map((_, index) => {
|
|
494
|
-
return `("
|
|
544
|
+
return `("documentId" = $${index * 2 + 1} AND "scope" = $${index * 2 + 2})`;
|
|
495
545
|
})
|
|
496
546
|
.join(" OR ");
|
|
497
547
|
const query = `
|
|
498
|
-
SELECT "
|
|
548
|
+
SELECT "documentId", "scope", "branch", MAX("timestamp") as "lastUpdated", MAX("index") as revision FROM "Operation"
|
|
499
549
|
WHERE ${whereClauses}
|
|
500
|
-
GROUP BY "
|
|
550
|
+
GROUP BY "documentId", "scope", "branch"
|
|
501
551
|
`;
|
|
502
|
-
const params = units
|
|
503
|
-
.map((unit) => [
|
|
504
|
-
unit.documentId ? unit.driveId : "drives",
|
|
505
|
-
unit.documentId || unit.driveId,
|
|
506
|
-
unit.scope,
|
|
507
|
-
])
|
|
508
|
-
.flat();
|
|
552
|
+
const params = units.map((unit) => [unit.documentId, unit.scope]).flat();
|
|
509
553
|
const results = await this.db.$queryRawUnsafe(query, ...params);
|
|
510
554
|
return results.map((row) => ({
|
|
511
555
|
...row,
|
|
512
|
-
|
|
513
|
-
documentId: row.driveId === "drives" ? "" : row.documentId,
|
|
556
|
+
documentId: row.documentId,
|
|
514
557
|
lastUpdated: new Date(row.lastUpdated).toISOString(),
|
|
515
558
|
}));
|
|
516
559
|
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { type DocumentDriveDocument } from "#drive-document-model/gen/types";
|
|
2
2
|
import { type SynchronizationUnitQuery } from "#server/types";
|
|
3
|
-
import type { DocumentHeader, Operation, OperationFromDocument,
|
|
4
|
-
export interface IStorageDelegate {
|
|
5
|
-
getCachedOperations<TDocument extends PHDocument = PHDocument>(drive: string, id: string): Promise<OperationsFromDocument<TDocument> | undefined>;
|
|
6
|
-
}
|
|
3
|
+
import type { DocumentHeader, Operation, OperationFromDocument, PHDocument } from "document-model";
|
|
7
4
|
export interface IDocumentStorage {
|
|
8
5
|
exists(documentId: string): Promise<boolean>;
|
|
9
6
|
create(documentId: string, document: PHDocument): Promise<void>;
|
|
7
|
+
get<TDocument extends PHDocument>(documentId: string): Promise<TDocument>;
|
|
8
|
+
delete(documentId: string): Promise<boolean>;
|
|
9
|
+
addChild(parentId: string, childId: string): Promise<void>;
|
|
10
|
+
removeChild(parentId: string, childId: string): Promise<boolean>;
|
|
11
|
+
getChildren(parentId: string): Promise<string[]>;
|
|
10
12
|
}
|
|
11
13
|
export interface IStorage {
|
|
12
14
|
checkDocumentExists(drive: string, id: string): Promise<boolean>;
|
|
@@ -20,9 +22,7 @@ export interface IStorage {
|
|
|
20
22
|
}>): Promise<void>;
|
|
21
23
|
deleteDocument(drive: string, id: string): Promise<void>;
|
|
22
24
|
getOperationResultingState?(drive: string, id: string, index: number, scope: string, branch: string): Promise<string | undefined>;
|
|
23
|
-
setStorageDelegate?(delegate: IStorageDelegate): void;
|
|
24
25
|
getSynchronizationUnitsRevision(units: SynchronizationUnitQuery[]): Promise<{
|
|
25
|
-
driveId: string;
|
|
26
26
|
documentId: string;
|
|
27
27
|
scope: string;
|
|
28
28
|
branch: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/storage/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAC7E,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,KAAK,EACV,cAAc,EACd,SAAS,EACT,qBAAqB,EACrB,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/storage/types.ts"],"names":[],"mappings":"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"}
|