emberflow 1.3.46 → 1.3.47

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 (36) hide show
  1. package/lib/index-utils.d.ts +9 -2
  2. package/lib/index-utils.js +156 -7
  3. package/lib/index-utils.js.map +1 -1
  4. package/lib/index.d.ts +9 -4
  5. package/lib/index.js +23 -5
  6. package/lib/index.js.map +1 -1
  7. package/lib/logics/view-logics.d.ts +2 -2
  8. package/lib/logics/view-logics.js +2 -4
  9. package/lib/logics/view-logics.js.map +1 -1
  10. package/lib/sample-custom/db-structure.d.ts +10 -1
  11. package/lib/sample-custom/db-structure.js +9 -0
  12. package/lib/sample-custom/db-structure.js.map +1 -1
  13. package/lib/tests/index-utils.test.js +538 -43
  14. package/lib/tests/index-utils.test.js.map +1 -1
  15. package/lib/tests/index.test.js +8 -8
  16. package/lib/tests/index.test.js.map +1 -1
  17. package/lib/tests/logics/view-logics.test.js +40 -40
  18. package/lib/tests/logics/view-logics.test.js.map +1 -1
  19. package/lib/tests/utils/distribution.test.js +19 -27
  20. package/lib/tests/utils/distribution.test.js.map +1 -1
  21. package/lib/tests/utils/forms.test.js +14 -18
  22. package/lib/tests/utils/forms.test.js.map +1 -1
  23. package/lib/tests/utils/paths.test.js +4 -0
  24. package/lib/tests/utils/paths.test.js.map +1 -1
  25. package/lib/tests/utils/pubsub.test.js +58 -20
  26. package/lib/tests/utils/pubsub.test.js.map +1 -1
  27. package/lib/types.d.ts +6 -3
  28. package/lib/utils/distribution.js +2 -4
  29. package/lib/utils/distribution.js.map +1 -1
  30. package/lib/utils/forms.js +2 -3
  31. package/lib/utils/forms.js.map +1 -1
  32. package/lib/utils/pubsub.d.ts +1 -0
  33. package/lib/utils/pubsub.js +15 -3
  34. package/lib/utils/pubsub.js.map +1 -1
  35. package/package.json +1 -1
  36. package/src/sample-custom/db-structure.ts +9 -0
@@ -24,7 +24,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
26
  const admin = __importStar(require("firebase-admin"));
27
- const index_utils_1 = require("../index-utils");
27
+ const indexUtils = __importStar(require("../index-utils"));
28
28
  const firebase_admin_1 = require("firebase-admin");
29
29
  const index_1 = require("../index");
30
30
  const db_structure_1 = require("../sample-custom/db-structure");
@@ -35,6 +35,10 @@ const paths_1 = require("../utils/paths");
35
35
  const batch_1 = require("../utils/batch");
36
36
  const distribution = __importStar(require("../utils/distribution"));
37
37
  const forms = __importStar(require("../utils/forms"));
38
+ const indexutils = __importStar(require("../index-utils"));
39
+ const pubsub = __importStar(require("../utils/pubsub"));
40
+ const misc = __importStar(require("../utils/misc"));
41
+ const index_utils_1 = require("../index-utils");
38
42
  jest.spyOn(console, "log").mockImplementation();
39
43
  jest.spyOn(console, "info").mockImplementation();
