emberflow 1.5.0 → 2.0.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/README.md +231 -4
- package/lib/index-utils.d.ts +18 -7
- package/lib/index-utils.js +71 -76
- package/lib/index-utils.js.map +1 -1
- package/lib/index.d.ts +22 -6
- package/lib/index.js +51 -36
- package/lib/index.js.map +1 -1
- package/lib/logics/view-logics.d.ts +0 -2
- package/lib/logics/view-logics.js +1 -19
- package/lib/logics/view-logics.js.map +1 -1
- package/lib/sample-custom/cleanup-configs.d.ts +2 -0
- package/lib/sample-custom/cleanup-configs.js +17 -0
- package/lib/sample-custom/cleanup-configs.js.map +1 -0
- package/lib/sample-custom/index.js +14 -1
- package/lib/sample-custom/index.js.map +1 -1
- package/lib/sample-custom/one-time-patches.d.ts +2 -0
- package/lib/sample-custom/one-time-patches.js +14 -0
- package/lib/sample-custom/one-time-patches.js.map +1 -0
- package/lib/tests/index-utils.test.js +349 -88
- package/lib/tests/index-utils.test.js.map +1 -1
- package/lib/tests/index.test.js +68 -4
- package/lib/tests/index.test.js.map +1 -1
- package/lib/tests/logics/patch-logics.test.js +50 -5
- package/lib/tests/logics/patch-logics.test.js.map +1 -1
- package/lib/tests/logics/view-logics.test.js +22 -4
- package/lib/tests/logics/view-logics.test.js.map +1 -1
- package/lib/tests/utils/cleanup.test.d.ts +1 -0
- package/lib/tests/utils/cleanup.test.js +243 -0
- package/lib/tests/utils/cleanup.test.js.map +1 -0
- package/lib/tests/utils/distribution.test.js +256 -1
- package/lib/tests/utils/distribution.test.js.map +1 -1
- package/lib/tests/utils/forms.test.js +10 -86
- package/lib/tests/utils/forms.test.js.map +1 -1
- package/lib/tests/utils/misc.test.js +96 -2
- package/lib/tests/utils/misc.test.js.map +1 -1
- package/lib/tests/utils/paths.test.js +20 -2
- package/lib/tests/utils/paths.test.js.map +1 -1
- package/lib/tests/utils/pubsub.test.js +10 -31
- package/lib/tests/utils/pubsub.test.js.map +1 -1
- package/lib/types.d.ts +51 -2
- package/lib/utils/cleanup.d.ts +5 -0
- package/lib/utils/cleanup.js +99 -0
- package/lib/utils/cleanup.js.map +1 -0
- package/lib/utils/distribution.d.ts +17 -3
- package/lib/utils/distribution.js +61 -23
- package/lib/utils/distribution.js.map +1 -1
- package/lib/utils/forms.d.ts +0 -2
- package/lib/utils/forms.js +1 -35
- package/lib/utils/forms.js.map +1 -1
- package/lib/utils/misc.d.ts +1 -0
- package/lib/utils/misc.js +32 -1
- package/lib/utils/misc.js.map +1 -1
- package/lib/utils/pubsub.d.ts +0 -2
- package/lib/utils/pubsub.js +1 -16
- package/lib/utils/pubsub.js.map +1 -1
- package/package.json +1 -1
- package/src/sample-custom/cleanup-configs.ts +15 -0
- package/src/sample-custom/index.ts +15 -11
- package/src/sample-custom/one-time-patches.ts +13 -0
|
@@ -36,9 +36,9 @@ const paths_1 = require("../utils/paths");
|
|
|
36
36
|
const batch_1 = require("../utils/batch");
|
|
37
37
|
const distribution = __importStar(require("../utils/distribution"));
|
|
38
38
|
const forms = __importStar(require("../utils/forms"));
|
|
39
|
-
const misc = __importStar(require("../utils/misc"));
|
|
40
39
|
const viewLogics = __importStar(require("../logics/view-logics"));
|
|
41
40
|
const view_logics_1 = require("../logics/view-logics");
|
|
41
|
+
const patchLogics = __importStar(require("../logics/patch-logics"));
|
|
42
42
|
const patch_logics_1 = require("../sample-custom/patch-logics");
|
|
43
43
|
var Timestamp = firebase_admin_1.firestore.Timestamp;
|
|
44
44
|
// should mock when using initializeEmberFlow and testing db.doc() calls count
|
|
@@ -262,7 +262,7 @@ describe("distributeDoc", () => {
|
|
|
262
262
|
expect(transactionSetMock).toHaveBeenNthCalledWith(1, dstDocRef, expectedInstructions, { merge: true });
|
|
263
263
|
expect(transactionSetMock).toHaveBeenNthCalledWith(2, dstDocRef, expectedData, { merge: true });
|
|
264
264
|
});
|
|
265
|
-
it("should
|
|
265
|
+
it("should allow submit-form logicResultDoc in transaction (handled later)", async () => {
|
|
266
266
|
const logicResultDoc = {
|
|
267
267
|
action: "submit-form",
|
|
268
268
|
priority: "normal",
|
|
@@ -272,10 +272,11 @@ describe("distributeDoc", () => {
|
|
|
272
272
|
},
|
|
273
273
|
dstPath: "/users/test-user-id/documents/test-doc-id",
|
|
274
274
|
};
|
|
275
|
-
const
|
|
275
|
+
const debugSpy = jest.spyOn(console, "debug").mockImplementation();
|
|
276
276
|
await indexUtils.distributeDoc(logicResultDoc, appVersion, undefined, transactionMock);
|
|
277
|
-
expect(
|
|
277
|
+
expect(debugSpy).toHaveBeenCalledWith("Submit-form in transactional logic result will be handled after transaction success");
|
|
278
278
|
expect(transactionSetMock).toHaveBeenCalledTimes(0);
|
|
279
|
+
debugSpy.mockRestore();
|
|
279
280
|
});
|
|
280
281
|
});
|
|
281
282
|
describe("batched", () => {
|
|
@@ -488,7 +489,16 @@ describe("distribute", () => {
|
|
|
488
489
|
dstPath: "/users/test-user-id/documents/test-doc-id",
|
|
489
490
|
}],
|
|
490
491
|
]]);
|
|
491
|
-
(0, index_1.initializeEmberFlow)(
|
|
492
|
+
(0, index_1.initializeEmberFlow)({
|
|
493
|
+
projectConfig,
|
|
494
|
+
admin,
|
|
495
|
+
dbStructure: db_structure_1.dbStructure,
|
|
496
|
+
Entity: db_structure_1.Entity,
|
|
497
|
+
securityConfigs: security_1.securityConfigs,
|
|
498
|
+
validatorConfigs: validators_1.validatorConfigs,
|
|
499
|
+
logicConfigs: [],
|
|
500
|
+
patchLogicConfigs: patch_logics_1.patchLogicConfigs,
|
|
501
|
+
});
|
|
492
502
|
await indexUtils.distributeFnNonTransactional(userDocsByDstPath, appVersion);
|
|
493
503
|
expect(admin.firestore().doc).toHaveBeenCalledTimes(1);
|
|
494
504
|
expect(admin.firestore().doc).toHaveBeenCalledWith("/users/test-user-id/documents/test-doc-id");
|
|
@@ -525,7 +535,16 @@ describe("distribute", () => {
|
|
|
525
535
|
describe("distributeLater", () => {
|
|
526
536
|
let queueForDistributionLaterSpy;
|
|
527
537
|
beforeEach(() => {
|
|
528
|
-
(0, index_1.initializeEmberFlow)(
|
|
538
|
+
(0, index_1.initializeEmberFlow)({
|
|
539
|
+
projectConfig,
|
|
540
|
+
admin,
|
|
541
|
+
dbStructure: db_structure_1.dbStructure,
|
|
542
|
+
Entity: db_structure_1.Entity,
|
|
543
|
+
securityConfigs: security_1.securityConfigs,
|
|
544
|
+
validatorConfigs: validators_1.validatorConfigs,
|
|
545
|
+
logicConfigs: [],
|
|
546
|
+
patchLogicConfigs: patch_logics_1.patchLogicConfigs,
|
|
547
|
+
});
|
|
529
548
|
queueForDistributionLaterSpy = jest.spyOn(distribution, "queueForDistributionLater").mockResolvedValue();
|
|
530
549
|
});
|
|
531
550
|
afterEach(() => {
|
|
@@ -557,7 +576,16 @@ describe("distributeLater", () => {
|
|
|
557
576
|
});
|
|
558
577
|
describe("validateForm", () => {
|
|
559
578
|
beforeEach(() => {
|
|
560
|
-
(0, index_1.initializeEmberFlow)(
|
|
579
|
+
(0, index_1.initializeEmberFlow)({
|
|
580
|
+
projectConfig,
|
|
581
|
+
admin,
|
|
582
|
+
dbStructure: db_structure_1.dbStructure,
|
|
583
|
+
Entity: db_structure_1.Entity,
|
|
584
|
+
securityConfigs: security_1.securityConfigs,
|
|
585
|
+
validatorConfigs: validators_1.validatorConfigs,
|
|
586
|
+
logicConfigs: [],
|
|
587
|
+
patchLogicConfigs: patch_logics_1.patchLogicConfigs,
|
|
588
|
+
});
|
|
561
589
|
});
|
|
562
590
|
const targetVersion = "2.5";
|
|
563
591
|
const entity = "user";
|
|
@@ -594,7 +622,16 @@ describe("validateForm", () => {
|
|
|
594
622
|
version: "02.04.06",
|
|
595
623
|
},
|
|
596
624
|
];
|
|
597
|
-
(0, index_1.initializeEmberFlow)(
|
|
625
|
+
(0, index_1.initializeEmberFlow)({
|
|
626
|
+
projectConfig,
|
|
627
|
+
admin,
|
|
628
|
+
dbStructure: db_structure_1.dbStructure,
|
|
629
|
+
Entity: db_structure_1.Entity,
|
|
630
|
+
securityConfigs: security_1.securityConfigs,
|
|
631
|
+
validatorConfigs,
|
|
632
|
+
logicConfigs: [],
|
|
633
|
+
patchLogicConfigs: patch_logics_1.patchLogicConfigs,
|
|
634
|
+
});
|
|
598
635
|
const document = {
|
|
599
636
|
name: "John Doe",
|
|
600
637
|
email: "johndoe@example.com",
|
|
@@ -642,7 +679,16 @@ describe("validateForm", () => {
|
|
|
642
679
|
version: "2.0.0",
|
|
643
680
|
},
|
|
644
681
|
];
|
|
645
|
-
(0, index_1.initializeEmberFlow)(
|
|
682
|
+
(0, index_1.initializeEmberFlow)({
|
|
683
|
+
projectConfig,
|
|
684
|
+
admin,
|
|
685
|
+
dbStructure: db_structure_1.dbStructure,
|
|
686
|
+
Entity: db_structure_1.Entity,
|
|
687
|
+
securityConfigs: security_1.securityConfigs,
|
|
688
|
+
validatorConfigs,
|
|
689
|
+
logicConfigs: [],
|
|
690
|
+
patchLogicConfigs: patch_logics_1.patchLogicConfigs,
|
|
691
|
+
});
|
|
646
692
|
const document = {
|
|
647
693
|
name: "John Doe",
|
|
648
694
|
};
|
|
@@ -690,7 +736,16 @@ describe("getSecurityFn", () => {
|
|
|
690
736
|
version: "02.04.06",
|
|
691
737
|
},
|
|
692
738
|
];
|
|
693
|
-
(0, index_1.initializeEmberFlow)(
|
|
739
|
+
(0, index_1.initializeEmberFlow)({
|
|
740
|
+
projectConfig,
|
|
741
|
+
admin,
|
|
742
|
+
dbStructure: db_structure_1.dbStructure,
|
|
743
|
+
Entity: db_structure_1.Entity,
|
|
744
|
+
securityConfigs,
|
|
745
|
+
validatorConfigs: validators_1.validatorConfigs,
|
|
746
|
+
logicConfigs: [],
|
|
747
|
+
patchLogicConfigs: patch_logics_1.patchLogicConfigs,
|
|
748
|
+
});
|
|
694
749
|
const result = await indexUtils.getSecurityFn(entity, targetVersion);
|
|
695
750
|
expect(result).toEqual(userSecurityFnV2);
|
|
696
751
|
});
|
|
@@ -709,7 +764,16 @@ describe("getSecurityFn", () => {
|
|
|
709
764
|
version: "2.0.0",
|
|
710
765
|
},
|
|
711
766
|
];
|
|
712
|
-
(0, index_1.initializeEmberFlow)(
|
|
767
|
+
(0, index_1.initializeEmberFlow)({
|
|
768
|
+
projectConfig,
|
|
769
|
+
admin,
|
|
770
|
+
dbStructure: db_structure_1.dbStructure,
|
|
771
|
+
Entity: db_structure_1.Entity,
|
|
772
|
+
securityConfigs,
|
|
773
|
+
validatorConfigs: validators_1.validatorConfigs,
|
|
774
|
+
logicConfigs: [],
|
|
775
|
+
patchLogicConfigs: patch_logics_1.patchLogicConfigs,
|
|
776
|
+
});
|
|
713
777
|
const entity = "user";
|
|
714
778
|
// targetVersion is 1.0.0, even if app is 2.0.0
|
|
715
779
|
const result = await indexUtils.getSecurityFn(entity, "1.0.0");
|
|
@@ -768,7 +832,16 @@ describe("delayFormSubmissionAndCheckIfCancelled", () => {
|
|
|
768
832
|
});
|
|
769
833
|
});
|
|
770
834
|
describe("groupDocsByTargetDocPath", () => {
|
|
771
|
-
(0, index_1.initializeEmberFlow)(
|
|
835
|
+
(0, index_1.initializeEmberFlow)({
|
|
836
|
+
projectConfig,
|
|
837
|
+
admin,
|
|
838
|
+
dbStructure: db_structure_1.dbStructure,
|
|
839
|
+
Entity: db_structure_1.Entity,
|
|
840
|
+
securityConfigs: [],
|
|
841
|
+
validatorConfigs: [],
|
|
842
|
+
logicConfigs: [],
|
|
843
|
+
patchLogicConfigs: [],
|
|
844
|
+
});
|
|
772
845
|
const docsByDstPath = new Map([
|
|
773
846
|
["users/user123/document1", [{ action: "merge", priority: "normal", dstPath: "users/user123/document1", doc: { field1: "value1", field2: "value2" } }]],
|
|
774
847
|
["users/user123/document1/threads/thread1", [{ action: "merge", priority: "normal", dstPath: "users/user123/document1/threads/thread1", doc: { field3: "value3", field6: "value6" } }]],
|
|
@@ -811,7 +884,7 @@ describe("runBusinessLogics", () => {
|
|
|
811
884
|
docId: "document123",
|
|
812
885
|
formId: "form123",
|
|
813
886
|
docPath: "users/user123",
|
|
814
|
-
entity
|
|
887
|
+
entity,
|
|
815
888
|
},
|
|
816
889
|
actionType,
|
|
817
890
|
document: {
|
|
@@ -919,7 +992,16 @@ describe("runBusinessLogics", () => {
|
|
|
919
992
|
obsoleteStartingFromVersion: "2", // Obsolete after appVersion
|
|
920
993
|
},
|
|
921
994
|
];
|
|
922
|
-
(0, index_1.initializeEmberFlow)(
|
|
995
|
+
(0, index_1.initializeEmberFlow)({
|
|
996
|
+
projectConfig,
|
|
997
|
+
admin,
|
|
998
|
+
dbStructure: db_structure_1.dbStructure,
|
|
999
|
+
Entity: db_structure_1.Entity,
|
|
1000
|
+
securityConfigs: security_1.securityConfigs,
|
|
1001
|
+
validatorConfigs: validators_1.validatorConfigs,
|
|
1002
|
+
logicConfigs: logics,
|
|
1003
|
+
patchLogicConfigs: patch_logics_1.patchLogicConfigs,
|
|
1004
|
+
});
|
|
923
1005
|
const txnGet = { get: jest.fn() };
|
|
924
1006
|
const { logicResults } = await indexUtils.runBusinessLogics(txnGet, action, appVersion);
|
|
925
1007
|
expect(logicResults.length).toBe(2);
|
|
@@ -943,7 +1025,16 @@ describe("runBusinessLogics", () => {
|
|
|
943
1025
|
obsoleteStartingFromVersion: "1", // Is obsolete
|
|
944
1026
|
},
|
|
945
1027
|
];
|
|
946
|
-
(0, index_1.initializeEmberFlow)(
|
|
1028
|
+
(0, index_1.initializeEmberFlow)({
|
|
1029
|
+
projectConfig,
|
|
1030
|
+
admin,
|
|
1031
|
+
dbStructure: db_structure_1.dbStructure,
|
|
1032
|
+
Entity: db_structure_1.Entity,
|
|
1033
|
+
securityConfigs: security_1.securityConfigs,
|
|
1034
|
+
validatorConfigs: validators_1.validatorConfigs,
|
|
1035
|
+
logicConfigs: logics,
|
|
1036
|
+
patchLogicConfigs: patch_logics_1.patchLogicConfigs,
|
|
1037
|
+
});
|
|
947
1038
|
const txnGet = { get: jest.fn() };
|
|
948
1039
|
const result = await indexUtils.runBusinessLogics(txnGet, action, appVersion);
|
|
949
1040
|
expect(result.logicResults.length).toBe(0);
|
|
@@ -975,7 +1066,16 @@ describe("runBusinessLogics", () => {
|
|
|
975
1066
|
version: "1",
|
|
976
1067
|
},
|
|
977
1068
|
];
|
|
978
|
-
(0, index_1.initializeEmberFlow)(
|
|
1069
|
+
(0, index_1.initializeEmberFlow)({
|
|
1070
|
+
projectConfig,
|
|
1071
|
+
admin,
|
|
1072
|
+
dbStructure: db_structure_1.dbStructure,
|
|
1073
|
+
Entity: db_structure_1.Entity,
|
|
1074
|
+
securityConfigs: security_1.securityConfigs,
|
|
1075
|
+
validatorConfigs: validators_1.validatorConfigs,
|
|
1076
|
+
logicConfigs: logics,
|
|
1077
|
+
patchLogicConfigs: patch_logics_1.patchLogicConfigs,
|
|
1078
|
+
});
|
|
979
1079
|
const runStatus = await indexUtils.runBusinessLogics(txnGet, action, "2");
|
|
980
1080
|
expect(logicFn1).toHaveBeenCalledWith(txnGet, action, new Map());
|
|
981
1081
|
expect(logicFn2).toHaveBeenCalledWith(txnGet, action, new Map());
|
|
@@ -1045,7 +1145,16 @@ describe("runBusinessLogics", () => {
|
|
|
1045
1145
|
version: "1",
|
|
1046
1146
|
},
|
|
1047
1147
|
];
|
|
1048
|
-
(0, index_1.initializeEmberFlow)(
|
|
1148
|
+
(0, index_1.initializeEmberFlow)({
|
|
1149
|
+
projectConfig,
|
|
1150
|
+
admin,
|
|
1151
|
+
dbStructure: db_structure_1.dbStructure,
|
|
1152
|
+
Entity: db_structure_1.Entity,
|
|
1153
|
+
securityConfigs: security_1.securityConfigs,
|
|
1154
|
+
validatorConfigs: validators_1.validatorConfigs,
|
|
1155
|
+
logicConfigs: logics,
|
|
1156
|
+
patchLogicConfigs: [],
|
|
1157
|
+
});
|
|
1049
1158
|
const newAction = Object.assign(Object.assign({}, action), { modifiedFields: {
|
|
1050
1159
|
shouldRun: true,
|
|
1051
1160
|
} });
|
|
@@ -1152,7 +1261,16 @@ describe("runBusinessLogics", () => {
|
|
|
1152
1261
|
obsoleteAfterVersion: "2.4.9",
|
|
1153
1262
|
},
|
|
1154
1263
|
];
|
|
1155
|
-
(0, index_1.initializeEmberFlow)(
|
|
1264
|
+
(0, index_1.initializeEmberFlow)({
|
|
1265
|
+
projectConfig,
|
|
1266
|
+
admin,
|
|
1267
|
+
dbStructure: db_structure_1.dbStructure,
|
|
1268
|
+
Entity: db_structure_1.Entity,
|
|
1269
|
+
securityConfigs: security_1.securityConfigs,
|
|
1270
|
+
validatorConfigs: validators_1.validatorConfigs,
|
|
1271
|
+
logicConfigs: logics,
|
|
1272
|
+
patchLogicConfigs: [],
|
|
1273
|
+
});
|
|
1156
1274
|
const newAction = Object.assign(Object.assign({}, action), { modifiedFields: {
|
|
1157
1275
|
field1: true,
|
|
1158
1276
|
} });
|
|
@@ -1218,7 +1336,16 @@ describe("runBusinessLogics", () => {
|
|
|
1218
1336
|
version: "1",
|
|
1219
1337
|
},
|
|
1220
1338
|
];
|
|
1221
|
-
(0, index_1.initializeEmberFlow)(
|
|
1339
|
+
(0, index_1.initializeEmberFlow)({
|
|
1340
|
+
projectConfig,
|
|
1341
|
+
admin,
|
|
1342
|
+
dbStructure: db_structure_1.dbStructure,
|
|
1343
|
+
Entity: db_structure_1.Entity,
|
|
1344
|
+
securityConfigs: security_1.securityConfigs,
|
|
1345
|
+
validatorConfigs: validators_1.validatorConfigs,
|
|
1346
|
+
logicConfigs: logics,
|
|
1347
|
+
patchLogicConfigs: [],
|
|
1348
|
+
});
|
|
1222
1349
|
const runStatus = await indexUtils.runBusinessLogics(txnGet, action, "1");
|
|
1223
1350
|
expect(logicFn1).not.toHaveBeenCalled();
|
|
1224
1351
|
expect(logicFn2).not.toHaveBeenCalled();
|
|
@@ -1272,7 +1399,16 @@ describe("runBusinessLogics", () => {
|
|
|
1272
1399
|
version: "1",
|
|
1273
1400
|
},
|
|
1274
1401
|
];
|
|
1275
|
-
(0, index_1.initializeEmberFlow)(
|
|
1402
|
+
(0, index_1.initializeEmberFlow)({
|
|
1403
|
+
projectConfig,
|
|
1404
|
+
admin,
|
|
1405
|
+
dbStructure: db_structure_1.dbStructure,
|
|
1406
|
+
Entity: db_structure_1.Entity,
|
|
1407
|
+
securityConfigs: security_1.securityConfigs,
|
|
1408
|
+
validatorConfigs: validators_1.validatorConfigs,
|
|
1409
|
+
logicConfigs: logics,
|
|
1410
|
+
patchLogicConfigs: [],
|
|
1411
|
+
});
|
|
1276
1412
|
const runStatus = await indexUtils.runBusinessLogics(txnGet, action, "1");
|
|
1277
1413
|
expect(logicFn1).toHaveBeenCalledWith(txnGet, action, expectedSharedMap);
|
|
1278
1414
|
expect(logicFn2).toHaveBeenCalledWith(txnGet, action, expectedSharedMap);
|
|
@@ -1634,46 +1770,6 @@ describe("createMetricExecution", () => {
|
|
|
1634
1770
|
});
|
|
1635
1771
|
});
|
|
1636
1772
|
});
|
|
1637
|
-
describe("cleanMetricExecutions", () => {
|
|
1638
|
-
let colGetMock;
|
|
1639
|
-
let deleteCollectionSpy;
|
|
1640
|
-
beforeEach(() => {
|
|
1641
|
-
colGetMock = jest.fn().mockResolvedValue({
|
|
1642
|
-
docs: [
|
|
1643
|
-
{
|
|
1644
|
-
ref: {
|
|
1645
|
-
collection: jest.fn().mockReturnValue({
|
|
1646
|
-
where: jest.fn().mockReturnValue({}),
|
|
1647
|
-
}),
|
|
1648
|
-
},
|
|
1649
|
-
},
|
|
1650
|
-
],
|
|
1651
|
-
});
|
|
1652
|
-
jest.spyOn(admin.firestore(), "collection").mockReturnValue({
|
|
1653
|
-
get: colGetMock,
|
|
1654
|
-
});
|
|
1655
|
-
deleteCollectionSpy = jest.spyOn(misc, "deleteCollection")
|
|
1656
|
-
.mockImplementation(async (query, callback) => {
|
|
1657
|
-
if (callback) {
|
|
1658
|
-
await callback({ size: 1 });
|
|
1659
|
-
}
|
|
1660
|
-
return Promise.resolve();
|
|
1661
|
-
});
|
|
1662
|
-
});
|
|
1663
|
-
afterEach(() => {
|
|
1664
|
-
jest.restoreAllMocks();
|
|
1665
|
-
});
|
|
1666
|
-
it("should clean metric executions", async () => {
|
|
1667
|
-
jest.spyOn(console, "info").mockImplementation();
|
|
1668
|
-
await (0, index_utils_1.cleanMetricExecutions)({});
|
|
1669
|
-
expect(console.info).toHaveBeenCalledWith("Running cleanMetricExecutions");
|
|
1670
|
-
expect(admin.firestore().collection).toHaveBeenCalledTimes(1);
|
|
1671
|
-
expect(admin.firestore().collection).toHaveBeenCalledWith("@metrics");
|
|
1672
|
-
expect(colGetMock).toHaveBeenCalledTimes(1);
|
|
1673
|
-
expect(deleteCollectionSpy).toHaveBeenCalled();
|
|
1674
|
-
expect(console.info).toHaveBeenCalledWith("Cleaned 1 logic metric executions");
|
|
1675
|
-
});
|
|
1676
|
-
});
|
|
1677
1773
|
describe("createMetricComputation", () => {
|
|
1678
1774
|
let colGetMock;
|
|
1679
1775
|
let setMock;
|
|
@@ -1786,44 +1882,209 @@ describe("createMetricComputation", () => {
|
|
|
1786
1882
|
});
|
|
1787
1883
|
});
|
|
1788
1884
|
});
|
|
1789
|
-
describe("
|
|
1885
|
+
describe("patchGroupDocs", () => {
|
|
1886
|
+
const collectionPath = "/users/user1/feeds";
|
|
1887
|
+
let docGetMock;
|
|
1888
|
+
let docSetMock;
|
|
1790
1889
|
let colGetMock;
|
|
1791
|
-
let
|
|
1890
|
+
let limitMock;
|
|
1891
|
+
let startAfterMock;
|
|
1892
|
+
let orderByMock;
|
|
1893
|
+
let batchUpdateMock;
|
|
1894
|
+
let batchCommitMock;
|
|
1895
|
+
let queueGroupPatchSpy;
|
|
1896
|
+
let runPatchLogicsSpy;
|
|
1897
|
+
function makeDoc(id, data = {}) {
|
|
1898
|
+
return {
|
|
1899
|
+
id,
|
|
1900
|
+
data: () => data,
|
|
1901
|
+
ref: {
|
|
1902
|
+
path: `${collectionPath.slice(1)}/${id}`,
|
|
1903
|
+
update: jest.fn(),
|
|
1904
|
+
},
|
|
1905
|
+
};
|
|
1906
|
+
}
|
|
1792
1907
|
beforeEach(() => {
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
],
|
|
1908
|
+
(0, index_1.initializeEmberFlow)({
|
|
1909
|
+
projectConfig,
|
|
1910
|
+
admin,
|
|
1911
|
+
dbStructure: db_structure_1.dbStructure,
|
|
1912
|
+
Entity: db_structure_1.Entity,
|
|
1913
|
+
securityConfigs: security_1.securityConfigs,
|
|
1914
|
+
validatorConfigs: validators_1.validatorConfigs,
|
|
1915
|
+
logicConfigs: [],
|
|
1916
|
+
patchLogicConfigs: [],
|
|
1803
1917
|
});
|
|
1918
|
+
docGetMock = jest.fn().mockResolvedValue({ exists: false, data: () => undefined });
|
|
1919
|
+
docSetMock = jest.fn().mockResolvedValue({});
|
|
1920
|
+
jest.spyOn(admin.firestore(), "doc").mockReturnValue({
|
|
1921
|
+
get: docGetMock,
|
|
1922
|
+
set: docSetMock,
|
|
1923
|
+
});
|
|
1924
|
+
colGetMock = jest.fn();
|
|
1925
|
+
limitMock = jest.fn().mockReturnValue({ get: colGetMock });
|
|
1926
|
+
startAfterMock = jest.fn().mockReturnValue({ limit: limitMock });
|
|
1927
|
+
orderByMock = jest.fn().mockReturnValue({ limit: limitMock, startAfter: startAfterMock });
|
|
1804
1928
|
jest.spyOn(admin.firestore(), "collection").mockReturnValue({
|
|
1805
|
-
|
|
1929
|
+
orderBy: orderByMock,
|
|
1806
1930
|
});
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
return Promise.resolve();
|
|
1931
|
+
batchUpdateMock = jest.fn();
|
|
1932
|
+
batchCommitMock = jest.fn().mockResolvedValue({});
|
|
1933
|
+
jest.spyOn(admin.firestore(), "batch").mockReturnValue({
|
|
1934
|
+
update: batchUpdateMock,
|
|
1935
|
+
commit: batchCommitMock,
|
|
1813
1936
|
});
|
|
1937
|
+
queueGroupPatchSpy = jest.spyOn(distribution, "queueGroupPatch").mockResolvedValue();
|
|
1938
|
+
runPatchLogicsSpy = jest.spyOn(patchLogics, "runPatchLogics").mockResolvedValue();
|
|
1814
1939
|
});
|
|
1815
1940
|
afterEach(() => {
|
|
1816
1941
|
jest.restoreAllMocks();
|
|
1817
1942
|
});
|
|
1818
|
-
it("
|
|
1819
|
-
|
|
1820
|
-
await (
|
|
1821
|
-
expect(
|
|
1822
|
-
expect(
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1943
|
+
it("marks the patch as completed and does not reschedule when there are no more docs", async () => {
|
|
1944
|
+
colGetMock.mockResolvedValue({ empty: true, docs: [] });
|
|
1945
|
+
await indexUtils.patchGroupDocs({ collectionPath, patchType: "back-fill", backFillPatchName: "ancestor-ids" });
|
|
1946
|
+
expect(docSetMock).toHaveBeenCalledWith(expect.objectContaining({ status: "completed" }), { merge: true });
|
|
1947
|
+
expect(queueGroupPatchSpy).not.toHaveBeenCalled();
|
|
1948
|
+
});
|
|
1949
|
+
it("skips processing entirely when the status is already completed", async () => {
|
|
1950
|
+
docGetMock.mockResolvedValue({ exists: true, data: () => ({ status: "completed" }) });
|
|
1951
|
+
await indexUtils.patchGroupDocs({ collectionPath, patchType: "back-fill", backFillPatchName: "ancestor-ids" });
|
|
1952
|
+
expect(admin.firestore().collection).not.toHaveBeenCalled();
|
|
1953
|
+
expect(queueGroupPatchSpy).not.toHaveBeenCalled();
|
|
1954
|
+
});
|
|
1955
|
+
it("runs the built-in ancestor-ids back-fill unchanged: bulk single-batch commit, only undefined keys patched", async () => {
|
|
1956
|
+
const doc1 = makeDoc("feed1");
|
|
1957
|
+
colGetMock.mockResolvedValue({ empty: false, docs: [doc1] });
|
|
1958
|
+
await indexUtils.patchGroupDocs({ collectionPath, patchType: "back-fill", backFillPatchName: "ancestor-ids" });
|
|
1959
|
+
expect(admin.firestore().batch).toHaveBeenCalledTimes(1);
|
|
1960
|
+
expect(batchUpdateMock).toHaveBeenCalledWith(doc1.ref, { "@entity": "feed", "@user": "user1" });
|
|
1961
|
+
expect(batchCommitMock).toHaveBeenCalledTimes(1);
|
|
1962
|
+
expect(docSetMock).toHaveBeenCalledWith(expect.objectContaining({ status: "running", count: 1, lastPatchedId: "feed1" }), { merge: true });
|
|
1963
|
+
expect(queueGroupPatchSpy).toHaveBeenCalledWith({
|
|
1964
|
+
path: collectionPath,
|
|
1965
|
+
patchType: "back-fill",
|
|
1966
|
+
backFillPatchName: "ancestor-ids",
|
|
1967
|
+
appVersion: undefined,
|
|
1968
|
+
lastPatchedId: "feed1",
|
|
1969
|
+
});
|
|
1970
|
+
});
|
|
1971
|
+
it("does not patch keys that are already defined on the document", async () => {
|
|
1972
|
+
const doc1 = makeDoc("feed1", { "@entity": "feed", "@user": "user1" });
|
|
1973
|
+
colGetMock.mockResolvedValue({ empty: false, docs: [doc1] });
|
|
1974
|
+
await indexUtils.patchGroupDocs({ collectionPath, patchType: "back-fill", backFillPatchName: "ancestor-ids" });
|
|
1975
|
+
expect(batchUpdateMock).not.toHaveBeenCalled();
|
|
1976
|
+
expect(batchCommitMock).not.toHaveBeenCalled();
|
|
1977
|
+
});
|
|
1978
|
+
it("dispatches to a custom back-fill config registered via initializeEmberFlow options", async () => {
|
|
1979
|
+
const customPatchFn = jest.fn().mockResolvedValue(undefined);
|
|
1980
|
+
(0, index_1.initializeEmberFlow)({
|
|
1981
|
+
projectConfig,
|
|
1982
|
+
admin,
|
|
1983
|
+
dbStructure: db_structure_1.dbStructure,
|
|
1984
|
+
Entity: db_structure_1.Entity,
|
|
1985
|
+
securityConfigs: security_1.securityConfigs,
|
|
1986
|
+
validatorConfigs: validators_1.validatorConfigs,
|
|
1987
|
+
logicConfigs: [],
|
|
1988
|
+
patchLogicConfigs: [],
|
|
1989
|
+
backFillPatchConfigs: [{ name: "custom-back-fill", patchFn: customPatchFn }],
|
|
1990
|
+
});
|
|
1991
|
+
const doc1 = makeDoc("feed1");
|
|
1992
|
+
colGetMock.mockResolvedValue({ empty: false, docs: [doc1] });
|
|
1993
|
+
await indexUtils.patchGroupDocs({ collectionPath, patchType: "back-fill", backFillPatchName: "custom-back-fill" });
|
|
1994
|
+
expect(customPatchFn).toHaveBeenCalledWith(collectionPath, [doc1]);
|
|
1995
|
+
expect(queueGroupPatchSpy).toHaveBeenCalledWith(expect.objectContaining({ backFillPatchName: "custom-back-fill" }));
|
|
1996
|
+
});
|
|
1997
|
+
it("dispatches to runPatchLogics per doc when patchType is patch-logics", async () => {
|
|
1998
|
+
const doc1 = makeDoc("feed1");
|
|
1999
|
+
const doc2 = makeDoc("feed2");
|
|
2000
|
+
colGetMock.mockResolvedValue({ empty: false, docs: [doc1, doc2] });
|
|
2001
|
+
await indexUtils.patchGroupDocs({ collectionPath, patchType: "patch-logics", appVersion: "1.2.3" });
|
|
2002
|
+
expect(runPatchLogicsSpy).toHaveBeenCalledTimes(2);
|
|
2003
|
+
expect(runPatchLogicsSpy).toHaveBeenNthCalledWith(1, "1.2.3", `/${doc1.ref.path}`);
|
|
2004
|
+
expect(runPatchLogicsSpy).toHaveBeenNthCalledWith(2, "1.2.3", `/${doc2.ref.path}`);
|
|
2005
|
+
expect(queueGroupPatchSpy).toHaveBeenCalledWith(expect.objectContaining({
|
|
2006
|
+
patchType: "patch-logics",
|
|
2007
|
+
appVersion: "1.2.3",
|
|
2008
|
+
lastPatchedId: "feed2",
|
|
2009
|
+
}));
|
|
2010
|
+
});
|
|
2011
|
+
it("marks the run as error (no silent default) when appVersion is missing for patch-logics", async () => {
|
|
2012
|
+
jest.spyOn(console, "error").mockImplementation();
|
|
2013
|
+
const doc1 = makeDoc("feed1");
|
|
2014
|
+
colGetMock.mockResolvedValue({ empty: false, docs: [doc1] });
|
|
2015
|
+
await expect(indexUtils.patchGroupDocs({ collectionPath, patchType: "patch-logics" })).rejects.toThrow();
|
|
2016
|
+
expect(docSetMock).toHaveBeenCalledWith(expect.objectContaining({ status: "error" }), { merge: true });
|
|
2017
|
+
expect(runPatchLogicsSpy).not.toHaveBeenCalled();
|
|
2018
|
+
expect(queueGroupPatchSpy).not.toHaveBeenCalled();
|
|
2019
|
+
});
|
|
2020
|
+
it("marks the run as error (no silent default) when the back-fill name is unresolved", async () => {
|
|
2021
|
+
jest.spyOn(console, "error").mockImplementation();
|
|
2022
|
+
const doc1 = makeDoc("feed1");
|
|
2023
|
+
colGetMock.mockResolvedValue({ empty: false, docs: [doc1] });
|
|
2024
|
+
await expect(indexUtils.patchGroupDocs({
|
|
2025
|
+
collectionPath,
|
|
2026
|
+
patchType: "back-fill",
|
|
2027
|
+
backFillPatchName: "does-not-exist",
|
|
2028
|
+
})).rejects.toThrow();
|
|
2029
|
+
expect(docSetMock).toHaveBeenCalledWith(expect.objectContaining({ status: "error", error: expect.stringContaining("does-not-exist") }), { merge: true });
|
|
2030
|
+
expect(queueGroupPatchSpy).not.toHaveBeenCalled();
|
|
2031
|
+
});
|
|
2032
|
+
it("marks the run as error (no silent default) for an unknown patchType", async () => {
|
|
2033
|
+
jest.spyOn(console, "error").mockImplementation();
|
|
2034
|
+
const doc1 = makeDoc("feed1");
|
|
2035
|
+
colGetMock.mockResolvedValue({ empty: false, docs: [doc1] });
|
|
2036
|
+
await expect(indexUtils.patchGroupDocs({
|
|
2037
|
+
collectionPath,
|
|
2038
|
+
patchType: "unknown-type",
|
|
2039
|
+
})).rejects.toThrow();
|
|
2040
|
+
expect(docSetMock).toHaveBeenCalledWith(expect.objectContaining({ status: "error" }), { merge: true });
|
|
2041
|
+
expect(queueGroupPatchSpy).not.toHaveBeenCalled();
|
|
2042
|
+
});
|
|
2043
|
+
});
|
|
2044
|
+
describe("ancestorIdsPatchConfig", () => {
|
|
2045
|
+
it("is auto-registered as a built-in BackFillPatchConfig named \"ancestor-ids\"", () => {
|
|
2046
|
+
(0, index_1.initializeEmberFlow)({
|
|
2047
|
+
projectConfig,
|
|
2048
|
+
admin,
|
|
2049
|
+
dbStructure: db_structure_1.dbStructure,
|
|
2050
|
+
Entity: db_structure_1.Entity,
|
|
2051
|
+
securityConfigs: security_1.securityConfigs,
|
|
2052
|
+
validatorConfigs: validators_1.validatorConfigs,
|
|
2053
|
+
logicConfigs: [],
|
|
2054
|
+
patchLogicConfigs: [],
|
|
2055
|
+
});
|
|
2056
|
+
const registered = indexUtils._mockable.getOneTimePatchConfigs()
|
|
2057
|
+
.find((config) => config.name === "ancestor-ids");
|
|
2058
|
+
expect(registered).toBe(indexUtils.ancestorIdsPatchConfig);
|
|
2059
|
+
});
|
|
2060
|
+
it("throws when a custom BackFillPatchConfig reuses the built-in \"ancestor-ids\" name", () => {
|
|
2061
|
+
expect(() => (0, index_1.initializeEmberFlow)({
|
|
2062
|
+
projectConfig,
|
|
2063
|
+
admin,
|
|
2064
|
+
dbStructure: db_structure_1.dbStructure,
|
|
2065
|
+
Entity: db_structure_1.Entity,
|
|
2066
|
+
securityConfigs: security_1.securityConfigs,
|
|
2067
|
+
validatorConfigs: validators_1.validatorConfigs,
|
|
2068
|
+
logicConfigs: [],
|
|
2069
|
+
patchLogicConfigs: [],
|
|
2070
|
+
backFillPatchConfigs: [{ name: "ancestor-ids", patchFn: jest.fn() }],
|
|
2071
|
+
})).toThrow();
|
|
2072
|
+
});
|
|
2073
|
+
it("throws when two custom BackFillPatchConfigs share the same name", () => {
|
|
2074
|
+
expect(() => (0, index_1.initializeEmberFlow)({
|
|
2075
|
+
projectConfig,
|
|
2076
|
+
admin,
|
|
2077
|
+
dbStructure: db_structure_1.dbStructure,
|
|
2078
|
+
Entity: db_structure_1.Entity,
|
|
2079
|
+
securityConfigs: security_1.securityConfigs,
|
|
2080
|
+
validatorConfigs: validators_1.validatorConfigs,
|
|
2081
|
+
logicConfigs: [],
|
|
2082
|
+
patchLogicConfigs: [],
|
|
2083
|
+
backFillPatchConfigs: [
|
|
2084
|
+
{ name: "dup", patchFn: jest.fn() },
|
|
2085
|
+
{ name: "dup", patchFn: jest.fn() },
|
|
2086
|
+
],
|
|
2087
|
+
})).toThrow();
|
|
1827
2088
|
});
|
|
1828
2089
|
});
|
|
1829
2090
|
//# sourceMappingURL=index-utils.test.js.map
|