@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.
Files changed (39) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/mailgun/package.json +1 -1
  3. package/model/package.json +1 -1
  4. package/model/src/lib/notification/index.d.ts +2 -2
  5. package/model/src/lib/notification/index.js +2 -2
  6. package/model/src/lib/notification/index.js.map +1 -1
  7. package/model/src/lib/notification/{notification.action.init.server.js → notification.action.init.service.js} +3 -3
  8. package/model/src/lib/notification/notification.action.init.service.js.map +1 -0
  9. package/model/src/lib/notification/{notification.action.server.d.ts → notification.action.service.d.ts} +1 -1
  10. package/model/src/lib/notification/{notification.action.server.js → notification.action.service.js} +2 -2
  11. package/model/src/lib/notification/notification.action.service.js.map +1 -0
  12. package/model/src/lib/notification/notification.expedite.service.d.ts +1 -1
  13. package/model/src/lib/notification/notification.module.d.ts +2 -2
  14. package/model/src/lib/notification/notification.module.js +12 -12
  15. package/model/src/lib/notification/notification.module.js.map +1 -1
  16. package/model/src/lib/storagefile/index.d.ts +1 -0
  17. package/model/src/lib/storagefile/index.js +1 -0
  18. package/model/src/lib/storagefile/index.js.map +1 -1
  19. package/model/src/lib/storagefile/storagefile.action.init.service.d.ts +62 -0
  20. package/model/src/lib/storagefile/storagefile.action.init.service.js +155 -0
  21. package/model/src/lib/storagefile/storagefile.action.init.service.js.map +1 -0
  22. package/model/src/lib/storagefile/storagefile.action.server.d.ts +79 -4
  23. package/model/src/lib/storagefile/storagefile.action.server.js +416 -97
  24. package/model/src/lib/storagefile/storagefile.action.server.js.map +1 -1
  25. package/model/src/lib/storagefile/storagefile.error.d.ts +5 -1
  26. package/model/src/lib/storagefile/storagefile.error.js +30 -2
  27. package/model/src/lib/storagefile/storagefile.error.js.map +1 -1
  28. package/model/src/lib/storagefile/storagefile.module.d.ts +4 -0
  29. package/model/src/lib/storagefile/storagefile.module.js +18 -4
  30. package/model/src/lib/storagefile/storagefile.module.js.map +1 -1
  31. package/model/src/lib/storagefile/storagefile.task.service.handler.d.ts +75 -5
  32. package/model/src/lib/storagefile/storagefile.task.service.handler.js +188 -5
  33. package/model/src/lib/storagefile/storagefile.task.service.handler.js.map +1 -1
  34. package/package.json +1 -1
  35. package/test/package.json +1 -1
  36. package/zoho/package.json +1 -1
  37. package/model/src/lib/notification/notification.action.init.server.js.map +0 -1
  38. package/model/src/lib/notification/notification.action.server.js.map +0 -1
  39. /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 processStorageFile(snapshotPair.document).catch(() => null);
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 { storageFileCollection, notificationCollectionGroup, firestoreContext, notificationExpediteService, firebaseServerActionTransformFunctionFactory } = context;
350
+ const { firestoreContext, notificationExpediteService, firebaseServerActionTransformFunctionFactory } = context;
351
+ const processStorageFileInTransaction = _processStorageFileInTransactionFactory(context);
249
352
  return firebaseServerActionTransformFunctionFactory(firebase_1.ProcessStorageFileParams, async (params) => {
250
- const { runImmediately, checkRetryProcessing, forceRestartProcessing } = params;
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
- const storageFileDocumentInTransaction = await storageFileCollection.documentAccessorForTransaction(transaction).loadDocumentFrom(storageFileDocument);
257
- const storageFile = await (0, firebase_server_1.assertSnapshotData)(storageFileDocumentInTransaction);
258
- async function beginProcessing(overrideExistingTask) {
259
- const state = storageFile.fs;
260
- // check the storageFile is in the OK state
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
- const storageFileDocument = await storageFileCollection.documentAccessor().loadDocumentFrom(inputStorageFileDocument);
379
- const storageFile = await (0, firebase_server_1.assertSnapshotData)(storageFileDocument);
380
- const fileAccessor = storageService.file(storageFile);
381
- if (!force) {
382
- if (!storageFile.sdat) {
383
- throw (0, storagefile_error_1.storageFileNotFlaggedForDeletionError)();
384
- }
385
- else if (!(0, util_1.isPast)(storageFile.sdat)) {
386
- throw (0, storagefile_error_1.storageFileCannotBeDeletedYetError)();
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
- // delete the file
390
- await fileAccessor.delete().catch(() => null);
391
- // delete the document
392
- await storageFileDocument.accessor.delete();
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 expires = (0, util_1.expirationDetails)({ expiresAt, expiresIn });
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 fileAccessor.getSignedUrl({
413
- action: 'read',
414
- expiresAt: downloadUrlExpiresAt ?? undefined,
415
- responseDisposition: responseDisposition ?? undefined, // can be set by anyone
416
- responseType: asAdmin ? (responseContentType ?? undefined) : undefined // can only be set by admins
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