40
44
  const projectConfig = {
@@ -84,7 +88,7 @@ describe("distributeDoc", () => {
84
88
  priority: "normal",
85
89
  dstPath: "/users/test-user-id/documents/test-doc-id",
86
90
  };
87
- await (0, index_utils_1.distributeDoc)(logicResultDoc);
91
+ await indexUtils.distributeDoc(logicResultDoc);
88
92
  expect(admin.firestore().doc).toHaveBeenCalledTimes(1);
89
93
  expect(admin.firestore().doc).toHaveBeenCalledWith("/users/test-user-id/documents/test-doc-id");
90
94
  expect(docDeleteMock).toHaveBeenCalledTimes(1);
@@ -97,7 +101,7 @@ describe("distributeDoc", () => {
97
101
  priority: "normal",
98
102
  dstPath: "/users/test-user-id/documents/test-doc-id",
99
103
  };
100
- await (0, index_utils_1.distributeDoc)(logicResultDoc, batch);
104
+ await indexUtils.distributeDoc(logicResultDoc, batch);
101
105
  expect(admin.firestore().doc).toHaveBeenCalledTimes(1);
102
106
  expect(admin.firestore().doc).toHaveBeenCalledWith("/users/test-user-id/documents/test-doc-id");
103
107
  expect(batchDeleteSpy).toHaveBeenCalledTimes(1);
@@ -111,7 +115,7 @@ describe("distributeDoc", () => {
111
115
  doc: { name: "test-doc-name-updated" },
112
116
  };
113
117
  const expectedData = Object.assign(Object.assign({}, logicResultDoc.doc), { "@id": "test-doc-id" });
114
- await (0, index_utils_1.distributeDoc)(logicResultDoc);
118
+ await indexUtils.distributeDoc(logicResultDoc);
115
119
  expect(admin.firestore().doc).toHaveBeenCalledTimes(1);
116
120
  expect(admin.firestore().doc).toHaveBeenCalledWith("/users/test-user-id/documents/test-doc-id");
117
121
  expect(docSetMock).toHaveBeenCalledTimes(1);
@@ -131,7 +135,7 @@ describe("distributeDoc", () => {
131
135
  dstPath: "/users/test-user-id/documents/test-doc-id",
132
136
  };
133
137
  const expectedData = Object.assign(Object.assign({}, logicResultDoc.doc), { "@id": "test-doc-id" });
134
- await (0, index_utils_1.distributeDoc)(logicResultDoc);
138
+ await indexUtils.distributeDoc(logicResultDoc);
135
139
  expect(admin.firestore().doc).toHaveBeenCalledTimes(1);
136
140
  expect(admin.firestore().doc).toHaveBeenCalledWith("/users/test-user-id/documents/test-doc-id");
137
141
  expect(queueInstructionsSpy).toHaveBeenCalledTimes(1);
@@ -146,7 +150,7 @@ describe("distributeDoc", () => {
146
150
  priority: "normal",
147
151
  dstPath: "/users/test-user-id/documents/test-doc-id",
148
152
  };
149
- await (0, index_utils_1.distributeDoc)(logicResultDoc, batch);
153
+ await indexUtils.distributeDoc(logicResultDoc, batch);
150
154
  expect(admin.firestore().doc).toHaveBeenCalledTimes(1);
151
155
  expect(admin.firestore().doc).toHaveBeenCalledWith("/users/test-user-id/documents/test-doc-id");
152
156
  expect(batchSetSpy).toHaveBeenCalledTimes(1);
@@ -161,7 +165,7 @@ describe("distributeDoc", () => {
161
165
  dstPath: "/users/test-user-id/documents/test-doc-id",
162
166
  };
163
167
  const formData = Object.assign({ "@docPath": logicResultDoc.dstPath, "@actionType": "create" }, logicResultDoc.doc);
164
- await (0, index_utils_1.distributeDoc)(logicResultDoc, batch);
168
+ await indexUtils.distributeDoc(logicResultDoc, batch);
165
169
  expect(admin.firestore().doc).toHaveBeenCalledTimes(1);
166
170
  expect(admin.firestore().doc).toHaveBeenCalledWith("/users/test-user-id/documents/test-doc-id");
167
171
  expect(queueSubmitFormSpy).toHaveBeenCalledTimes(1);
@@ -187,6 +191,7 @@ describe("distribute", () => {
187
191
  doc: jest.fn(() => dbDoc),
188
192
  });
189
193
  queueInstructionsSpy = jest.spyOn(distribution, "queueInstructions").mockResolvedValue();
194
+ jest.spyOn(pubsub, "createPubSubTopics").mockResolvedValue();
190
195
  });
191
196
  afterEach(() => {
192
197
  dbSpy.mockRestore();
@@ -211,7 +216,7 @@ describe("distribute", () => {
211
216
  }],
212
217
  ]]);
213
218
  (0, index_1.initializeEmberFlow)(projectConfig, admin, db_structure_1.dbStructure, db_structure_1.Entity, security_1.securityConfig, validators_1.validatorConfig, []);
214
- await (0, index_utils_1.distribute)(userDocsByDstPath);
219
+ await indexUtils.distribute(userDocsByDstPath);
215
220
  expect(admin.firestore().doc).toHaveBeenCalledTimes(1);
216
221
  expect(admin.firestore().doc).toHaveBeenCalledWith("/users/test-user-id/documents/test-doc-id");
217
222
  expect(queueInstructionsSpy).toHaveBeenCalledTimes(1);
@@ -237,7 +242,7 @@ describe("distribute", () => {
237
242
  dstPath: "/users/test-user-id/documents/test-doc-id",
238
243
  }],
239
244
  ]]);
240
- await (0, index_utils_1.distribute)(userDocsByDstPath);
245
+ await indexUtils.distribute(userDocsByDstPath);
241
246
  expect(admin.firestore().doc).toHaveBeenCalledTimes(1);
242
247
  expect(admin.firestore().doc).toHaveBeenCalledWith("/users/test-user-id/documents/test-doc-id");
243
248
  expect(batchDeleteSpy).toHaveBeenCalledTimes(1);
@@ -270,7 +275,7 @@ describe("distributeLater", () => {
270
275
  ["/users/test-user-id/documents/doc1", [doc1]],
271
276
  ["/users/test-user-id/documents/doc2", [doc2]],
272
277
  ]);
273
- await (0, index_utils_1.distributeLater)(usersDocsByDstPath);
278
+ await indexUtils.distributeLater(usersDocsByDstPath);
274
279
  expect(queueForDistributionLaterSpy).toHaveBeenCalledTimes(1);
275
280
  expect(queueForDistributionLaterSpy).toHaveBeenCalledWith(doc1, doc2);
276
281
  });
@@ -283,7 +288,7 @@ describe("validateForm", () => {
283
288
  email: "johndoe@example.com",
284
289
  password: "abc123",
285
290
  };
286
- const [hasValidationError, validationResult] = await (0, index_utils_1.validateForm)(entity, document);
291
+ const [hasValidationError, validationResult] = await indexUtils.validateForm(entity, document);
287
292
  expect(hasValidationError).toBe(false);
288
293
  expect(validationResult).toEqual({});
289
294
  });
@@ -294,7 +299,7 @@ describe("validateForm", () => {
294
299
  email: "johndoe@example.com",
295
300
  password: "abc",
296
301
  };
297
- const [hasValidationError, validationResult] = await (0, index_utils_1.validateForm)(entity, document);
302
+ const [hasValidationError, validationResult] = await indexUtils.validateForm(entity, document);
298
303
  expect(hasValidationError).toBe(true);
299
304
  expect(validationResult).toEqual({ name: ["Name is required"] });
300
305
  });
