@dereekb/firebase-server 12.5.10 → 12.6.0
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/CHANGELOG.md +9 -0
- package/mailgun/package.json +1 -1
- package/model/package.json +1 -1
- package/model/src/lib/notification/index.d.ts +2 -2
- package/model/src/lib/notification/index.js +2 -2
- package/model/src/lib/notification/index.js.map +1 -1
- package/model/src/lib/notification/{notification.action.init.server.js → notification.action.init.service.js} +3 -3
- package/model/src/lib/notification/notification.action.init.service.js.map +1 -0
- package/model/src/lib/notification/{notification.action.server.d.ts → notification.action.service.d.ts} +1 -1
- package/model/src/lib/notification/{notification.action.server.js → notification.action.service.js} +2 -2
- package/model/src/lib/notification/notification.action.service.js.map +1 -0
- package/model/src/lib/notification/notification.expedite.service.d.ts +1 -1
- package/model/src/lib/notification/notification.module.d.ts +2 -2
- package/model/src/lib/notification/notification.module.js +12 -12
- package/model/src/lib/notification/notification.module.js.map +1 -1
- package/model/src/lib/storagefile/index.d.ts +1 -0
- package/model/src/lib/storagefile/index.js +1 -0
- package/model/src/lib/storagefile/index.js.map +1 -1
- package/model/src/lib/storagefile/storagefile.action.init.service.d.ts +62 -0
- package/model/src/lib/storagefile/storagefile.action.init.service.js +155 -0
- package/model/src/lib/storagefile/storagefile.action.init.service.js.map +1 -0
- package/model/src/lib/storagefile/storagefile.action.server.d.ts +79 -4
- package/model/src/lib/storagefile/storagefile.action.server.js +416 -97
- package/model/src/lib/storagefile/storagefile.action.server.js.map +1 -1
- package/model/src/lib/storagefile/storagefile.error.d.ts +5 -1
- package/model/src/lib/storagefile/storagefile.error.js +30 -2
- package/model/src/lib/storagefile/storagefile.error.js.map +1 -1
- package/model/src/lib/storagefile/storagefile.module.d.ts +4 -0
- package/model/src/lib/storagefile/storagefile.module.js +18 -4
- package/model/src/lib/storagefile/storagefile.module.js.map +1 -1
- package/model/src/lib/storagefile/storagefile.task.service.handler.d.ts +75 -5
- package/model/src/lib/storagefile/storagefile.task.service.handler.js +188 -5
- package/model/src/lib/storagefile/storagefile.task.service.handler.js.map +1 -1
- package/package.json +1 -1
- package/test/package.json +1 -1
- package/zoho/package.json +1 -1
- package/model/src/lib/notification/notification.action.init.server.js.map +0 -1
- package/model/src/lib/notification/notification.action.server.js.map +0 -1
- /package/model/src/lib/notification/{notification.action.init.server.d.ts → notification.action.init.service.d.ts} +0 -0
|
@@ -8,10 +8,18 @@ exports._initializeStorageFileFromUploadFileFactory = _initializeStorageFileFrom
|
|
|
8
8
|
exports.initializeStorageFileFromUploadFactory = initializeStorageFileFromUploadFactory;
|
|
9
9
|
exports.updateStorageFileFactory = updateStorageFileFactory;
|
|
10
10
|
exports.processAllQueuedStorageFilesFactory = processAllQueuedStorageFilesFactory;
|
|
11
|
+
exports._processStorageFileInTransactionFactory = _processStorageFileInTransactionFactory;
|
|
11
12
|
exports.processStorageFileFactory = processStorageFileFactory;
|
|
12
13
|
exports.deleteAllQueuedStorageFilesFactory = deleteAllQueuedStorageFilesFactory;
|
|
13
14
|
exports.deleteStorageFileFactory = deleteStorageFileFactory;
|
|
14
15
|
exports.downloadStorageFileFactory = downloadStorageFileFactory;
|
|
16
|
+
exports.createStorageFileGroupInTransactionFactory = createStorageFileGroupInTransactionFactory;
|
|
17
|
+
exports.createStorageFileGroupFactory = createStorageFileGroupFactory;
|
|
18
|
+
exports._syncStorageFileWithGroupsInTransactionFactory = _syncStorageFileWithGroupsInTransactionFactory;
|
|
19
|
+
exports.syncStorageFileWithGroupsFactory = syncStorageFileWithGroupsFactory;
|
|
20
|
+
exports.syncAllFlaggedStorageFilesWithGroupsFactory = syncAllFlaggedStorageFilesWithGroupsFactory;
|
|
21
|
+
exports.regenerateStorageFileGroupContentFactory = regenerateStorageFileGroupContentFactory;
|
|
22
|
+
exports.regenerateAllFlaggedStorageFileGroupsContentFactory = regenerateAllFlaggedStorageFileGroupsContentFactory;
|
|
15
23
|
const firebase_1 = require("@dereekb/firebase");
|
|
16
24
|
const firebase_server_1 = require("@dereekb/firebase-server");
|
|
17
25
|
const storagefile_error_1 = require("./storagefile.error");
|
|
@@ -39,7 +47,12 @@ function storageFileServerActions(context) {
|
|
|
39
47
|
processStorageFile: processStorageFileFactory(context),
|
|
40
48
|
deleteAllQueuedStorageFiles: deleteAllQueuedStorageFilesFactory(context),
|
|
41
49
|
deleteStorageFile: deleteStorageFileFactory(context),
|
|
42
|
-
downloadStorageFile: downloadStorageFileFactory(context)
|
|
50
|
+
downloadStorageFile: downloadStorageFileFactory(context),
|
|
51
|
+
createStorageFileGroup: createStorageFileGroupFactory(context),
|
|
52
|
+
syncStorageFileWithGroups: syncStorageFileWithGroupsFactory(context),
|
|
53
|
+
syncAllFlaggedStorageFilesWithGroups: syncAllFlaggedStorageFilesWithGroupsFactory(context),
|
|
54
|
+
regenerateStorageFileGroupContent: regenerateStorageFileGroupContentFactory(context),
|
|
55
|
+
regenerateAllFlaggedStorageFileGroupsContent: regenerateAllFlaggedStorageFileGroupsContentFactory(context)
|
|
43
56
|
};
|
|
44
57
|
}
|
|
45
58
|
// MARK: Actions
|
|
@@ -216,11 +229,15 @@ function processAllQueuedStorageFilesFactory(context) {
|
|
|
216
229
|
let storageFilesVisited = 0;
|
|
217
230
|
let storageFilesProcessStarted = 0;
|
|
218
231
|
let storageFilesFailedStarting = 0;
|
|
232
|
+
const proceessStorageFileParams = {
|
|
233
|
+
key: (0, firebase_1.firestoreDummyKey)()
|
|
234
|
+
};
|
|
235
|
+
const processStorageFileInstance = await processStorageFile(proceessStorageFileParams);
|
|
219
236
|
await (0, firebase_1.iterateFirestoreDocumentSnapshotPairs)({
|
|
220
237
|
documentAccessor: storageFileCollection.documentAccessor(),
|
|
221
238
|
iterateSnapshotPair: async (snapshotPair) => {
|
|
222
239
|
storageFilesVisited++;
|
|
223
|
-
const processStorageFileResult = await
|
|
240
|
+
const processStorageFileResult = await processStorageFileInstance(snapshotPair.document).catch(() => null);
|
|
224
241
|
if (processStorageFileResult) {
|
|
225
242
|
storageFilesProcessStarted++;
|
|
226
243
|
}
|
|
@@ -244,86 +261,115 @@ function processAllQueuedStorageFilesFactory(context) {
|
|
|
244
261
|
};
|
|
245
262
|
});
|
|
246
263
|
}
|
|
264
|
+
function _processStorageFileInTransactionFactory(context) {
|
|
265
|
+
const { storageFileCollection, notificationCollectionGroup } = context;
|
|
266
|
+
return async (input, transaction) => {
|
|
267
|
+
const { storageFileDocument, storageFile: inputStorageFile, params, expediteInstance } = input;
|
|
268
|
+
const { checkRetryProcessing, forceRestartProcessing, processAgainIfSuccessful } = params ?? {};
|
|
269
|
+
const storageFileDocumentInTransaction = storageFileCollection.documentAccessorForTransaction(transaction).loadDocumentFrom(storageFileDocument);
|
|
270
|
+
const storageFile = inputStorageFile ?? (await (0, firebase_server_1.assertSnapshotData)(storageFileDocumentInTransaction));
|
|
271
|
+
async function beginProcessing(overrideExistingTask) {
|
|
272
|
+
const state = storageFile.fs;
|
|
273
|
+
// check the storageFile is in the OK state
|
|
274
|
+
if (state !== firebase_1.StorageFileState.OK) {
|
|
275
|
+
throw (0, storagefile_error_1.storageFileProcessingNotAllowedForInvalidStateError)();
|
|
276
|
+
}
|
|
277
|
+
const createNotificationTaskResult = await (0, firebase_1.createNotificationDocument)({
|
|
278
|
+
context,
|
|
279
|
+
transaction,
|
|
280
|
+
template: (0, firebase_1.storageFileProcessingNotificationTaskTemplate)({
|
|
281
|
+
storageFileDocument,
|
|
282
|
+
overrideExistingTask
|
|
283
|
+
})
|
|
284
|
+
});
|
|
285
|
+
await storageFileDocumentInTransaction.update({
|
|
286
|
+
ps: firebase_1.StorageFileProcessingState.PROCESSING,
|
|
287
|
+
pat: new Date(), // set new processing start date
|
|
288
|
+
pcat: null, // clear processing completion date
|
|
289
|
+
pn: createNotificationTaskResult.notificationDocument.key
|
|
290
|
+
});
|
|
291
|
+
expediteInstance?.enqueueCreateResult(createNotificationTaskResult);
|
|
292
|
+
}
|
|
293
|
+
switch (storageFile.ps) {
|
|
294
|
+
case firebase_1.StorageFileProcessingState.INIT_OR_NONE:
|
|
295
|
+
// queue up for processing, unless it has no purpose
|
|
296
|
+
if (!storageFile.p) {
|
|
297
|
+
throw (0, storagefile_error_1.storageFileProcessingNotAvailableForTypeError)();
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
await beginProcessing(false);
|
|
301
|
+
}
|
|
302
|
+
break;
|
|
303
|
+
case firebase_1.StorageFileProcessingState.QUEUED_FOR_PROCESSING:
|
|
304
|
+
// begin processing
|
|
305
|
+
await beginProcessing(false);
|
|
306
|
+
break;
|
|
307
|
+
case firebase_1.StorageFileProcessingState.PROCESSING:
|
|
308
|
+
// check if the processing task is still running
|
|
309
|
+
const shouldCheckProcessing = !(0, util_1.isThrottled)(firebase_1.STORAGE_FILE_PROCESSING_STUCK_THROTTLE_CHECK_MS, storageFile.pat);
|
|
310
|
+
if (!storageFile.pn) {
|
|
311
|
+
await beginProcessing(true); // if no processing task is set, restart processing to recover from the broken state
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
const { pn } = storageFile;
|
|
315
|
+
const notificationDocument = notificationCollectionGroup.documentAccessorForTransaction(transaction).loadDocumentForKey(pn);
|
|
316
|
+
if (checkRetryProcessing || shouldCheckProcessing) {
|
|
317
|
+
const notification = await notificationDocument.snapshotData();
|
|
318
|
+
if (!notification) {
|
|
319
|
+
// the notification document is missing. Re-begin processing
|
|
320
|
+
await beginProcessing(true);
|
|
321
|
+
}
|
|
322
|
+
else if (notification.d || forceRestartProcessing) {
|
|
323
|
+
// if the notification is somehow in the done state but the StorageFile never got notified in the same transaction, requeue.
|
|
324
|
+
await beginProcessing(true);
|
|
325
|
+
}
|
|
326
|
+
// NOTE: We could look at the state of the notification task more, but at this point the task is probably still valid and still running,
|
|
327
|
+
// so we can only wait on it. In general if the task still exists and is not yet done, then we should wait on it as the
|
|
328
|
+
// task running system should complete eventually by design.
|
|
329
|
+
}
|
|
330
|
+
else if (expediteInstance) {
|
|
331
|
+
// enqueue the existing notification to be run in the expedite instance
|
|
332
|
+
expediteInstance.enqueue(notificationDocument);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
break;
|
|
336
|
+
case firebase_1.StorageFileProcessingState.DO_NOT_PROCESS:
|
|
337
|
+
throw (0, storagefile_error_1.storageFileProcessingNotQueuedForProcessingError)();
|
|
338
|
+
case firebase_1.StorageFileProcessingState.SUCCESS:
|
|
339
|
+
if (forceRestartProcessing || processAgainIfSuccessful) {
|
|
340
|
+
await beginProcessing(true);
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
throw (0, storagefile_error_1.storageFileAlreadyProcessedError)();
|
|
344
|
+
}
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
}
|
|
247
349
|
function processStorageFileFactory(context) {
|
|
248
|
-
const {
|
|
350
|
+
const { firestoreContext, notificationExpediteService, firebaseServerActionTransformFunctionFactory } = context;
|
|
351
|
+
const processStorageFileInTransaction = _processStorageFileInTransactionFactory(context);
|
|
249
352
|
return firebaseServerActionTransformFunctionFactory(firebase_1.ProcessStorageFileParams, async (params) => {
|
|
250
|
-
const { runImmediately
|
|
353
|
+
const { runImmediately } = params;
|
|
251
354
|
return async (storageFileDocument) => {
|
|
252
|
-
const result = {};
|
|
253
355
|
const expediteInstance = notificationExpediteService.expediteInstance();
|
|
254
356
|
await firestoreContext.runTransaction(async (transaction) => {
|
|
255
357
|
expediteInstance.initialize();
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
if (state !== firebase_1.StorageFileState.OK) {
|
|
262
|
-
throw (0, storagefile_error_1.storageFileProcessingNotAllowedForInvalidStateError)();
|
|
263
|
-
}
|
|
264
|
-
const createNotificationTaskResult = await (0, firebase_1.createNotificationDocument)({
|
|
265
|
-
context,
|
|
266
|
-
transaction,
|
|
267
|
-
template: (0, firebase_1.storageFileProcessingNotificationTaskTemplate)({
|
|
268
|
-
storageFileDocument,
|
|
269
|
-
overrideExistingTask
|
|
270
|
-
})
|
|
271
|
-
});
|
|
272
|
-
await storageFileDocumentInTransaction.update({
|
|
273
|
-
ps: firebase_1.StorageFileProcessingState.PROCESSING,
|
|
274
|
-
pat: new Date(), // set new processing start date
|
|
275
|
-
pcat: null, // clear processing completion date
|
|
276
|
-
pn: createNotificationTaskResult.notificationDocument.key
|
|
277
|
-
});
|
|
278
|
-
expediteInstance.enqueueCreateResult(createNotificationTaskResult);
|
|
279
|
-
}
|
|
280
|
-
switch (storageFile.ps) {
|
|
281
|
-
case firebase_1.StorageFileProcessingState.INIT_OR_NONE:
|
|
282
|
-
// queue up for processing, unless it has no purpose
|
|
283
|
-
if (!storageFile.p) {
|
|
284
|
-
throw (0, storagefile_error_1.storageFileProcessingNotAvailableForTypeError)();
|
|
285
|
-
}
|
|
286
|
-
else {
|
|
287
|
-
await beginProcessing(false);
|
|
288
|
-
}
|
|
289
|
-
break;
|
|
290
|
-
case firebase_1.StorageFileProcessingState.QUEUED_FOR_PROCESSING:
|
|
291
|
-
// begin processing
|
|
292
|
-
await beginProcessing(false);
|
|
293
|
-
break;
|
|
294
|
-
case firebase_1.StorageFileProcessingState.PROCESSING:
|
|
295
|
-
// check if the processing task is still running
|
|
296
|
-
const shouldCheckProcessing = !(0, util_1.isThrottled)(firebase_1.STORAGE_FILE_PROCESSING_STUCK_THROTTLE_CHECK_MS, storageFile.pat);
|
|
297
|
-
if (!storageFile.pn) {
|
|
298
|
-
await beginProcessing(true); // if no processing task is set, restart processing to recover from the broken state
|
|
299
|
-
}
|
|
300
|
-
else if (checkRetryProcessing || shouldCheckProcessing) {
|
|
301
|
-
const { pn } = storageFile;
|
|
302
|
-
const notificationDocument = notificationCollectionGroup.documentAccessorForTransaction(transaction).loadDocumentForKey(pn);
|
|
303
|
-
const notification = await notificationDocument.snapshotData();
|
|
304
|
-
if (!notification) {
|
|
305
|
-
// the notification document is missing. Re-begin processing
|
|
306
|
-
await beginProcessing(true);
|
|
307
|
-
}
|
|
308
|
-
else if (notification.d || forceRestartProcessing) {
|
|
309
|
-
// if the notification is somehow in the done state but the StorageFile never got notified in the same transaction, requeue.
|
|
310
|
-
await beginProcessing(true);
|
|
311
|
-
}
|
|
312
|
-
// NOTE: We could look at the state of the notification task more, but at this point the task is probably still valid and still running,
|
|
313
|
-
// so we can only wait on it. In general if the task still exists and is not yet done, then we should wait on it as the
|
|
314
|
-
// task running system should complete eventually by design.
|
|
315
|
-
}
|
|
316
|
-
break;
|
|
317
|
-
case firebase_1.StorageFileProcessingState.DO_NOT_PROCESS:
|
|
318
|
-
throw (0, storagefile_error_1.storageFileProcessingNotQueuedForProcessingError)();
|
|
319
|
-
case firebase_1.StorageFileProcessingState.SUCCESS:
|
|
320
|
-
throw (0, storagefile_error_1.storageFileAlreadySuccessfullyProcessedError)();
|
|
321
|
-
}
|
|
358
|
+
await processStorageFileInTransaction({
|
|
359
|
+
storageFileDocument,
|
|
360
|
+
params,
|
|
361
|
+
expediteInstance
|
|
362
|
+
}, transaction);
|
|
322
363
|
});
|
|
364
|
+
let expediteResult = null;
|
|
323
365
|
// expedite the task if requested
|
|
324
366
|
if (runImmediately) {
|
|
325
|
-
await expediteInstance.send();
|
|
367
|
+
expediteResult = await expediteInstance.send().then((x) => x[0]);
|
|
326
368
|
}
|
|
369
|
+
const result = {
|
|
370
|
+
runImmediately: runImmediately ?? false,
|
|
371
|
+
expediteResult
|
|
372
|
+
};
|
|
327
373
|
return result;
|
|
328
374
|
};
|
|
329
375
|
});
|
|
@@ -371,52 +417,68 @@ function deleteAllQueuedStorageFilesFactory(context) {
|
|
|
371
417
|
});
|
|
372
418
|
}
|
|
373
419
|
function deleteStorageFileFactory(context) {
|
|
374
|
-
const { storageService, storageFileCollection, firebaseServerActionTransformFunctionFactory } = context;
|
|
420
|
+
const { firestoreContext, storageService, storageFileCollection, firebaseServerActionTransformFunctionFactory } = context;
|
|
421
|
+
const syncStorageFileWithGroupsInTransaction = _syncStorageFileWithGroupsInTransactionFactory(context);
|
|
375
422
|
return firebaseServerActionTransformFunctionFactory(firebase_1.DeleteStorageFileParams, async (params) => {
|
|
376
423
|
const { force } = params;
|
|
377
424
|
return async (inputStorageFileDocument) => {
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
if (!
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
425
|
+
await firestoreContext.runTransaction(async (transaction) => {
|
|
426
|
+
const storageFileDocument = await storageFileCollection.documentAccessorForTransaction(transaction).loadDocumentFrom(inputStorageFileDocument);
|
|
427
|
+
const storageFile = await (0, firebase_server_1.assertSnapshotData)(storageFileDocument);
|
|
428
|
+
const fileAccessor = storageService.file(storageFile);
|
|
429
|
+
if (!force) {
|
|
430
|
+
if (!storageFile.sdat) {
|
|
431
|
+
throw (0, storagefile_error_1.storageFileNotFlaggedForDeletionError)();
|
|
432
|
+
}
|
|
433
|
+
else if (!(0, util_1.isPast)(storageFile.sdat)) {
|
|
434
|
+
throw (0, storagefile_error_1.storageFileCannotBeDeletedYetError)();
|
|
435
|
+
}
|
|
387
436
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
437
|
+
// remove the storage file from any groups
|
|
438
|
+
await syncStorageFileWithGroupsInTransaction({ storageFileDocument, storageFile, force: true, removeAllStorageFileGroups: true }, transaction);
|
|
439
|
+
// delete the file
|
|
440
|
+
await fileAccessor.delete().catch(() => null);
|
|
441
|
+
// delete the document
|
|
442
|
+
await storageFileDocument.accessor.delete();
|
|
443
|
+
});
|
|
393
444
|
};
|
|
394
445
|
});
|
|
395
446
|
}
|
|
396
447
|
function downloadStorageFileFactory(context) {
|
|
397
|
-
const { storageService, firebaseServerActionTransformFunctionFactory } = context;
|
|
448
|
+
const { storageService, firebaseServerActionTransformFunctionFactory, storageFileCollection } = context;
|
|
398
449
|
return firebaseServerActionTransformFunctionFactory(firebase_1.DownloadStorageFileParams, async (params) => {
|
|
399
|
-
const { asAdmin, expiresAt, expiresIn, responseDisposition, responseContentType } = params;
|
|
450
|
+
const { key: targetStorageFileDocumentKey, asAdmin, expiresAt, expiresIn: inputExpiresIn, responseDisposition, responseContentType } = params;
|
|
400
451
|
return async (storageFileDocument) => {
|
|
452
|
+
// if the StorageFileDocument was not provided, set it from the target key
|
|
453
|
+
if (!storageFileDocument) {
|
|
454
|
+
storageFileDocument = storageFileCollection.documentAccessor().loadDocumentForKey(targetStorageFileDocumentKey);
|
|
455
|
+
}
|
|
401
456
|
const storageFile = await (0, firebase_server_1.assertSnapshotData)(storageFileDocument);
|
|
402
457
|
const fileAccessor = storageService.file(storageFile);
|
|
403
458
|
let result;
|
|
404
459
|
if (fileAccessor.getSignedUrl) {
|
|
405
|
-
const
|
|
460
|
+
const expiresIn = inputExpiresIn ?? util_1.MS_IN_MINUTE * 30;
|
|
461
|
+
const expires = (0, util_1.expirationDetails)({ defaultExpiresFromDateToNow: true, expiresAt, expiresIn });
|
|
406
462
|
let downloadUrlExpiresAt = expires.getExpirationDate();
|
|
407
463
|
// if they're not an admin, limit the expiration to a max of 30 days.
|
|
408
464
|
if (downloadUrlExpiresAt && !asAdmin) {
|
|
409
465
|
const maxExpirationDate = (0, date_fns_1.addDays)(new Date(), 30);
|
|
410
466
|
downloadUrlExpiresAt = (0, date_1.findMinDate)([downloadUrlExpiresAt, maxExpirationDate]);
|
|
411
467
|
}
|
|
412
|
-
const downloadUrl = await
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
468
|
+
const [downloadUrl, metadata] = await Promise.all([
|
|
469
|
+
fileAccessor.getSignedUrl({
|
|
470
|
+
action: 'read',
|
|
471
|
+
expiresAt: downloadUrlExpiresAt ?? undefined,
|
|
472
|
+
responseDisposition: responseDisposition ?? undefined, // can be set by anyone
|
|
473
|
+
responseType: asAdmin ? (responseContentType ?? undefined) : undefined // can only be set by admins
|
|
474
|
+
}),
|
|
475
|
+
fileAccessor.getMetadata()
|
|
476
|
+
]);
|
|
418
477
|
result = {
|
|
419
|
-
url: downloadUrl
|
|
478
|
+
url: downloadUrl,
|
|
479
|
+
fileName: metadata.name ? (0, util_1.slashPathDetails)(metadata.name).end : undefined,
|
|
480
|
+
mimeType: responseContentType ?? metadata.contentType,
|
|
481
|
+
expiresAt: (0, util_1.unixDateTimeSecondsNumberFromDate)(downloadUrlExpiresAt)
|
|
420
482
|
};
|
|
421
483
|
}
|
|
422
484
|
else {
|
|
@@ -426,4 +488,261 @@ function downloadStorageFileFactory(context) {
|
|
|
426
488
|
};
|
|
427
489
|
});
|
|
428
490
|
}
|
|
491
|
+
function createStorageFileGroupInTransactionFactory(context) {
|
|
492
|
+
const { storageFileGroupCollection } = context;
|
|
493
|
+
return async (params, transaction) => {
|
|
494
|
+
const { now: inputNow, skipCreate, template } = params;
|
|
495
|
+
const now = inputNow ?? new Date();
|
|
496
|
+
const storageFileGroupDocument = (0, firebase_1.loadStorageFileGroupDocumentForReferencePair)(params, storageFileGroupCollection.documentAccessorForTransaction(transaction));
|
|
497
|
+
const storageFileGroupTemplate = {
|
|
498
|
+
o: (0, firebase_1.firestoreDummyKey)(), // set during initialization
|
|
499
|
+
cat: now,
|
|
500
|
+
s: true, // requires initialization
|
|
501
|
+
f: [],
|
|
502
|
+
...template
|
|
503
|
+
};
|
|
504
|
+
if (!skipCreate) {
|
|
505
|
+
await storageFileGroupDocument.create(storageFileGroupTemplate);
|
|
506
|
+
}
|
|
507
|
+
return {
|
|
508
|
+
storageFileGroupTemplate,
|
|
509
|
+
storageFileGroupDocument
|
|
510
|
+
};
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
function createStorageFileGroupFactory(context) {
|
|
514
|
+
const { firestoreContext, firebaseServerActionTransformFunctionFactory } = context;
|
|
515
|
+
const createStorageFileGroupInTransaction = createStorageFileGroupInTransactionFactory(context);
|
|
516
|
+
return firebaseServerActionTransformFunctionFactory(firebase_1.CreateStorageFileGroupParams, async (params) => {
|
|
517
|
+
const { model, storageFileId } = params;
|
|
518
|
+
const storageFileGroupRelatedModelKey = model ? model : storageFileId ? (0, firebase_1.inferKeyFromTwoWayFlatFirestoreModelKey)(storageFileId) : undefined;
|
|
519
|
+
if (!storageFileGroupRelatedModelKey) {
|
|
520
|
+
throw (0, storagefile_error_1.createStorageFileGroupInputError)();
|
|
521
|
+
}
|
|
522
|
+
return async () => {
|
|
523
|
+
const result = await firestoreContext.runTransaction(async (transaction) => {
|
|
524
|
+
const { storageFileGroupDocument } = await createStorageFileGroupInTransaction({ storageFileGroupRelatedModelKey }, transaction);
|
|
525
|
+
return storageFileGroupDocument;
|
|
526
|
+
});
|
|
527
|
+
return result;
|
|
528
|
+
};
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
function _syncStorageFileWithGroupsInTransactionFactory(context) {
|
|
532
|
+
const { storageFileCollection, storageFileGroupCollection } = context;
|
|
533
|
+
const createStorageFileGroupInTransaction = createStorageFileGroupInTransactionFactory(context);
|
|
534
|
+
return async (input, transaction) => {
|
|
535
|
+
const { storageFileDocument, storageFile: inputStorageFile, force, removeAllStorageFileGroups, skipStorageFileUpdate } = input;
|
|
536
|
+
const storageFileDocumentInTransaction = storageFileCollection.documentAccessorForTransaction(transaction).loadDocumentFrom(storageFileDocument);
|
|
537
|
+
const storageFileGroupDocumentAccessor = storageFileGroupCollection.documentAccessorForTransaction(transaction);
|
|
538
|
+
const storageFile = inputStorageFile ?? (await (0, firebase_server_1.assertSnapshotData)(storageFileDocumentInTransaction));
|
|
539
|
+
if (!storageFile.gs && !force) {
|
|
540
|
+
throw (0, storagefile_error_1.storageFileNotFlaggedForGroupsSyncError)();
|
|
541
|
+
}
|
|
542
|
+
const g = storageFile.g ?? [];
|
|
543
|
+
const storageFileGroupDocuments = (0, firebase_1.loadDocumentsForIds)(storageFileGroupDocumentAccessor, g);
|
|
544
|
+
const storageFileGroupPairs = await (0, firebase_1.getDocumentSnapshotDataPairs)(storageFileGroupDocuments);
|
|
545
|
+
let storageFilesGroupsCreated = 0;
|
|
546
|
+
let storageFilesGroupsUpdated = 0;
|
|
547
|
+
await (0, util_1.performAsyncTasks)(storageFileGroupPairs, async (storageFileGroupPair) => {
|
|
548
|
+
const { data: storageFileGroup, document: storageFileGroupDocument } = storageFileGroupPair;
|
|
549
|
+
const existsInStorageFileGroup = storageFileGroup?.f.some((x) => x.s === storageFileDocument.id);
|
|
550
|
+
const change = removeAllStorageFileGroups ? (existsInStorageFileGroup ? 'remove' : undefined) : !existsInStorageFileGroup ? 'add' : undefined;
|
|
551
|
+
switch (change) {
|
|
552
|
+
case 'add':
|
|
553
|
+
// add it if it doesn't exist
|
|
554
|
+
const createTemplate = (0, firebase_1.calculateStorageFileGroupEmbeddedFileUpdate)({
|
|
555
|
+
storageFileGroup: storageFileGroup ?? { f: [] },
|
|
556
|
+
insert: [
|
|
557
|
+
{
|
|
558
|
+
s: storageFileDocument.id
|
|
559
|
+
}
|
|
560
|
+
],
|
|
561
|
+
allowRecalculateRegenerateFlag: false
|
|
562
|
+
});
|
|
563
|
+
if (!storageFileGroup) {
|
|
564
|
+
// if the group does not exist, then create it
|
|
565
|
+
await createStorageFileGroupInTransaction({ storageFileGroupDocument, template: createTemplate }, transaction);
|
|
566
|
+
storageFilesGroupsCreated += 1;
|
|
567
|
+
}
|
|
568
|
+
else {
|
|
569
|
+
// if the group exists, then update it
|
|
570
|
+
await storageFileGroupDocument.update(createTemplate);
|
|
571
|
+
storageFilesGroupsUpdated += 1;
|
|
572
|
+
}
|
|
573
|
+
break;
|
|
574
|
+
case 'remove':
|
|
575
|
+
// remove it
|
|
576
|
+
const removeTemplate = (0, firebase_1.calculateStorageFileGroupEmbeddedFileUpdate)({
|
|
577
|
+
storageFileGroup: storageFileGroup ?? { f: [] },
|
|
578
|
+
remove: [storageFileDocument.id]
|
|
579
|
+
});
|
|
580
|
+
await storageFileGroupDocument.update(removeTemplate);
|
|
581
|
+
storageFilesGroupsUpdated += 1;
|
|
582
|
+
break;
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
const result = {
|
|
586
|
+
storageFilesGroupsCreated,
|
|
587
|
+
storageFilesGroupsUpdated
|
|
588
|
+
};
|
|
589
|
+
// update the storage file to no longer be flagged for sync
|
|
590
|
+
if (!skipStorageFileUpdate) {
|
|
591
|
+
await storageFileDocumentInTransaction.update({
|
|
592
|
+
gs: false
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
return result;
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
function syncStorageFileWithGroupsFactory(context) {
|
|
599
|
+
const { firestoreContext, storageFileCollection, storageFileGroupCollection, firebaseServerActionTransformFunctionFactory } = context;
|
|
600
|
+
const syncStorageFileWithGroupsInTransaction = _syncStorageFileWithGroupsInTransactionFactory(context);
|
|
601
|
+
return firebaseServerActionTransformFunctionFactory(firebase_1.SyncStorageFileWithGroupsParams, async (params) => {
|
|
602
|
+
const { force } = params;
|
|
603
|
+
return async (storageFileDocument) => {
|
|
604
|
+
return firestoreContext.runTransaction(async (transaction) => syncStorageFileWithGroupsInTransaction({ storageFileDocument, force }, transaction));
|
|
605
|
+
};
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
function syncAllFlaggedStorageFilesWithGroupsFactory(context) {
|
|
609
|
+
const { firebaseServerActionTransformFunctionFactory, storageFileCollection } = context;
|
|
610
|
+
const syncStorageFileWithGroups = syncStorageFileWithGroupsFactory(context);
|
|
611
|
+
return firebaseServerActionTransformFunctionFactory(firebase_1.SyncAllFlaggedStorageFilesWithGroupsParams, async (params) => {
|
|
612
|
+
return async () => {
|
|
613
|
+
const syncStorageFileWithGroupsInstance = await syncStorageFileWithGroups({
|
|
614
|
+
key: (0, firebase_1.firestoreDummyKey)(),
|
|
615
|
+
force: true // force anyways; they should all be flagged for sync when the query hits
|
|
616
|
+
});
|
|
617
|
+
let storageFilesSynced = 0;
|
|
618
|
+
let storageFilesGroupsCreated = 0;
|
|
619
|
+
let storageFilesGroupsUpdated = 0;
|
|
620
|
+
await (0, firebase_1.iterateFirestoreDocumentSnapshotPairBatches)({
|
|
621
|
+
documentAccessor: storageFileCollection.documentAccessor(),
|
|
622
|
+
iterateSnapshotPairsBatch: async (snapshotPairBatch) => {
|
|
623
|
+
// only sync StorageFiles that are flagged for sync
|
|
624
|
+
await (0, util_1.runAsyncTasksForValues)(snapshotPairBatch.filter((x) => x.data.gs), async (snapshotPair) => {
|
|
625
|
+
const { document: storageFileDocument } = snapshotPair;
|
|
626
|
+
const result = await syncStorageFileWithGroupsInstance(storageFileDocument);
|
|
627
|
+
storageFilesSynced += 1;
|
|
628
|
+
storageFilesGroupsCreated += result.storageFilesGroupsCreated;
|
|
629
|
+
storageFilesGroupsUpdated += result.storageFilesGroupsUpdated;
|
|
630
|
+
}, {
|
|
631
|
+
maxParallelTasks: 10, // can update 10 storageFiles/Groups at the same time
|
|
632
|
+
nonConcurrentTaskKeyFactory: (x) => x.data.g // do not update the same group at the same time
|
|
633
|
+
});
|
|
634
|
+
},
|
|
635
|
+
queryFactory: storageFileCollection,
|
|
636
|
+
constraintsFactory: () => (0, firebase_1.storageFileFlaggedForSyncWithGroupsQuery)(),
|
|
637
|
+
performTasksConfig: {
|
|
638
|
+
sequential: true // run batches sequentially to avoid contention in updating a StorageFileGroup
|
|
639
|
+
},
|
|
640
|
+
totalSnapshotsLimit: 1000,
|
|
641
|
+
limitPerCheckpoint: 100
|
|
642
|
+
});
|
|
643
|
+
const result = {
|
|
644
|
+
storageFilesSynced,
|
|
645
|
+
storageFilesGroupsCreated,
|
|
646
|
+
storageFilesGroupsUpdated
|
|
647
|
+
};
|
|
648
|
+
return result;
|
|
649
|
+
};
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
function regenerateStorageFileGroupContentFactory(context) {
|
|
653
|
+
const { firestoreContext, storageService, storageFileCollection, storageFileGroupCollection, firebaseServerActionTransformFunctionFactory } = context;
|
|
654
|
+
const processStorageFileInTransaction = _processStorageFileInTransactionFactory(context);
|
|
655
|
+
return firebaseServerActionTransformFunctionFactory(firebase_1.RegenerateStorageFileGroupContentParams, async (params) => {
|
|
656
|
+
const { force } = params;
|
|
657
|
+
const createStorageFileDocumentPair = (0, firebase_1.createStorageFileDocumentPairFactory)({
|
|
658
|
+
defaultCreationType: firebase_1.StorageFileCreationType.FOR_STORAGE_FILE_GROUP
|
|
659
|
+
});
|
|
660
|
+
return async (storageFileGroupDocument) => {
|
|
661
|
+
return firestoreContext.runTransaction(async (transaction) => {
|
|
662
|
+
const storageFileGroupDocumentInTransaction = storageFileGroupCollection.documentAccessorForTransaction(transaction).loadDocumentFrom(storageFileGroupDocument);
|
|
663
|
+
const storageFileGroup = await (0, firebase_server_1.assertSnapshotData)(storageFileGroupDocumentInTransaction);
|
|
664
|
+
const storageFileDocumentAccessor = storageFileCollection.documentAccessorForTransaction(transaction);
|
|
665
|
+
const { o, zsf, s } = storageFileGroup;
|
|
666
|
+
// must not be queued for initialization
|
|
667
|
+
if (s) {
|
|
668
|
+
throw (0, storagefile_error_1.storageFileGroupQueuedForInitializationError)();
|
|
669
|
+
}
|
|
670
|
+
const existingZipStorageFileDocument = zsf ? storageFileDocumentAccessor.loadDocumentForId(zsf) : undefined;
|
|
671
|
+
const [existingZipStorageFilePair] = await Promise.all([existingZipStorageFileDocument ? (0, firebase_1.getDocumentSnapshotDataPair)(existingZipStorageFileDocument) : undefined]);
|
|
672
|
+
let contentStorageFilesFlaggedForProcessing = 0;
|
|
673
|
+
const updateTemplate = {
|
|
674
|
+
re: false // clear the regeneration flag
|
|
675
|
+
};
|
|
676
|
+
// For each content type, create/update/flag the StorageFile for processing that type
|
|
677
|
+
const { regenerateZip } = (0, firebase_1.calculateStorageFileGroupRegeneration)({ storageFileGroup, force });
|
|
678
|
+
if (regenerateZip) {
|
|
679
|
+
// check that the storageFile exists, and if it doesn't, create a new one
|
|
680
|
+
if (!existingZipStorageFilePair?.data) {
|
|
681
|
+
const zipStorageFile = storageService.file((0, firebase_1.storageFileGroupZipFileStoragePath)(storageFileGroupDocument.id));
|
|
682
|
+
// create a new StorageFile
|
|
683
|
+
const { storageFileDocument } = await createStorageFileDocumentPair({
|
|
684
|
+
storagePathRef: zipStorageFile,
|
|
685
|
+
accessor: storageFileDocumentAccessor,
|
|
686
|
+
parentStorageFileGroup: storageFileGroupDocument,
|
|
687
|
+
purpose: firebase_1.STORAGE_FILE_GROUP_ZIP_STORAGE_FILE_PURPOSE,
|
|
688
|
+
shouldBeProcessed: true,
|
|
689
|
+
ownershipKey: o,
|
|
690
|
+
metadata: {
|
|
691
|
+
sfg: storageFileGroupDocument.id
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
updateTemplate.zsf = storageFileDocument.id;
|
|
695
|
+
}
|
|
696
|
+
else {
|
|
697
|
+
// flag it for processing again
|
|
698
|
+
await processStorageFileInTransaction({ params: { processAgainIfSuccessful: true }, storageFileDocument: existingZipStorageFilePair.document, storageFile: existingZipStorageFilePair.data }, transaction);
|
|
699
|
+
}
|
|
700
|
+
contentStorageFilesFlaggedForProcessing += 1;
|
|
701
|
+
}
|
|
702
|
+
// update the StorageFileGroup
|
|
703
|
+
await storageFileGroupDocumentInTransaction.update(updateTemplate);
|
|
704
|
+
const result = {
|
|
705
|
+
contentStorageFilesFlaggedForProcessing
|
|
706
|
+
};
|
|
707
|
+
return result;
|
|
708
|
+
});
|
|
709
|
+
};
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
function regenerateAllFlaggedStorageFileGroupsContentFactory(context) {
|
|
713
|
+
const { firebaseServerActionTransformFunctionFactory, storageFileGroupCollection } = context;
|
|
714
|
+
const regenerateStorageFileGroupContent = regenerateStorageFileGroupContentFactory(context);
|
|
715
|
+
return firebaseServerActionTransformFunctionFactory(firebase_1.RegenerateAllFlaggedStorageFileGroupsContentParams, async (params) => {
|
|
716
|
+
return async () => {
|
|
717
|
+
const regenerateStorageFileGroupContentInstance = await regenerateStorageFileGroupContent({
|
|
718
|
+
key: (0, firebase_1.firestoreDummyKey)()
|
|
719
|
+
});
|
|
720
|
+
let storageFileGroupsUpdated = 0;
|
|
721
|
+
let contentStorageFilesFlaggedForProcessing = 0;
|
|
722
|
+
await (0, firebase_1.iterateFirestoreDocumentSnapshotPairs)({
|
|
723
|
+
documentAccessor: storageFileGroupCollection.documentAccessor(),
|
|
724
|
+
iterateSnapshotPair: async (snapshotPair) => {
|
|
725
|
+
const { data: storageFileGroup } = snapshotPair;
|
|
726
|
+
if (!storageFileGroup.s) {
|
|
727
|
+
const result = await regenerateStorageFileGroupContentInstance(snapshotPair.document);
|
|
728
|
+
storageFileGroupsUpdated += 1;
|
|
729
|
+
contentStorageFilesFlaggedForProcessing += result.contentStorageFilesFlaggedForProcessing;
|
|
730
|
+
}
|
|
731
|
+
},
|
|
732
|
+
queryFactory: storageFileGroupCollection,
|
|
733
|
+
constraintsFactory: () => (0, firebase_1.storageFileGroupsFlaggedForContentRegenerationQuery)(),
|
|
734
|
+
performTasksConfig: {
|
|
735
|
+
maxParallelTasks: 10
|
|
736
|
+
},
|
|
737
|
+
totalSnapshotsLimit: 1000,
|
|
738
|
+
limitPerCheckpoint: 100
|
|
739
|
+
});
|
|
740
|
+
const result = {
|
|
741
|
+
storageFileGroupsUpdated,
|
|
742
|
+
contentStorageFilesFlaggedForProcessing
|
|
743
|
+
};
|
|
744
|
+
return result;
|
|
745
|
+
};
|
|
746
|
+
});
|
|
747
|
+
}
|
|
429
748
|
//# sourceMappingURL=storagefile.action.server.js.map
|