emberflow 1.5.1 → 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 +70 -75
- package/lib/index-utils.js.map +1 -1
- package/lib/index.d.ts +22 -6
- package/lib/index.js +43 -34
- 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 +345 -85
- package/lib/tests/index-utils.test.js.map +1 -1
- package/lib/tests/index.test.js +33 -5
- 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
|
|
@@ -489,7 +489,16 @@ describe("distribute", () => {
|
|
|
489
489
|
dstPath: "/users/test-user-id/documents/test-doc-id",
|
|
490
490
|
}],
|
|
491
491
|
]]);
|
|
492
|
-
(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
|
+
});
|
|
493
502
|
await indexUtils.distributeFnNonTransactional(userDocsByDstPath, appVersion);
|
|
494
503
|
expect(admin.firestore().doc).toHaveBeenCalledTimes(1);
|
|
495
504
|
expect(admin.firestore().doc).toHaveBeenCalledWith("/users/test-user-id/documents/test-doc-id");
|
|
@@ -526,7 +535,16 @@ describe("distribute", () => {
|
|
|
526
535
|
describe("distributeLater", () => {
|
|
527
536
|
let queueForDistributionLaterSpy;
|
|
528
537
|
beforeEach(() => {
|
|
529
|
-
(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
|
+
});
|
|
530
548
|
queueForDistributionLaterSpy = jest.spyOn(distribution, "queueForDistributionLater").mockResolvedValue();
|
|
531
549
|
});
|
|
532
550
|
afterEach(() => {
|
|
@@ -558,7 +576,16 @@ describe("distributeLater", () => {
|
|
|
558
576
|
});
|
|
559
577
|
describe("validateForm", () => {
|
|
560
578
|
beforeEach(() => {
|
|
561
|
-
(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
|
+
});
|
|
562
589
|
});
|
|
563
590
|
const targetVersion = "2.5";
|
|
564
591
|
const entity = "user";
|
|
@@ -595,7 +622,16 @@ describe("validateForm", () => {
|
|
|
595
622
|
version: "02.04.06",
|
|
596
623
|
},
|
|
597
624
|
];
|
|
598
|
-
(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
|
+
});
|
|
599
635
|
const document = {
|
|
600
636
|
name: "John Doe",
|
|
601
637
|
email: "johndoe@example.com",
|
|
@@ -643,7 +679,16 @@ describe("validateForm", () => {
|
|
|
643
679
|
version: "2.0.0",
|
|
644
680
|
},
|
|
645
681
|
];
|
|
646
|
-
(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
|
+
});
|
|
647
692
|
const document = {
|
|
648
693
|
name: "John Doe",
|
|
649
694
|
};
|
|
@@ -691,7 +736,16 @@ describe("getSecurityFn", () => {
|
|
|
691
736
|
version: "02.04.06",
|
|
692
737
|
},
|
|
693
738
|
];
|
|
694
|
-
(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
|
+
});
|
|
695
749
|
const result = await indexUtils.getSecurityFn(entity, targetVersion);
|
|
696
750
|
expect(result).toEqual(userSecurityFnV2);
|
|
697
751
|
});
|
|
@@ -710,7 +764,16 @@ describe("getSecurityFn", () => {
|
|
|
710
764
|
version: "2.0.0",
|
|
711
765
|
},
|
|
712
766
|
];
|
|
713
|
-
(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
|
+
});
|
|
714
777
|
const entity = "user";
|
|
715
778
|
// targetVersion is 1.0.0, even if app is 2.0.0
|
|
716
779
|
const result = await indexUtils.getSecurityFn(entity, "1.0.0");
|
|
@@ -769,7 +832,16 @@ describe("delayFormSubmissionAndCheckIfCancelled", () => {
|
|
|
769
832
|
});
|
|
770
833
|
});
|
|
771
834
|
describe("groupDocsByTargetDocPath", () => {
|
|
772
|
-
(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
|
+
});
|
|
773
845
|
const docsByDstPath = new Map([
|
|
774
846
|
["users/user123/document1", [{ action: "merge", priority: "normal", dstPath: "users/user123/document1", doc: { field1: "value1", field2: "value2" } }]],
|
|
775
847
|
["users/user123/document1/threads/thread1", [{ action: "merge", priority: "normal", dstPath: "users/user123/document1/threads/thread1", doc: { field3: "value3", field6: "value6" } }]],
|
|
@@ -812,7 +884,7 @@ describe("runBusinessLogics", () => {
|
|
|
812
884
|
docId: "document123",
|
|
813
885
|
formId: "form123",
|
|
814
886
|
docPath: "users/user123",
|
|
815
|
-
entity
|
|
887
|
+
entity,
|
|
816
888
|
},
|
|
817
889
|
actionType,
|
|
818
890
|
document: {
|
|
@@ -920,7 +992,16 @@ describe("runBusinessLogics", () => {
|
|
|
920
992
|
obsoleteStartingFromVersion: "2", // Obsolete after appVersion
|
|
921
993
|
},
|
|
922
994
|
];
|
|
923
|
-
(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
|
+
});
|
|
924
1005
|
const txnGet = { get: jest.fn() };
|
|
925
1006
|
const { logicResults } = await indexUtils.runBusinessLogics(txnGet, action, appVersion);
|
|
926
1007
|
expect(logicResults.length).toBe(2);
|
|
@@ -944,7 +1025,16 @@ describe("runBusinessLogics", () => {
|
|
|
944
1025
|
obsoleteStartingFromVersion: "1", // Is obsolete
|
|
945
1026
|
},
|
|
946
1027
|
];
|
|
947
|
-
(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
|
+
});
|
|
948
1038
|
const txnGet = { get: jest.fn() };
|
|
949
1039
|
const result = await indexUtils.runBusinessLogics(txnGet, action, appVersion);
|
|
950
1040
|
expect(result.logicResults.length).toBe(0);
|
|
@@ -976,7 +1066,16 @@ describe("runBusinessLogics", () => {
|
|
|
976
1066
|
version: "1",
|
|
977
1067
|
},
|
|
978
1068
|
];
|
|
979
|
-
(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
|
+
});
|
|
980
1079
|
const runStatus = await indexUtils.runBusinessLogics(txnGet, action, "2");
|
|
981
1080
|
expect(logicFn1).toHaveBeenCalledWith(txnGet, action, new Map());
|
|
982
1081
|
expect(logicFn2).toHaveBeenCalledWith(txnGet, action, new Map());
|
|
@@ -1046,7 +1145,16 @@ describe("runBusinessLogics", () => {
|
|
|
1046
1145
|
version: "1",
|
|
1047
1146
|
},
|
|
1048
1147
|
];
|
|
1049
|
-
(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
|
+
});
|
|
1050
1158
|
const newAction = Object.assign(Object.assign({}, action), { modifiedFields: {
|
|
1051
1159
|
shouldRun: true,
|
|
1052
1160
|
} });
|
|
@@ -1153,7 +1261,16 @@ describe("runBusinessLogics", () => {
|
|
|
1153
1261
|
obsoleteAfterVersion: "2.4.9",
|
|
1154
1262
|
},
|
|
1155
1263
|
];
|
|
1156
|
-
(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
|
+
});
|
|
1157
1274
|
const newAction = Object.assign(Object.assign({}, action), { modifiedFields: {
|
|
1158
1275
|
field1: true,
|
|
1159
1276
|
} });
|
|
@@ -1219,7 +1336,16 @@ describe("runBusinessLogics", () => {
|
|
|
1219
1336
|
version: "1",
|
|
1220
1337
|
},
|
|
1221
1338
|
];
|
|
1222
|
-
(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
|
+
});
|
|
1223
1349
|
const runStatus = await indexUtils.runBusinessLogics(txnGet, action, "1");
|
|
1224
1350
|
expect(logicFn1).not.toHaveBeenCalled();
|
|
1225
1351
|
expect(logicFn2).not.toHaveBeenCalled();
|
|
@@ -1273,7 +1399,16 @@ describe("runBusinessLogics", () => {
|
|
|
1273
1399
|
version: "1",
|
|
1274
1400
|
},
|
|
1275
1401
|
];
|
|
1276
|
-
(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
|
+
});
|
|
1277
1412
|
const runStatus = await indexUtils.runBusinessLogics(txnGet, action, "1");
|
|
1278
1413
|
expect(logicFn1).toHaveBeenCalledWith(txnGet, action, expectedSharedMap);
|
|
1279
1414
|
expect(logicFn2).toHaveBeenCalledWith(txnGet, action, expectedSharedMap);
|
|
@@ -1635,46 +1770,6 @@ describe("createMetricExecution", () => {
|
|
|
1635
1770
|
});
|
|
1636
1771
|
});
|
|
1637
1772
|
});
|
|
1638
|
-
describe("cleanMetricExecutions", () => {
|
|
1639
|
-
let colGetMock;
|
|
1640
|
-
let deleteCollectionSpy;
|
|
1641
|
-
beforeEach(() => {
|
|
1642
|
-
colGetMock = jest.fn().mockResolvedValue({
|
|
1643
|
-
docs: [
|
|
1644
|
-
{
|
|
1645
|
-
ref: {
|
|
1646
|
-
collection: jest.fn().mockReturnValue({
|
|
1647
|
-
where: jest.fn().mockReturnValue({}),
|
|
1648
|
-
}),
|
|
1649
|
-
},
|
|
1650
|
-
},
|
|
1651
|
-
],
|
|
1652
|
-
});
|
|
1653
|
-
jest.spyOn(admin.firestore(), "collection").mockReturnValue({
|
|
1654
|
-
get: colGetMock,
|
|
1655
|
-
});
|
|
1656
|
-
deleteCollectionSpy = jest.spyOn(misc, "deleteCollection")
|
|
1657
|
-
.mockImplementation(async (query, callback) => {
|
|
1658
|
-
if (callback) {
|
|
1659
|
-
await callback({ size: 1 });
|
|
1660
|
-
}
|
|
1661
|
-
return Promise.resolve();
|
|
1662
|
-
});
|
|
1663
|
-
});
|
|
1664
|
-
afterEach(() => {
|
|
1665
|
-
jest.restoreAllMocks();
|
|
1666
|
-
});
|
|
1667
|
-
it("should clean metric executions", async () => {
|
|
1668
|
-
jest.spyOn(console, "info").mockImplementation();
|
|
1669
|
-
await (0, index_utils_1.cleanMetricExecutions)({});
|
|
1670
|
-
expect(console.info).toHaveBeenCalledWith("Running cleanMetricExecutions");
|
|
1671
|
-
expect(admin.firestore().collection).toHaveBeenCalledTimes(1);
|
|
1672
|
-
expect(admin.firestore().collection).toHaveBeenCalledWith("@metrics");
|
|
1673
|
-
expect(colGetMock).toHaveBeenCalledTimes(1);
|
|
1674
|
-
expect(deleteCollectionSpy).toHaveBeenCalled();
|
|
1675
|
-
expect(console.info).toHaveBeenCalledWith("Cleaned 1 logic metric executions");
|
|
1676
|
-
});
|
|
1677
|
-
});
|
|
1678
1773
|
describe("createMetricComputation", () => {
|
|
1679
1774
|
let colGetMock;
|
|
1680
1775
|
let setMock;
|
|
@@ -1787,44 +1882,209 @@ describe("createMetricComputation", () => {
|
|
|
1787
1882
|
});
|
|
1788
1883
|
});
|
|
1789
1884
|
});
|
|
1790
|
-
describe("
|
|
1885
|
+
describe("patchGroupDocs", () => {
|
|
1886
|
+
const collectionPath = "/users/user1/feeds";
|
|
1887
|
+
let docGetMock;
|
|
1888
|
+
let docSetMock;
|
|
1791
1889
|
let colGetMock;
|
|
1792
|
-
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
|
+
}
|
|
1793
1907
|
beforeEach(() => {
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
],
|
|
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: [],
|
|
1804
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 });
|
|
1805
1928
|
jest.spyOn(admin.firestore(), "collection").mockReturnValue({
|
|
1806
|
-
|
|
1929
|
+
orderBy: orderByMock,
|
|
1807
1930
|
});
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
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,
|
|
1814
1936
|
});
|
|
1937
|
+
queueGroupPatchSpy = jest.spyOn(distribution, "queueGroupPatch").mockResolvedValue();
|
|
1938
|
+
runPatchLogicsSpy = jest.spyOn(patchLogics, "runPatchLogics").mockResolvedValue();
|
|
1815
1939
|
});
|
|
1816
1940
|
afterEach(() => {
|
|
1817
1941
|
jest.restoreAllMocks();
|
|
1818
1942
|
});
|
|
1819
|
-
it("
|
|
1820
|
-
|
|
1821
|
-
await (
|
|
1822
|
-
expect(
|
|
1823
|
-
expect(
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
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();
|
|
1828
2088
|
});
|
|
1829
2089
|
});
|
|
1830
2090
|
//# sourceMappingURL=index-utils.test.js.map
|