@@ -302,7 +307,7 @@ describe("validateForm", () => {
302
307
  describe("getFormModifiedFields", () => {
303
308
  it("should return an empty object when there are no form fields", () => {
304
309
  const document = { name: "John Doe", age: 30 };
305
- const modifiedFields = (0, index_utils_1.getFormModifiedFields)({}, document);
310
+ const modifiedFields = indexUtils.getFormModifiedFields({}, document);
306
311
  expect(modifiedFields).toEqual({});
307
312
  });
308
313
  it("should return an array of modified form fields", () => {
@@ -310,7 +315,7 @@ describe("getFormModifiedFields", () => {
310
315
  "name": "John Doe",
311
316
  "age": 30,
312
317
  };
313
- const modifiedFields = (0, index_utils_1.getFormModifiedFields)({
318
+ const modifiedFields = indexUtils.getFormModifiedFields({
314
319
  "name": "Jane Doe",
315
320
  "address": "123 Main St",
316
321
  "@status": "submit",
@@ -327,7 +332,7 @@ describe("delayFormSubmissionAndCheckIfCancelled", () => {
327
332
  val: () => ({ "@form": { "@status": "delay" } }),
328
333
  }),
329
334
  };
330
- const cancelFormSubmission = await (0, index_utils_1.delayFormSubmissionAndCheckIfCancelled)(delay, formResponseRef);
335
+ const cancelFormSubmission = await indexUtils.delayFormSubmissionAndCheckIfCancelled(delay, formResponseRef);
331
336
  expect(cancelFormSubmission).toBe(false);
332
337
  expect(formResponseRef.update).toHaveBeenCalledTimes(1);
333
338
  expect(formResponseRef.update).toHaveBeenCalledWith({ "@status": "delay" });
@@ -342,7 +347,7 @@ describe("delayFormSubmissionAndCheckIfCancelled", () => {
342
347
  val: () => ({ "@status": "cancel" }),
343
348
  }),
344
349
  };
345
- const cancelFormSubmission = await (0, index_utils_1.delayFormSubmissionAndCheckIfCancelled)(delay, formResponseRef);
350
+ const cancelFormSubmission = await indexUtils.delayFormSubmissionAndCheckIfCancelled(delay, formResponseRef);
346
351
  expect(cancelFormSubmission).toBe(true);
347
352
  expect(formResponseRef.update).toHaveBeenCalledTimes(1);
348
353
  expect(formResponseRef.update).toHaveBeenCalledWith({ "@status": "delay" });
@@ -366,7 +371,7 @@ describe("runBusinessLogics", () => {
366
371
  docId: "document123",
367
372
  formId: "form123",
368
373
  docPath: "users/user123",
369
- entity: "user",
374
+ entity: entity,
370
375
  },
371
376
  actionType,
372
377
  document: {
@@ -383,11 +388,16 @@ describe("runBusinessLogics", () => {
383
388
  let logicFn2;
384
389
  let logicFn3;
385
390
  let dbSpy;
391
+ let simulateSubmitFormSpy;
392
+ let updateLogicMetricsSpy;
393
+ let actionRef;
386
394
  beforeEach(() => {
387
395
  distributeFn = jest.fn();
388
396
  logicFn1 = jest.fn().mockResolvedValue({ status: "finished" });
389
397
  logicFn2 = jest.fn().mockResolvedValue({ status: "finished" });
390
398
  logicFn3 = jest.fn().mockResolvedValue({ status: "error", message: "Error message" });
399
+ simulateSubmitFormSpy = jest.spyOn(indexUtils._mockable, "simulateSubmitForm").mockResolvedValue();
400
+ updateLogicMetricsSpy = jest.spyOn(indexUtils._mockable, "updateLogicMetrics").mockResolvedValue();
391
401
  const dbDoc = {
392
402
  get: jest.fn().mockResolvedValue({
393
403
  data: jest.fn().mockReturnValue({
@@ -396,6 +406,17 @@ describe("runBusinessLogics", () => {
396
406
  }),
397
407
  };
398
408
  dbSpy = jest.spyOn(admin.firestore(), "doc").mockReturnValue(dbDoc);
409
+ const setActionMock = jest.fn().mockResolvedValue({
410
+ update: jest.fn(),
411
+ });
412
+ const updateActionMock = jest.fn().mockResolvedValue({
413
+ update: jest.fn(),
414
+ });
415
+ actionRef = {
416
+ set: setActionMock,
417
+ update: updateActionMock,
418
+ };
419
+ jest.spyOn(index_1._mockable, "initActionRef").mockReturnValue(actionRef);
399
420
  });
400
421
  afterEach(() => {
401
422
  // Cleanup
@@ -404,6 +425,8 @@ describe("runBusinessLogics", () => {
404
425
  logicFn3.mockRestore();
405
426
  distributeFn.mockRestore();
406
427
  dbSpy.mockRestore();
428
+ simulateSubmitFormSpy.mockRestore();
429
+ updateLogicMetricsSpy.mockRestore();
407
430
  });
408
431
  it("should call all matching logics and pass their results to distributeFn", async () => {
409
432
  const logics = [
@@ -430,13 +453,12 @@ describe("runBusinessLogics", () => {
430
453
  },
431
454
  ];
432
455
  (0, index_1.initializeEmberFlow)(projectConfig, admin, db_structure_1.dbStructure, db_structure_1.Entity, security_1.securityConfig, validators_1.validatorConfig, logics);
433
- const runStatus = await (0, index_utils_1.runBusinessLogics)(actionType, formModifiedFields, entity, action, distributeFn);
456
+ const runStatus = await indexUtils.runBusinessLogics(actionRef, action, distributeFn);
434
457
  expect(logicFn1).toHaveBeenCalledWith(action, undefined);
435
458
  expect(logicFn2).toHaveBeenCalledWith(action, undefined);
436
459
  expect(logicFn3).not.toHaveBeenCalled();
437
460
  expect(distributeFn).toHaveBeenCalledTimes(1);
438
- expect(distributeFn).toHaveBeenCalledWith([
439
- expect.objectContaining({
461
+ expect(distributeFn).toHaveBeenCalledWith(actionRef, [expect.objectContaining({
440
462
  status: "finished",
441
463
  execTime: expect.any(Number),
442
464
  timeFinished: expect.any(Timestamp),
@@ -445,9 +467,10 @@ describe("runBusinessLogics", () => {
445
467
  status: "finished",
446
468
  execTime: expect.any(Number),
447
469
  timeFinished: expect.any(Timestamp),
448
- }),
449
- ], 0);
470
+ })], 0);
450
471
  expect(runStatus).toEqual("done");
472
+ expect(updateLogicMetricsSpy).toHaveBeenCalledTimes(1);
473
+ expect(simulateSubmitFormSpy).toHaveBeenCalledTimes(1);
451
474
  });
452
475
  it("should recall logic when it returns \"partial-result\" status", async () => {
453
476
  logicFn2.mockResolvedValueOnce({ status: "partial-result", nextPage: {} });
@@ -468,12 +491,12 @@ describe("runBusinessLogics", () => {
468
491
  },
469
492
  ];
470
493
  (0, index_1.initializeEmberFlow)(projectConfig, admin, db_structure_1.dbStructure, db_structure_1.Entity, security_1.securityConfig, validators_1.validatorConfig, logics);
471
- const runStatus = await (0, index_utils_1.runBusinessLogics)(actionType, formModifiedFields, entity, action, distributeFn);
494
+ const runStatus = await indexUtils.runBusinessLogics(actionRef, action, distributeFn);
472
495
  expect(logicFn1).toHaveBeenCalledWith(action, undefined);
473
496
  expect(logicFn2).toHaveBeenCalledWith(action, undefined);
474
497
  expect(distributeFn).toHaveBeenCalledTimes(2);
475
- expect(distributeFn.mock.calls[0]).toEqual([[
476
- expect.objectContaining({
498
+ expect(distributeFn.mock.calls[0]).toEqual([actionRef,
499
+ [expect.objectContaining({
477
500
  status: "partial-result",
478
501
  nextPage: {},
479
502
  execTime: expect.any(Number),
@@ -483,16 +506,14 @@ describe("runBusinessLogics", () => {
483
506
  status: "finished",
484
507
  execTime: expect.any(Number),
485
508
  timeFinished: expect.any(Timestamp),
486
- }),
487
- ], 0]);
509
+ })], 0]);
488
510
  expect(logicFn2).toHaveBeenCalledWith(action, {});
489
- expect(distributeFn.mock.calls[1]).toEqual([[
490
- expect.objectContaining({
511
+ expect(distributeFn.mock.calls[1]).toEqual([actionRef,
512
+ [expect.objectContaining({
491
513
  status: "finished",
492
514
  execTime: expect.any(Number),
493
515
  timeFinished: expect.any(Timestamp),
494
- }),
495
- ], 1]);
516
+ })], 1]);
496
517
  expect(runStatus).toEqual("done");
497
518
  });
498
519
  it("should not call any logic when no matching logics are found but distributeFn should still be " +
@@ -521,12 +542,12 @@ describe("runBusinessLogics", () => {
521
542
  },
522
543
  ];
523
544
  (0, index_1.initializeEmberFlow)(projectConfig, admin, db_structure_1.dbStructure, db_structure_1.Entity, security_1.securityConfig, validators_1.validatorConfig, logics);
524
- const runStatus = await (0, index_utils_1.runBusinessLogics)(actionType, formModifiedFields, entity, action, distributeFn);
545
+ const runStatus = await indexUtils.runBusinessLogics(actionRef, action, distributeFn);
525
546
  expect(logicFn1).not.toHaveBeenCalled();
526
547
  expect(logicFn2).not.toHaveBeenCalled();
527
548
  expect(logicFn3).not.toHaveBeenCalled();
528
549
  expect(distributeFn).toHaveBeenCalledTimes(1);
529
- expect(distributeFn).toHaveBeenCalledWith([], 0);
550
+ expect(distributeFn).toHaveBeenCalledWith(actionRef, [], 0);
530
551
  expect(runStatus).toEqual("no-matching-logics");
531
552
  });
532
553
  it("should recall logic when it returns \"partial-result\" status indefinitely up to the config maxLogicResultPages", async () => {
@@ -548,7 +569,7 @@ describe("runBusinessLogics", () => {
548
569
  },
549
570
  ];
550
571
  (0, index_1.initializeEmberFlow)(projectConfig, admin, db_structure_1.dbStructure, db_structure_1.Entity, security_1.securityConfig, validators_1.validatorConfig, logics);
551
- const runStatus = await (0, index_utils_1.runBusinessLogics)(actionType, formModifiedFields, entity, action, distributeFn);
572
+ const runStatus = await indexUtils.runBusinessLogics(actionRef, action, distributeFn);
552
573
  expect(logicFn1).toHaveBeenCalledWith(action, undefined);
553
574
  expect(logicFn2).toHaveBeenCalledWith(action, undefined);
554
575
  expect(logicFn1).toHaveBeenCalledTimes(1);
@@ -583,7 +604,7 @@ describe("runBusinessLogics", () => {
583
604
  },
584
605
  ];
585
606
  (0, index_1.initializeEmberFlow)(projectConfig, admin, db_structure_1.dbStructure, db_structure_1.Entity, security_1.securityConfig, validators_1.validatorConfig, logics);
586
- const runStatus = await (0, index_utils_1.runBusinessLogics)(actionType, formModifiedFields, entity, action, distributeFn);
607
+ const runStatus = await indexUtils.runBusinessLogics(actionRef, action, distributeFn);
587
608
  expect(logicFn1).not.toHaveBeenCalled();
588
609
  expect(logicFn2).toHaveBeenCalledWith(action, undefined);
589
610
  expect(logicFn3).toHaveBeenCalledWith(action, undefined);
@@ -593,6 +614,356 @@ describe("runBusinessLogics", () => {
593
614
  expect(runStatus).toEqual("cancel-then-retry");
594
615
  });
595
616
  });
617
+ describe("simulateSubmitForm", () => {
618
+ const eventContext = {
619
+ id: "test-event-id",
620
+ uid: "test-user-id",
621
+ docPath: "servers/test-doc-id",
622
+ docId: "test-doc-id",
623
+ formId: "test-form-id",
624
+ entity: "servers",
625
+ };
626
+ const user = {
627
+ "@id": "test-user-id",
628
+ "username": "Topic Creator",
629
+ "avatarUrl": "Avatar URL",
630
+ "firstName": "Topic",
631
+ "lastName": "Creator",
632
+ };
633
+ const action = {
634
+ actionType: "create",
635
+ eventContext: eventContext,
636
+ user: user,
637
+ document: {},
638
+ status: "new",
639
+ timeCreated: admin.firestore.Timestamp.now(),
640
+ modifiedFields: {},
641
+ };
642
+ const distributeFn = jest.fn();
643
+ let runBusinessLogicsSpy;
644
+ let dataMock;
645
+ let docMock;
646
+ let now;
647
+ beforeEach(() => {
648
+ jest.spyOn(console, "debug").mockImplementation();
649
+ jest.spyOn(console, "warn").mockImplementation();
650
+ dataMock = jest.fn().mockReturnValue({});
651
+ const getMock = {
652
+ data: dataMock,
653
+ };
654
+ docMock = {
655
+ set: jest.fn(),
656
+ get: jest.fn().mockResolvedValue(getMock),
657
+ collection: jest.fn(() => collectionMock),
658
+ };
659
+ const collectionMock = {
660
+ doc: jest.fn(() => docMock),
661
+ };
662
+ jest.spyOn(admin.firestore(), "doc").mockReturnValue(docMock);
663
+ jest.spyOn(admin.firestore(), "collection").mockReturnValue(collectionMock);
664
+ now = Timestamp.now();
665
+ jest.spyOn(indexUtils._mockable, "createNowTimestamp").mockReturnValue(now);
666
+ runBusinessLogicsSpy =
667
+ jest.spyOn(indexutils, "runBusinessLogics").mockResolvedValue("done");
668
+ });
669
+ afterEach(() => {
670
+ jest.restoreAllMocks();
671
+ });
672
+ it("should skip when there is no logic result doc with 'simulate-submit-form' action", async () => {
673
+ await indexUtils._mockable.simulateSubmitForm([], action, distributeFn);
674
+ expect(console.debug).toHaveBeenCalledTimes(1);
675
+ expect(console.debug).toHaveBeenCalledWith("Simulating submit form: ", 0);
676
+ expect(runBusinessLogicsSpy).not.toHaveBeenCalled();
677
+ });
678
+ it("should skip when entity is undefined", async () => {
679
+ const logicResults = [];
680
+ const logicResultDoc = {
681
+ action: "simulate-submit-form",
682
+ dstPath: "no-entity/sample-doc-id",
683
+ doc: {
684
+ "@id": "no-entity/sample-doc-id",
685
+ },
686
+ };
687
+ const logicResult = {
688
+ name: "sampleLogicResult",
689
+ status: "finished",
690
+ documents: [logicResultDoc],
691
+ };
692
+ logicResults.push(logicResult);
693
+ await indexUtils._mockable.simulateSubmitForm(logicResults, action, distributeFn);
694
+ expect(console.warn).toHaveBeenCalledTimes(1);
695
+ expect(console.warn).toHaveBeenCalledWith("No matching entity found for logic no-entity/sample-doc-id. Skipping");
696
+ expect(runBusinessLogicsSpy).not.toHaveBeenCalled();
697
+ });
698
+ it("should skip when logic result doc is undefined", async () => {
699
+ const logicResults = [];
700
+ const logicResultDoc = {
701
+ action: "simulate-submit-form",
702
+ dstPath: "servers/sample-server-id",
703
+ };
704
+ const logicResult = {
705
+ name: "sampleLogicResult",
706
+ status: "finished",
707
+ documents: [logicResultDoc],
708
+ };
709
+ logicResults.push(logicResult);
710
+ await indexUtils._mockable.simulateSubmitForm(logicResults, action, distributeFn);
711
+ expect(console.warn).toHaveBeenCalledTimes(1);
712
+ expect(console.warn).toHaveBeenCalledWith("LogicResultDoc.doc should not be undefined. Skipping");
713
+ expect(runBusinessLogicsSpy).not.toHaveBeenCalled();
714
+ });
715
+ it("should skip when @actionType is undefined", async () => {
716
+ const logicResults = [];
717
+ const logicResultDoc = {
718
+ action: "simulate-submit-form",
719
+ dstPath: "servers/sample-server-id",
720
+ doc: { "@id": "sample-server-id" },
721
+ };
722
+ const logicResult = {
723
+ name: "sampleLogicResult",
724
+ status: "finished",
725
+ documents: [logicResultDoc],
726
+ };
727
+ logicResults.push(logicResult);
728
+ await indexUtils._mockable.simulateSubmitForm(logicResults, action, distributeFn);
729
+ expect(console.warn).toHaveBeenCalledTimes(1);
730
+ expect(console.warn).toHaveBeenCalledWith("No @actionType found. Skipping");
731
+ expect(runBusinessLogicsSpy).not.toHaveBeenCalled();
732
+ });
733
+ it("should skip when submitFormAs data is undefined", async () => {
734
+ dataMock.mockReturnValueOnce(undefined);
735
+ const logicResults = [];
736
+ const logicResultDoc = {
737
+ action: "simulate-submit-form",
738
+ dstPath: "servers/sample-server-id",
739
+ doc: {
740
+ "@actionType": "create",
741
+ "@submitFormAs": "test-user-id",
742
+ },
743
+ };
744
+ const logicResult = {
745
+ name: "sampleLogicResult",
746
+ status: "finished",
747
+ documents: [logicResultDoc],
748
+ };
749
+ logicResults.push(logicResult);
750
+ await indexUtils._mockable.simulateSubmitForm(logicResults, action, distributeFn);
751
+ expect(console.warn).toHaveBeenCalledTimes(1);
752
+ expect(console.warn).toHaveBeenCalledWith("User test-user-id not found. Skipping");
753
+ expect(runBusinessLogicsSpy).not.toHaveBeenCalled();
754
+ });
755
+ it("should simulate submit form correctly when submitFormAs is defined", async () => {
756
+ dataMock.mockReturnValueOnce({ "@id": "test-user-id" });
757
+ const logicResults = [];
758
+ const logicResultDoc = {
759
+ action: "simulate-submit-form",
760
+ dstPath: "servers/sample-server-id",
761
+ doc: {
762
+ "@actionType": "create",
763
+ "@submitFormAs": "test-user-id",
764
+ "name": "sample-server-name",
765
+ "createdAt": now,
766
+ },
767
+ };
768
+ const logicResult = {
769
+ name: "sampleLogicResult",
770
+ status: "finished",
771
+ documents: [logicResultDoc],
772
+ };
773
+ logicResults.push(logicResult);
774
+ const expectedEventContext = {
775
+ id: action.eventContext.id + "-1",
776
+ uid: action.eventContext.uid,
777
+ formId: action.eventContext.formId + "-1",
778
+ docId: "sample-server-id",
779
+ docPath: logicResultDoc.dstPath,
780
+ entity: "server",
781
+ };
782
+ const expectedAction = {
783
+ eventContext: expectedEventContext,
784
+ actionType: "create",
785
+ document: {},
786
+ modifiedFields: {
787
+ "name": "sample-server-name",
788
+ "createdAt": now,
789
+ },
790
+ user: { "@id": "test-user-id" },
791
+ status: "new",
792
+ timeCreated: now,
793
+ };
794
+ await indexUtils._mockable.simulateSubmitForm(logicResults, action, distributeFn);
795
+ expect(docMock.set).toHaveBeenCalledTimes(1);
796
+ expect(docMock.set).toHaveBeenCalledWith(expectedAction);
797
+ expect(runBusinessLogicsSpy).toHaveBeenCalledTimes(1);
798
+ expect(runBusinessLogicsSpy).toHaveBeenCalledWith(docMock, expectedAction, distributeFn);
799
+ });
800
+ it("should simulate submit form correctly when submitFormAs is undefined", async () => {
801
+ const logicResults = [];
802
+ const logicResultDoc = {
803
+ action: "simulate-submit-form",
804
+ dstPath: "servers/sample-server-id",
805
+ doc: {
806
+ "@actionType": "create",
807
+ "name": "sample-server-name",
808
+ "createdAt": now,
809
+ },
810
+ };
811
+ const logicResult = {
812
+ name: "sampleLogicResult",
813
+ status: "finished",
814
+ documents: [logicResultDoc],
815
+ };
816
+ logicResults.push(logicResult);
817
+ const expectedEventContext = {
818
+ id: action.eventContext.id + "-1",
819
+ uid: action.eventContext.uid,
820
+ formId: action.eventContext.formId + "-1",
821
+ docId: "sample-server-id",
822
+ docPath: logicResultDoc.dstPath,
823
+ entity: "server",
824
+ };
825
+ const expectedAction = {
826
+ eventContext: expectedEventContext,
827
+ actionType: "create",
828
+ document: {},
829
+ modifiedFields: {
830
+ "name": "sample-server-name",
831
+ "createdAt": now,
832
+ },
833
+ user: user,
834
+ status: "new",
835
+ timeCreated: now,
836
+ };
837
+ await indexUtils._mockable.simulateSubmitForm(logicResults, action, distributeFn);
838
+ expect(docMock.set).toHaveBeenCalledTimes(1);
839
+ expect(docMock.set).toHaveBeenCalledWith(expectedAction);
840
+ expect(runBusinessLogicsSpy).toHaveBeenCalledTimes(1);
841
+ expect(runBusinessLogicsSpy).toHaveBeenCalledWith(docMock, expectedAction, distributeFn);
842
+ });
843
+ it("should simulate submit form correctly with multiple logic result docs", async () => {
844
+ const logicResults = [];
845
+ const logicResultDoc1 = {
846
+ action: "simulate-submit-form",
847
+ dstPath: "servers/sample-server-id",
848
+ doc: {
849
+ "@actionType": "create",
850
+ "name": "sample-server-name",
851
+ "createdAt": now,
852
+ },
853
+ };
854
+ const logicResultDoc2 = {
855
+ action: "merge",
856
+ dstPath: "servers/merge-server-id",
857
+ doc: {
858
+ "@actionType": "update",
859
+ "name": "sample-server-name",
860
+ },
861
+ };
862
+ const logicResultDoc3 = {
863
+ action: "simulate-submit-form",
864
+ dstPath: "users/sample-user-id",
865
+ doc: {
866
+ "@actionType": "create",
867
+ "name": "sample-user-name",
868
+ },
869
+ };
870
+ const logicResult1 = {
871
+ name: "serverLogicResult",
872
+ status: "finished",
873
+ documents: [logicResultDoc1, logicResultDoc2],
874
+ };
875
+ const logicResult2 = {
876
+ name: "userLogicResult",
877
+ status: "finished",
878
+ documents: [logicResultDoc3],
879
+ };
880
+ logicResults.push(logicResult1);
881
+ logicResults.push(logicResult2);
882
+ const expectedServerEventContext = {
883
+ id: action.eventContext.id + "-1",
884
+ uid: action.eventContext.uid,
885
+ formId: action.eventContext.formId + "-1",
886
+ docId: "sample-server-id",
887
+ docPath: logicResultDoc1.dstPath,
888
+ entity: "server",
889
+ };
890
+ const expectedServerAction = {
891
+ eventContext: expectedServerEventContext,
892
+ actionType: "create",
893
+ document: {},
894
+ modifiedFields: {
895
+ "name": "sample-server-name",
896
+ "createdAt": now,
897
+ },
898
+ user: user,
899
+ status: "new",
900
+ timeCreated: now,
901
+ };
902
+ const expectedUserEventContext = {
903
+ id: action.eventContext.id + "-2",
904
+ uid: action.eventContext.uid,
905
+ formId: action.eventContext.formId + "-2",
906
+ docId: "sample-user-id",
907
+ docPath: logicResultDoc3.dstPath,
908
+ entity: "user",
909
+ };
910
+ const expectedUserAction = {
911
+ eventContext: expectedUserEventContext,
912
+ actionType: "create",
913
+ document: {},
914
+ modifiedFields: {
915
+ "name": "sample-user-name",
916
+ },
917
+ user: user,
918
+ status: "new",
919
+ timeCreated: now,
920
+ };
921
+ await indexUtils._mockable.simulateSubmitForm(logicResults, action, distributeFn);
922
+ expect(docMock.set).toHaveBeenCalledTimes(2);
923
+ expect(docMock.set).toHaveBeenNthCalledWith(1, expectedServerAction);
924
+ expect(docMock.set).toHaveBeenNthCalledWith(2, expectedUserAction);
925
+ expect(runBusinessLogicsSpy).toHaveBeenCalledTimes(2);
926
+ expect(runBusinessLogicsSpy).toHaveBeenNthCalledWith(1, docMock, expectedServerAction, distributeFn);
927
+ expect(runBusinessLogicsSpy).toHaveBeenNthCalledWith(2, docMock, expectedUserAction, distributeFn);
928
+ });
929
+ it("should skip when maximum retry count is reached", async () => {
930
+ runBusinessLogicsSpy.mockResolvedValueOnce("cancel-then-retry")
931
+ .mockResolvedValueOnce("cancel-then-retry")
932
+ .mockResolvedValueOnce("cancel-then-retry")
933
+ .mockResolvedValueOnce("cancel-then-retry")
934
+ .mockResolvedValueOnce("cancel-then-retry")
935
+ .mockResolvedValueOnce("cancel-then-retry");
936
+ const logicResults = [];
937
+ const doc = {
938
+ "@actionType": "create",
939
+ };
940
+ const logicResultDoc = {
941
+ action: "simulate-submit-form",
942
+ dstPath: "servers/sample-server-id",
943
+ doc: doc,
944
+ };
945
+ const logicResult = {
946
+ name: "sampleLogicResult",
947
+ status: "finished",
948
+ documents: [logicResultDoc],
949
+ };
950
+ logicResults.push(logicResult);
951
+ const dateSpy = jest.spyOn(Date, "now");
952
+ dateSpy.mockReturnValueOnce(now.toMillis())
953
+ .mockReturnValueOnce(now.toMillis() + 2000)
954
+ .mockReturnValueOnce(now.toMillis())
955
+ .mockReturnValueOnce(now.toMillis() + 4000)
956
+ .mockReturnValueOnce(now.toMillis())
957
+ .mockReturnValueOnce(now.toMillis() + 8000)
958
+ .mockReturnValueOnce(now.toMillis())
959
+ .mockReturnValueOnce(now.toMillis() + 16000)
960
+ .mockReturnValueOnce(now.toMillis())
961
+ .mockReturnValueOnce(now.toMillis() + 32000);
962
+ await indexUtils._mockable.simulateSubmitForm(logicResults, action, distributeFn);
963
+ expect(console.warn).toHaveBeenCalledTimes(1);
964
+ expect(console.warn).toHaveBeenCalledWith("Maximum retry count reached for logic servers/sample-server-id");
965
+ });
966
+ });
596
967
  describe("groupDocsByUserAndDstPath", () => {
597
968
  (0, index_1.initializeEmberFlow)(projectConfig, admin, db_structure_1.dbStructure, db_structure_1.Entity, {}, {}, []);
598
969
  const docsByDstPath = new Map([
@@ -615,7 +986,7 @@ describe("groupDocsByUserAndDstPath", () => {
615
986
  ["othercollection/document5", [{ action: "merge", priority: "normal", dstPath: "othercollection/document5", doc: { field5: "value5" } }]],
616
987
  ]),
617
988
  };
618
- const results = (0, index_utils_1.groupDocsByUserAndDstPath)(docsByDstPath, userId);
989
+ const results = indexUtils.groupDocsByUserAndDstPath(docsByDstPath, userId);
619
990
  expect(results).toEqual(expectedResults);
620
991
  });
621
992
  });
@@ -727,7 +1098,7 @@ describe("expandConsolidateAndGroupByDstPath", () => {
727
1098
  ];
728
1099
  // Act
729
1100
  const logicResultDocs = logicResults.map((logicResult) => logicResult.documents).flat();
730
- const result = await (0, index_utils_1.expandConsolidateAndGroupByDstPath)(logicResultDocs);
1101
+ const result = await indexUtils.expandConsolidateAndGroupByDstPath(logicResultDocs);
731
1102
  // Assert
732
1103
  const expectedResult = new Map([
733
1104
  ["path1/doc1", [{ action: "merge", priority: "normal", dstPath: "path1/doc1", doc: { field1: "value1a", field3: "value3" }, instructions: { field2: "--", field4: "--" } }]],
@@ -773,7 +1144,7 @@ describe("expandConsolidateAndGroupByDstPath", () => {
773
1144
  ];
774
1145
  // Act
775
1146
  const logicResultDocs = logicResults.map((logicResult) => logicResult.documents).flat();
776
- const result = await (0, index_utils_1.expandConsolidateAndGroupByDstPath)(logicResultDocs);
1147
+ const result = await indexUtils.expandConsolidateAndGroupByDstPath(logicResultDocs);
777
1148
  // Assert
778
1149
  const expectedResult = new Map([
779
1150
  ["path1/doc1", [{
@@ -836,7 +1207,7 @@ describe("expandConsolidateAndGroupByDstPath", () => {
836
1207
  ];
837
1208
  // Act
838
1209
  const logicResultDocs = logicResults.map((logicResult) => logicResult.documents).flat();
839
- const result = await (0, index_utils_1.expandConsolidateAndGroupByDstPath)(logicResultDocs);
1210
+ const result = await indexUtils.expandConsolidateAndGroupByDstPath(logicResultDocs);
840
1211
  // Assert
841
1212
  const expectedResult = new Map([
842
1213
  ["path1/doc1", [{
@@ -892,7 +1263,7 @@ describe("expandConsolidateAndGroupByDstPath", () => {
892
1263
  ];
893
1264
  // Act
894
1265
  const logicResultDocs = logicResults.map((logicResult) => logicResult.documents).flat();
895
- const result = await (0, index_utils_1.expandConsolidateAndGroupByDstPath)(logicResultDocs);
1266
+ const result = await indexUtils.expandConsolidateAndGroupByDstPath(logicResultDocs);
896
1267
  // Assert
897
1268
  const expectedResult = new Map([
898
1269
  ["path1/doc1", [{
@@ -940,7 +1311,7 @@ describe("runViewLogics", () => {
940
1311
  },
941
1312
  ];
942
1313
  beforeEach(() => {
943
- jest.spyOn(index_utils_1._mockable, "getViewLogicsConfig").mockReturnValue(customViewLogicsConfig);
1314
+ jest.spyOn(indexUtils._mockable, "getViewLogicsConfig").mockReturnValue(customViewLogicsConfig);
944
1315
  });
945
1316
  it("should run view logics properly", async () => {
946
1317
  const logicResult1 = {
@@ -956,8 +1327,8 @@ describe("runViewLogics", () => {
956
1327
  };
957
1328
  viewLogicFn1.mockResolvedValue({});
958
1329
  viewLogicFn2.mockResolvedValue({});
959
- const results1 = await (0, index_utils_1.runViewLogics)(logicResult1);
960
- const results2 = await (0, index_utils_1.runViewLogics)(logicResult2);
1330
+ const results1 = await indexUtils.runViewLogics(logicResult1);
1331
+ const results2 = await indexUtils.runViewLogics(logicResult2);
961
1332
  const results = [...results1, ...results2];
962
1333
  expect(viewLogicFn1).toHaveBeenCalledTimes(2);
963
1334
  expect(viewLogicFn1.mock.calls[0][0]).toBe(logicResult1);
@@ -967,4 +1338,128 @@ describe("runViewLogics", () => {
967
1338
  expect(results).toHaveLength(3);
968
1339
  });
969
1340
  });
1341
+ describe("updateLogicMetrics", () => {
1342
+ let colSpy;
1343
+ let docMock;
1344
+ let queueInstructionsSpy;
1345
+ let warnSpy;
1346
+ let setMock;
1347
+ beforeEach(() => {
1348
+ setMock = jest.fn().mockResolvedValue({});
1349
+ warnSpy = jest.spyOn(console, "warn").mockImplementation();
1350
+ docMock = jest.fn().mockImplementation((doc) => {
1351
+ return {
1352
+ path: `@metrics/${doc}`,
1353
+ collection: jest.fn().mockReturnValue({
1354
+ doc: jest.fn().mockReturnValue({
1355
+ set: setMock,
1356
+ }),
1357
+ }),
1358
+ };
1359
+ });
1360
+ colSpy = jest.spyOn(admin.firestore(), "collection").mockReturnValue({
1361
+ doc: docMock,
1362
+ });
1363
+ queueInstructionsSpy = jest.spyOn(distribution, "queueInstructions").mockResolvedValue();
1364
+ });
1365
+ afterEach(() => {
1366
+ colSpy.mockRestore();
1367
+ docMock.mockRestore();
1368
+ queueInstructionsSpy.mockRestore();
1369
+ warnSpy.mockRestore();
1370
+ });
1371
+ it("should skip logic result when execTime is undefined", async () => {
1372
+ const logicResults = [];
1373
+ const logicResult = {
1374
+ name: "sampleLogicResult",
1375
+ };
1376
+ logicResults.push(logicResult);
1377
+ await indexUtils._mockable.updateLogicMetrics(logicResults);
1378
+ expect(colSpy).toHaveBeenCalledTimes(1);
1379
+ expect(colSpy).toHaveBeenCalledWith("@metrics");
1380
+ expect(warnSpy).toHaveBeenCalledTimes(1);
1381
+ expect(warnSpy).toHaveBeenCalledWith("No execTime found for logic sampleLogicResult");
1382
+ expect(docMock).not.toHaveBeenCalled();
1383
+ expect(setMock).not.toHaveBeenCalled();
1384
+ expect(queueInstructionsSpy).not.toHaveBeenCalled();
1385
+ });
1386
+ it("should queue logic result metrics", async () => {
1387
+ const logicResults = [];
1388
+ const logicResult1 = {
1389
+ name: "sampleLogicResult",
1390
+ execTime: 100,
1391
+ };
1392
+ const logicResult2 = {
1393
+ name: "anotherLogicResult",
1394
+ execTime: 200,
1395
+ };
1396
+ logicResults.push(logicResult1);
1397
+ logicResults.push(logicResult2);
1398
+ await indexUtils._mockable.updateLogicMetrics(logicResults);
1399
+ expect(colSpy).toHaveBeenCalledTimes(1);
1400
+ expect(colSpy).toHaveBeenCalledWith("@metrics");
1401
+ expect(warnSpy).toHaveBeenCalledWith("anotherLogicResult took 200ms to execute");
1402
+ expect(docMock).toHaveBeenCalledTimes(2);
1403
+ expect(docMock).toHaveBeenNthCalledWith(1, "sampleLogicResult");
1404
+ expect(docMock).toHaveBeenNthCalledWith(2, "anotherLogicResult");
1405
+ expect(queueInstructionsSpy).toHaveBeenCalledTimes(2);
1406
+ expect(queueInstructionsSpy).toHaveBeenNthCalledWith(1, `@metrics/${logicResult1.name}`, {
1407
+ totalExecTime: `+${logicResult1.execTime}`,
1408
+ totalExecCount: "++",
1409
+ });
1410
+ expect(queueInstructionsSpy).toHaveBeenNthCalledWith(2, `@metrics/${logicResult2.name}`, {
1411
+ totalExecTime: `+${logicResult2.execTime}`,
1412
+ totalExecCount: "++",
1413
+ });
1414
+ expect(setMock).toHaveBeenCalledTimes(2);
1415
+ expect(setMock).toHaveBeenNthCalledWith(1, {
1416
+ execDate: expect.any(Timestamp),
1417
+ execTime: logicResult1.execTime,
1418
+ });
1419
+ expect(setMock).toHaveBeenNthCalledWith(2, {
1420
+ execDate: expect.any(Timestamp),
1421
+ execTime: logicResult2.execTime,
1422
+ });
1423
+ });
1424
+ });
1425
+ describe("cleanLogicMetricsExecutions", () => {
1426
+ let colGetMock;
1427
+ let deleteCollectionSpy;
1428
+ beforeEach(() => {
1429
+ colGetMock = jest.fn().mockResolvedValue({
1430
+ docs: [
1431
+ {
1432
+ ref: {
1433
+ collection: jest.fn().mockReturnValue({
1434
+ where: jest.fn().mockReturnValue({}),
1435
+ }),
1436
+ },
1437
+ },
1438
+ ],
1439
+ });
1440
+ jest.spyOn(admin.firestore(), "collection").mockReturnValue({
1441
+ get: colGetMock,
1442
+ });
1443
+ deleteCollectionSpy = jest.spyOn(misc, "deleteCollection")
1444
+ .mockImplementation(async (query, callback) => {
1445
+ if (callback) {
1446
+ await callback({ size: 1 });
1447
+ }
1448
+ return Promise.resolve();
1449
+ });
1450
+ });
1451
+ afterEach(() => {
1452
+ jest.restoreAllMocks();
1453
+ });
1454
+ it("should clean logic metrics executions", async () => {
1455
+ jest.spyOn(console, "info").mockImplementation();
1456
+ await (0, index_utils_1.cleanLogicMetricsExecutions)({});
1457
+ expect(console.info).toHaveBeenCalledWith("Running cleanLogicMetricsExecutions");
1458
+ expect(admin.firestore().collection).toHaveBeenCalledTimes(1);
1459
+ expect(admin.firestore().collection).toHaveBeenCalledWith("@metrics");
1460
+ expect(colGetMock).toHaveBeenCalledTimes(1);
1461
+ expect(deleteCollectionSpy).toHaveBeenCalled();
1462
+ expect(console.info).toHaveBeenCalledWith("Cleaned 1 logic metrics executions");
1463
+ });
1464
+ });
970
1465
  //# sourceMappingURL=index-utils.test.js.map