mongodb-dynamic-api 3.0.1 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -0
- package/package.json +1 -1
- package/src/dynamic-api.module.d.ts +1 -1
- package/src/dynamic-api.module.js +11 -3
- package/src/dynamic-api.module.js.map +1 -1
- package/src/gateways/base.gateway.d.ts +2 -1
- package/src/gateways/base.gateway.js +17 -0
- package/src/gateways/base.gateway.js.map +1 -1
- package/src/gateways/dynamic-api-broadcast.gateway.d.ts +11 -0
- package/src/gateways/dynamic-api-broadcast.gateway.js +41 -0
- package/src/gateways/dynamic-api-broadcast.gateway.js.map +1 -0
- package/src/gateways/index.d.ts +1 -0
- package/src/gateways/index.js +1 -0
- package/src/gateways/index.js.map +1 -1
- package/src/helpers/mixin-data.helper.d.ts +1 -1
- package/src/helpers/mixin-data.helper.js +2 -1
- package/src/helpers/mixin-data.helper.js.map +1 -1
- package/src/interfaces/dynamic-api-ability.interface.d.ts +2 -1
- package/src/interfaces/dynamic-api-broadcast-config.interface.d.ts +6 -0
- package/src/interfaces/dynamic-api-broadcast-config.interface.js +3 -0
- package/src/interfaces/dynamic-api-broadcast-config.interface.js.map +1 -0
- package/src/interfaces/dynamic-api-global-state.interface.d.ts +1 -0
- package/src/interfaces/dynamic-api-options.interface.d.ts +2 -0
- package/src/interfaces/dynamic-api-options.interface.js.map +1 -1
- package/src/interfaces/dynamic-api-route-config.interface.d.ts +2 -0
- package/src/interfaces/index.d.ts +1 -0
- package/src/interfaces/index.js +1 -0
- package/src/interfaces/index.js.map +1 -1
- package/src/modules/auth/auth.helper.d.ts +7 -5
- package/src/modules/auth/auth.helper.js +10 -9
- package/src/modules/auth/auth.helper.js.map +1 -1
- package/src/modules/auth/auth.module.js +9 -5
- package/src/modules/auth/auth.module.js.map +1 -1
- package/src/modules/auth/interfaces/auth-options.interface.d.ts +6 -1
- package/src/modules/auth/mixins/auth-controller.mixin.d.ts +2 -2
- package/src/modules/auth/mixins/auth-controller.mixin.js +2 -1
- package/src/modules/auth/mixins/auth-controller.mixin.js.map +1 -1
- package/src/modules/auth/mixins/auth-gateway.mixin.d.ts +2 -2
- package/src/modules/auth/mixins/auth-gateway.mixin.js +2 -1
- package/src/modules/auth/mixins/auth-gateway.mixin.js.map +1 -1
- package/src/modules/auth/services/base-auth.service.d.ts +1 -0
- package/src/modules/auth/services/base-auth.service.js +4 -0
- package/src/modules/auth/services/base-auth.service.js.map +1 -1
- package/src/routes/aggregate/aggregate.module.js +1 -1
- package/src/routes/aggregate/aggregate.module.js.map +1 -1
- package/src/routes/create-many/create-many-controller.interface.d.ts +2 -1
- package/src/routes/create-many/create-many-controller.mixin.d.ts +1 -1
- package/src/routes/create-many/create-many-controller.mixin.js +14 -6
- package/src/routes/create-many/create-many-controller.mixin.js.map +1 -1
- package/src/routes/create-many/create-many-gateway.mixin.d.ts +1 -1
- package/src/routes/create-many/create-many-gateway.mixin.js +5 -3
- package/src/routes/create-many/create-many-gateway.mixin.js.map +1 -1
- package/src/routes/create-many/create-many.helper.js +7 -3
- package/src/routes/create-many/create-many.helper.js.map +1 -1
- package/src/routes/create-many/create-many.module.js +4 -1
- package/src/routes/create-many/create-many.module.js.map +1 -1
- package/src/routes/create-one/create-one-controller.interface.d.ts +2 -1
- package/src/routes/create-one/create-one-controller.mixin.d.ts +1 -1
- package/src/routes/create-one/create-one-controller.mixin.js +14 -6
- package/src/routes/create-one/create-one-controller.mixin.js.map +1 -1
- package/src/routes/create-one/create-one-gateway.mixin.d.ts +1 -1
- package/src/routes/create-one/create-one-gateway.mixin.js +5 -3
- package/src/routes/create-one/create-one-gateway.mixin.js.map +1 -1
- package/src/routes/create-one/create-one.helper.js +7 -3
- package/src/routes/create-one/create-one.helper.js.map +1 -1
- package/src/routes/create-one/create-one.module.js +4 -1
- package/src/routes/create-one/create-one.module.js.map +1 -1
- package/src/routes/delete-many/delete-many-controller.interface.d.ts +2 -1
- package/src/routes/delete-many/delete-many-controller.mixin.d.ts +1 -1
- package/src/routes/delete-many/delete-many-controller.mixin.js +14 -6
- package/src/routes/delete-many/delete-many-controller.mixin.js.map +1 -1
- package/src/routes/delete-many/delete-many-gateway.mixin.d.ts +1 -1
- package/src/routes/delete-many/delete-many-gateway.mixin.js +5 -3
- package/src/routes/delete-many/delete-many-gateway.mixin.js.map +1 -1
- package/src/routes/delete-many/delete-many.helper.js +7 -3
- package/src/routes/delete-many/delete-many.helper.js.map +1 -1
- package/src/routes/delete-many/delete-many.module.js +4 -1
- package/src/routes/delete-many/delete-many.module.js.map +1 -1
- package/src/routes/delete-one/delete-one-controller.interface.d.ts +2 -1
- package/src/routes/delete-one/delete-one-controller.mixin.d.ts +1 -1
- package/src/routes/delete-one/delete-one-controller.mixin.js +14 -6
- package/src/routes/delete-one/delete-one-controller.mixin.js.map +1 -1
- package/src/routes/delete-one/delete-one-gateway.mixin.d.ts +1 -1
- package/src/routes/delete-one/delete-one-gateway.mixin.js +5 -3
- package/src/routes/delete-one/delete-one-gateway.mixin.js.map +1 -1
- package/src/routes/delete-one/delete-one.helper.js +7 -3
- package/src/routes/delete-one/delete-one.helper.js.map +1 -1
- package/src/routes/delete-one/delete-one.module.js +4 -1
- package/src/routes/delete-one/delete-one.module.js.map +1 -1
- package/src/routes/duplicate-many/duplicate-many-controller.interface.d.ts +2 -1
- package/src/routes/duplicate-many/duplicate-many-controller.mixin.d.ts +1 -1
- package/src/routes/duplicate-many/duplicate-many-controller.mixin.js +14 -6
- package/src/routes/duplicate-many/duplicate-many-controller.mixin.js.map +1 -1
- package/src/routes/duplicate-many/duplicate-many-gateway.mixin.d.ts +1 -1
- package/src/routes/duplicate-many/duplicate-many-gateway.mixin.js +5 -3
- package/src/routes/duplicate-many/duplicate-many-gateway.mixin.js.map +1 -1
- package/src/routes/duplicate-many/duplicate-many.helper.js +7 -3
- package/src/routes/duplicate-many/duplicate-many.helper.js.map +1 -1
- package/src/routes/duplicate-many/duplicate-many.module.js +4 -1
- package/src/routes/duplicate-many/duplicate-many.module.js.map +1 -1
- package/src/routes/duplicate-one/duplicate-one-controller.interface.d.ts +2 -1
- package/src/routes/duplicate-one/duplicate-one-controller.mixin.d.ts +1 -1
- package/src/routes/duplicate-one/duplicate-one-controller.mixin.js +14 -6
- package/src/routes/duplicate-one/duplicate-one-controller.mixin.js.map +1 -1
- package/src/routes/duplicate-one/duplicate-one-gateway.mixin.d.ts +1 -1
- package/src/routes/duplicate-one/duplicate-one-gateway.mixin.js +5 -3
- package/src/routes/duplicate-one/duplicate-one-gateway.mixin.js.map +1 -1
- package/src/routes/duplicate-one/duplicate-one.helper.js +7 -3
- package/src/routes/duplicate-one/duplicate-one.helper.js.map +1 -1
- package/src/routes/duplicate-one/duplicate-one.module.js +4 -1
- package/src/routes/duplicate-one/duplicate-one.module.js.map +1 -1
- package/src/routes/get-many/get-many.module.js +1 -1
- package/src/routes/get-many/get-many.module.js.map +1 -1
- package/src/routes/get-one/get-one.module.js +1 -1
- package/src/routes/get-one/get-one.module.js.map +1 -1
- package/src/routes/replace-one/replace-one-controller.interface.d.ts +2 -1
- package/src/routes/replace-one/replace-one-controller.mixin.d.ts +1 -1
- package/src/routes/replace-one/replace-one-controller.mixin.js +14 -6
- package/src/routes/replace-one/replace-one-controller.mixin.js.map +1 -1
- package/src/routes/replace-one/replace-one-gateway.mixin.d.ts +1 -1
- package/src/routes/replace-one/replace-one-gateway.mixin.js +5 -3
- package/src/routes/replace-one/replace-one-gateway.mixin.js.map +1 -1
- package/src/routes/replace-one/replace-one.helper.js +7 -3
- package/src/routes/replace-one/replace-one.helper.js.map +1 -1
- package/src/routes/replace-one/replace-one.module.js +4 -1
- package/src/routes/replace-one/replace-one.module.js.map +1 -1
- package/src/routes/update-many/update-many-controller.interface.d.ts +2 -1
- package/src/routes/update-many/update-many-controller.mixin.d.ts +1 -1
- package/src/routes/update-many/update-many-controller.mixin.js +14 -6
- package/src/routes/update-many/update-many-controller.mixin.js.map +1 -1
- package/src/routes/update-many/update-many-gateway.mixin.d.ts +1 -1
- package/src/routes/update-many/update-many-gateway.mixin.js +5 -3
- package/src/routes/update-many/update-many-gateway.mixin.js.map +1 -1
- package/src/routes/update-many/update-many.helper.js +7 -3
- package/src/routes/update-many/update-many.helper.js.map +1 -1
- package/src/routes/update-many/update-many.module.js +4 -1
- package/src/routes/update-many/update-many.module.js.map +1 -1
- package/src/routes/update-one/update-one-controller.interface.d.ts +2 -1
- package/src/routes/update-one/update-one-controller.mixin.d.ts +1 -1
- package/src/routes/update-one/update-one-controller.mixin.js +14 -6
- package/src/routes/update-one/update-one-controller.mixin.js.map +1 -1
- package/src/routes/update-one/update-one-gateway.mixin.d.ts +1 -1
- package/src/routes/update-one/update-one-gateway.mixin.js +5 -3
- package/src/routes/update-one/update-one-gateway.mixin.js.map +1 -1
- package/src/routes/update-one/update-one.helper.js +7 -3
- package/src/routes/update-one/update-one.helper.js.map +1 -1
- package/src/routes/update-one/update-one.module.js +4 -1
- package/src/routes/update-one/update-one.module.js.map +1 -1
- package/src/services/dynamic-api-broadcast/dynamic-api-broadcast.service.d.ts +7 -0
- package/src/services/dynamic-api-broadcast/dynamic-api-broadcast.service.js +38 -0
- package/src/services/dynamic-api-broadcast/dynamic-api-broadcast.service.js.map +1 -0
- package/src/services/index.d.ts +1 -0
- package/src/services/index.js +1 -0
- package/src/services/index.js.map +1 -1
- package/src/version.json +1 -1
- package/test/dynamic-api-for-feature.e2e-spec.js +463 -1
- package/test/dynamic-api-for-feature.e2e-spec.js.map +1 -1
- package/test/e2e.setup.d.ts +24 -2
- package/test/e2e.setup.js +173 -18
- package/test/e2e.setup.js.map +1 -1
- package/test/test-socket-adapter.d.ts +8 -0
- package/test/test-socket-adapter.js +23 -0
- package/test/test-socket-adapter.js.map +1 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Server } from 'socket.io';
|
|
2
|
+
import { DynamicApiBroadcastConfig } from '../../interfaces';
|
|
3
|
+
export declare class DynamicApiBroadcastService {
|
|
4
|
+
private static wsServer;
|
|
5
|
+
setWsServer(server: Server): void;
|
|
6
|
+
broadcastFromHttp<T extends object>(event: string, data: T[], broadcastConfig: DynamicApiBroadcastConfig<T>): void;
|
|
7
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var DynamicApiBroadcastService_1;
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.DynamicApiBroadcastService = void 0;
|
|
11
|
+
const common_1 = require("@nestjs/common");
|
|
12
|
+
let DynamicApiBroadcastService = DynamicApiBroadcastService_1 = class DynamicApiBroadcastService {
|
|
13
|
+
setWsServer(server) {
|
|
14
|
+
DynamicApiBroadcastService_1.wsServer = server;
|
|
15
|
+
}
|
|
16
|
+
broadcastFromHttp(event, data, broadcastConfig) {
|
|
17
|
+
if (!DynamicApiBroadcastService_1.wsServer || !broadcastConfig || !data?.length) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const { enabled, eventName } = broadcastConfig;
|
|
21
|
+
if (typeof enabled === 'boolean' && !enabled) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const broadcastData = typeof enabled === 'function'
|
|
25
|
+
? data.filter((item) => enabled(item, undefined))
|
|
26
|
+
: data;
|
|
27
|
+
if (!broadcastData.length) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
DynamicApiBroadcastService_1.wsServer.emit(eventName || event, broadcastData);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
exports.DynamicApiBroadcastService = DynamicApiBroadcastService;
|
|
34
|
+
DynamicApiBroadcastService.wsServer = null;
|
|
35
|
+
exports.DynamicApiBroadcastService = DynamicApiBroadcastService = DynamicApiBroadcastService_1 = __decorate([
|
|
36
|
+
(0, common_1.Injectable)()
|
|
37
|
+
], DynamicApiBroadcastService);
|
|
38
|
+
//# sourceMappingURL=dynamic-api-broadcast.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dynamic-api-broadcast.service.js","sourceRoot":"","sources":["../../../../libs/dynamic-api/src/services/dynamic-api-broadcast/dynamic-api-broadcast.service.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAA4C;AAKrC,IAAM,0BAA0B,kCAAhC,MAAM,0BAA0B;IAGrC,WAAW,CAAC,MAAc;QACxB,4BAA0B,CAAC,QAAQ,GAAG,MAAM,CAAC;IAC/C,CAAC;IAED,iBAAiB,CACf,KAAa,EACb,IAAS,EACT,eAA6C;QAE7C,IAAI,CAAC,4BAA0B,CAAC,QAAQ,IAAI,CAAC,eAAe,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;YAC9E,OAAO;QACT,CAAC;QAED,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,eAAe,CAAC;QAE/C,IAAI,OAAO,OAAO,KAAK,SAAS,IAAI,CAAC,OAAO,EAAE,CAAC;YAC7C,OAAO;QACT,CAAC;QAED,MAAM,aAAa,GAAG,OAAO,OAAO,KAAK,UAAU;YACjD,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAE,OAAoB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;YAC/D,CAAC,CAAC,IAAI,CAAC;QAET,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC;YAC1B,OAAO;QACT,CAAC;QAED,4BAA0B,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,IAAI,KAAK,EAAE,aAAa,CAAC,CAAC;IAC9E,CAAC;;AA/BU,gEAA0B;AACtB,mCAAQ,GAAkB,IAAI,AAAtB,CAAuB;qCADnC,0BAA0B;IADtC,IAAA,mBAAU,GAAE;GACA,0BAA0B,CAgCtC"}
|
package/src/services/index.d.ts
CHANGED
package/src/services/index.js
CHANGED
|
@@ -16,5 +16,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./base/base.service"), exports);
|
|
18
18
|
__exportStar(require("./bcrypt/bcrypt.service"), exports);
|
|
19
|
+
__exportStar(require("./dynamic-api-broadcast/dynamic-api-broadcast.service"), exports);
|
|
19
20
|
__exportStar(require("./dynamic-api-global-state/dynamic-api-global-state.service"), exports);
|
|
20
21
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../libs/dynamic-api/src/services/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,sDAAoC;AACpC,0DAAwC;AACxC,8FAA4E"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../libs/dynamic-api/src/services/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,sDAAoC;AACpC,0DAAwC;AACxC,wFAAsE;AACtE,8FAA4E"}
|
package/src/version.json
CHANGED
|
@@ -18,12 +18,13 @@ require("dotenv/config");
|
|
|
18
18
|
const utils_1 = require("./utils");
|
|
19
19
|
describe('DynamicApiModule forFeature (e2e)', () => {
|
|
20
20
|
const uri = process.env.MONGO_DB_URL;
|
|
21
|
-
const initApp = async (forFeatureOptions, forRootOptions = {}, initFixtures, initMainCb) => {
|
|
21
|
+
const initApp = async (forFeatureOptions, forRootOptions = {}, initFixtures, initMainCb, testGateway) => {
|
|
22
22
|
const moduleRef = await testing_1.Test.createTestingModule({
|
|
23
23
|
imports: [
|
|
24
24
|
src_1.DynamicApiModule.forRoot(uri, forRootOptions),
|
|
25
25
|
src_1.DynamicApiModule.forFeature(forFeatureOptions),
|
|
26
26
|
],
|
|
27
|
+
providers: testGateway ? [e2e_setup_1.TestGateway] : [],
|
|
27
28
|
}).compile();
|
|
28
29
|
await (0, e2e_setup_1.createTestingApp)(moduleRef, initFixtures, initMainCb);
|
|
29
30
|
};
|
|
@@ -634,5 +635,466 @@ describe('DynamicApiModule forFeature (e2e)', () => {
|
|
|
634
635
|
});
|
|
635
636
|
});
|
|
636
637
|
});
|
|
638
|
+
describe('Websockets', () => {
|
|
639
|
+
let UserEntity = class UserEntity extends src_1.BaseEntity {
|
|
640
|
+
};
|
|
641
|
+
__decorate([
|
|
642
|
+
(0, mongoose_1.Prop)({ type: String, required: true }),
|
|
643
|
+
__metadata("design:type", String)
|
|
644
|
+
], UserEntity.prototype, "email", void 0);
|
|
645
|
+
__decorate([
|
|
646
|
+
(0, mongoose_1.Prop)({ type: String, required: true }),
|
|
647
|
+
__metadata("design:type", String)
|
|
648
|
+
], UserEntity.prototype, "password", void 0);
|
|
649
|
+
__decorate([
|
|
650
|
+
(0, mongoose_1.Prop)({ type: Boolean, default: false }),
|
|
651
|
+
__metadata("design:type", Boolean)
|
|
652
|
+
], UserEntity.prototype, "isAdmin", void 0);
|
|
653
|
+
UserEntity = __decorate([
|
|
654
|
+
(0, mongoose_1.Schema)({ collection: 'users' })
|
|
655
|
+
], UserEntity);
|
|
656
|
+
let ProductEntity = class ProductEntity extends src_1.BaseEntity {
|
|
657
|
+
};
|
|
658
|
+
__decorate([
|
|
659
|
+
(0, mongoose_1.Prop)({ type: String, required: true }),
|
|
660
|
+
__metadata("design:type", String)
|
|
661
|
+
], ProductEntity.prototype, "name", void 0);
|
|
662
|
+
__decorate([
|
|
663
|
+
(0, mongoose_1.Prop)({ type: Boolean, default: false }),
|
|
664
|
+
__metadata("design:type", Boolean)
|
|
665
|
+
], ProductEntity.prototype, "inStock", void 0);
|
|
666
|
+
__decorate([
|
|
667
|
+
(0, mongoose_1.Prop)({ type: String }),
|
|
668
|
+
__metadata("design:type", String)
|
|
669
|
+
], ProductEntity.prototype, "status", void 0);
|
|
670
|
+
ProductEntity = __decorate([
|
|
671
|
+
(0, mongoose_1.Schema)({ collection: 'products' })
|
|
672
|
+
], ProductEntity);
|
|
673
|
+
const customEvent = 'product-custom-event';
|
|
674
|
+
let adminAccessToken;
|
|
675
|
+
let userAccessToken;
|
|
676
|
+
let firstProduct;
|
|
677
|
+
let secondProduct;
|
|
678
|
+
beforeEach(async () => {
|
|
679
|
+
const fixtures = async (_) => {
|
|
680
|
+
const model = await (0, utils_1.getModelFromEntity)(ProductEntity);
|
|
681
|
+
await model.insertMany([
|
|
682
|
+
{ name: 'mayo', inStock: true },
|
|
683
|
+
{ name: 'ketchup' },
|
|
684
|
+
]).then(([mayoProduct, ketchupProduct]) => {
|
|
685
|
+
firstProduct = mayoProduct;
|
|
686
|
+
secondProduct = ketchupProduct;
|
|
687
|
+
});
|
|
688
|
+
};
|
|
689
|
+
await initApp({
|
|
690
|
+
entity: ProductEntity,
|
|
691
|
+
controllerOptions: {
|
|
692
|
+
apiTag: 'Product',
|
|
693
|
+
path: 'products',
|
|
694
|
+
},
|
|
695
|
+
routes: [
|
|
696
|
+
{ type: 'GetMany', isPublic: true },
|
|
697
|
+
{
|
|
698
|
+
type: 'CreateMany',
|
|
699
|
+
webSocket: true,
|
|
700
|
+
broadcast: {
|
|
701
|
+
enabled: (_, user) => user.isAdmin === true,
|
|
702
|
+
},
|
|
703
|
+
},
|
|
704
|
+
{
|
|
705
|
+
type: 'CreateOne',
|
|
706
|
+
webSocket: true,
|
|
707
|
+
broadcast: { enabled: true },
|
|
708
|
+
},
|
|
709
|
+
{
|
|
710
|
+
type: 'UpdateMany',
|
|
711
|
+
webSocket: true,
|
|
712
|
+
broadcast: {
|
|
713
|
+
enabled: false,
|
|
714
|
+
eventName: customEvent,
|
|
715
|
+
},
|
|
716
|
+
},
|
|
717
|
+
{
|
|
718
|
+
type: 'UpdateOne',
|
|
719
|
+
webSocket: true,
|
|
720
|
+
broadcast: {
|
|
721
|
+
enabled: true,
|
|
722
|
+
eventName: customEvent,
|
|
723
|
+
},
|
|
724
|
+
},
|
|
725
|
+
{
|
|
726
|
+
type: 'ReplaceOne',
|
|
727
|
+
webSocket: true,
|
|
728
|
+
broadcast: {
|
|
729
|
+
enabled: true,
|
|
730
|
+
},
|
|
731
|
+
},
|
|
732
|
+
{
|
|
733
|
+
type: 'DuplicateOne',
|
|
734
|
+
webSocket: true,
|
|
735
|
+
broadcast: {
|
|
736
|
+
enabled: (_, user) => user.isAdmin === true,
|
|
737
|
+
eventName: customEvent,
|
|
738
|
+
},
|
|
739
|
+
},
|
|
740
|
+
{
|
|
741
|
+
type: 'DuplicateMany',
|
|
742
|
+
webSocket: true,
|
|
743
|
+
broadcast: {
|
|
744
|
+
enabled: true,
|
|
745
|
+
},
|
|
746
|
+
},
|
|
747
|
+
{
|
|
748
|
+
type: 'DeleteOne',
|
|
749
|
+
webSocket: true,
|
|
750
|
+
broadcast: {
|
|
751
|
+
enabled: true,
|
|
752
|
+
},
|
|
753
|
+
},
|
|
754
|
+
{
|
|
755
|
+
type: 'DeleteMany',
|
|
756
|
+
webSocket: true,
|
|
757
|
+
broadcast: {
|
|
758
|
+
enabled: true,
|
|
759
|
+
},
|
|
760
|
+
},
|
|
761
|
+
],
|
|
762
|
+
}, {
|
|
763
|
+
useAuth: {
|
|
764
|
+
userEntity: UserEntity,
|
|
765
|
+
login: {
|
|
766
|
+
additionalFields: ['isAdmin'],
|
|
767
|
+
},
|
|
768
|
+
webSocket: true,
|
|
769
|
+
},
|
|
770
|
+
}, fixtures, async (app) => {
|
|
771
|
+
app.useWebSocketAdapter(new e2e_setup_1.TestSocketAdapter(app));
|
|
772
|
+
}, true);
|
|
773
|
+
adminAccessToken = (await e2e_setup_1.server.emit('auth-register', { email: 'unit@test.co', password: 'test', isAdmin: true })).accessToken;
|
|
774
|
+
userAccessToken = (await e2e_setup_1.server.emit('auth-register', { email: 'toto@test.co', password: 'test' })).accessToken;
|
|
775
|
+
});
|
|
776
|
+
it('[CreateOne] should broadcast created product to all clients', async () => {
|
|
777
|
+
await e2e_setup_1.server.emit('create-one-product', { name: 'mustard', inStock: true }, { accessToken: adminAccessToken, expectBroadcast: true });
|
|
778
|
+
expect(e2e_setup_1.handleSocketBroadcast).toHaveBeenCalledTimes(1);
|
|
779
|
+
expect(e2e_setup_1.handleSocketBroadcast).toHaveBeenCalledWith({
|
|
780
|
+
event: 'create-one-product',
|
|
781
|
+
data: expect.arrayContaining([
|
|
782
|
+
expect.objectContaining({
|
|
783
|
+
name: 'mustard',
|
|
784
|
+
inStock: true,
|
|
785
|
+
}),
|
|
786
|
+
]),
|
|
787
|
+
});
|
|
788
|
+
});
|
|
789
|
+
it('[CreateMany] should broadcast created products to all clients when admin', async () => {
|
|
790
|
+
await e2e_setup_1.server.emit('create-many-product', {
|
|
791
|
+
list: [
|
|
792
|
+
{ name: 'mustard', inStock: true },
|
|
793
|
+
{ name: 'relish', inStock: false },
|
|
794
|
+
],
|
|
795
|
+
}, { accessToken: adminAccessToken, expectBroadcast: true });
|
|
796
|
+
expect(e2e_setup_1.handleSocketBroadcast).toHaveBeenCalledTimes(1);
|
|
797
|
+
expect(e2e_setup_1.handleSocketBroadcast).toHaveBeenCalledWith({
|
|
798
|
+
event: 'create-many-product',
|
|
799
|
+
data: expect.arrayContaining([
|
|
800
|
+
expect.objectContaining({
|
|
801
|
+
name: 'mustard',
|
|
802
|
+
inStock: true,
|
|
803
|
+
}),
|
|
804
|
+
expect.objectContaining({
|
|
805
|
+
name: 'relish',
|
|
806
|
+
inStock: false,
|
|
807
|
+
}),
|
|
808
|
+
]),
|
|
809
|
+
});
|
|
810
|
+
});
|
|
811
|
+
it('[CreateMany] should NOT broadcast created products when user is not admin', async () => {
|
|
812
|
+
await e2e_setup_1.server.emit('create-many-product', {
|
|
813
|
+
list: [
|
|
814
|
+
{ name: 'pickles', inStock: true },
|
|
815
|
+
{ name: 'onions', inStock: false },
|
|
816
|
+
],
|
|
817
|
+
}, { accessToken: userAccessToken });
|
|
818
|
+
expect(e2e_setup_1.handleSocketBroadcast).not.toHaveBeenCalled();
|
|
819
|
+
});
|
|
820
|
+
it('[UpdateOne] should broadcast updated product to all clients with custom event name', async () => {
|
|
821
|
+
await e2e_setup_1.server.emit('update-one-product', {
|
|
822
|
+
id: firstProduct.id,
|
|
823
|
+
name: 'updated mayo',
|
|
824
|
+
inStock: false,
|
|
825
|
+
}, { accessToken: adminAccessToken, broadcastEvent: customEvent, expectBroadcast: true });
|
|
826
|
+
expect(e2e_setup_1.handleSocketBroadcast).toHaveBeenCalledTimes(1);
|
|
827
|
+
expect(e2e_setup_1.handleSocketBroadcast).toHaveBeenCalledWith({
|
|
828
|
+
event: customEvent,
|
|
829
|
+
data: expect.arrayContaining([
|
|
830
|
+
expect.objectContaining({
|
|
831
|
+
id: firstProduct.id,
|
|
832
|
+
name: 'updated mayo',
|
|
833
|
+
inStock: false,
|
|
834
|
+
}),
|
|
835
|
+
]),
|
|
836
|
+
});
|
|
837
|
+
});
|
|
838
|
+
it('[UpdateMany] should NOT broadcast updated products when broadcast is disabled', async () => {
|
|
839
|
+
const { body: products } = await e2e_setup_1.server.get('/products');
|
|
840
|
+
await e2e_setup_1.server.emit('update-many-product', {
|
|
841
|
+
ids: products.map(p => p.id),
|
|
842
|
+
status: 'updated',
|
|
843
|
+
}, { accessToken: adminAccessToken });
|
|
844
|
+
expect(e2e_setup_1.handleSocketBroadcast).not.toHaveBeenCalled();
|
|
845
|
+
});
|
|
846
|
+
it('[DeleteOne] should broadcast deleted product id to all clients', async () => {
|
|
847
|
+
await e2e_setup_1.server.emit('delete-one-product', {
|
|
848
|
+
id: firstProduct.id,
|
|
849
|
+
}, { accessToken: adminAccessToken, expectBroadcast: true });
|
|
850
|
+
expect(e2e_setup_1.handleSocketBroadcast).toHaveBeenCalledTimes(1);
|
|
851
|
+
expect(e2e_setup_1.handleSocketBroadcast).toHaveBeenCalledWith({
|
|
852
|
+
event: 'delete-one-product',
|
|
853
|
+
data: expect.arrayContaining([
|
|
854
|
+
expect.objectContaining({
|
|
855
|
+
id: firstProduct.id,
|
|
856
|
+
}),
|
|
857
|
+
]),
|
|
858
|
+
});
|
|
859
|
+
});
|
|
860
|
+
it('[ReplaceOne] should broadcast replaced product to all clients', async () => {
|
|
861
|
+
await e2e_setup_1.server.emit('replace-one-product', {
|
|
862
|
+
id: firstProduct.id,
|
|
863
|
+
name: 'replaced mayo',
|
|
864
|
+
inStock: false,
|
|
865
|
+
}, { accessToken: adminAccessToken, expectBroadcast: true });
|
|
866
|
+
expect(e2e_setup_1.handleSocketBroadcast).toHaveBeenCalledTimes(1);
|
|
867
|
+
expect(e2e_setup_1.handleSocketBroadcast).toHaveBeenCalledWith({
|
|
868
|
+
event: 'replace-one-product',
|
|
869
|
+
data: expect.arrayContaining([
|
|
870
|
+
expect.objectContaining({
|
|
871
|
+
id: firstProduct.id,
|
|
872
|
+
name: 'replaced mayo',
|
|
873
|
+
inStock: false,
|
|
874
|
+
}),
|
|
875
|
+
]),
|
|
876
|
+
});
|
|
877
|
+
});
|
|
878
|
+
it('[DuplicateOne] should broadcast duplicated product to all clients with custom event name when admin', async () => {
|
|
879
|
+
await e2e_setup_1.server.emit('duplicate-one-product', {
|
|
880
|
+
id: firstProduct.id,
|
|
881
|
+
name: 'duplicated mayo',
|
|
882
|
+
}, { accessToken: adminAccessToken, broadcastEvent: customEvent, expectBroadcast: true });
|
|
883
|
+
expect(e2e_setup_1.handleSocketBroadcast).toHaveBeenCalledTimes(1);
|
|
884
|
+
expect(e2e_setup_1.handleSocketBroadcast).toHaveBeenCalledWith({
|
|
885
|
+
event: customEvent,
|
|
886
|
+
data: expect.arrayContaining([
|
|
887
|
+
expect.objectContaining({
|
|
888
|
+
name: 'duplicated mayo',
|
|
889
|
+
inStock: firstProduct.inStock,
|
|
890
|
+
}),
|
|
891
|
+
]),
|
|
892
|
+
});
|
|
893
|
+
});
|
|
894
|
+
it('[DuplicateOne] should NOT broadcast duplicated product when user is not admin', async () => {
|
|
895
|
+
await e2e_setup_1.server.emit('duplicate-one-product', {
|
|
896
|
+
id: firstProduct.id,
|
|
897
|
+
name: 'duplicated mayo user',
|
|
898
|
+
}, { accessToken: userAccessToken });
|
|
899
|
+
expect(e2e_setup_1.handleSocketBroadcast).not.toHaveBeenCalled();
|
|
900
|
+
});
|
|
901
|
+
it('[DuplicateMany] should broadcast duplicated products to all clients', async () => {
|
|
902
|
+
await e2e_setup_1.server.emit('duplicate-many-product', {
|
|
903
|
+
ids: [secondProduct.id, firstProduct.id],
|
|
904
|
+
status: 'duplicated',
|
|
905
|
+
}, { accessToken: adminAccessToken, expectBroadcast: true });
|
|
906
|
+
expect(e2e_setup_1.handleSocketBroadcast).toHaveBeenCalledTimes(1);
|
|
907
|
+
expect(e2e_setup_1.handleSocketBroadcast).toHaveBeenCalledWith({
|
|
908
|
+
event: 'duplicate-many-product',
|
|
909
|
+
data: expect.arrayContaining([
|
|
910
|
+
expect.objectContaining({
|
|
911
|
+
id: expect.any(String),
|
|
912
|
+
name: secondProduct.name,
|
|
913
|
+
inStock: secondProduct.inStock,
|
|
914
|
+
status: 'duplicated',
|
|
915
|
+
}),
|
|
916
|
+
expect.objectContaining({
|
|
917
|
+
id: expect.any(String),
|
|
918
|
+
name: firstProduct.name,
|
|
919
|
+
inStock: firstProduct.inStock,
|
|
920
|
+
status: 'duplicated',
|
|
921
|
+
}),
|
|
922
|
+
]),
|
|
923
|
+
});
|
|
924
|
+
});
|
|
925
|
+
it('[DeleteMany] should broadcast deleted product ids to all clients', async () => {
|
|
926
|
+
const products = [firstProduct, secondProduct];
|
|
927
|
+
await e2e_setup_1.server.emit('delete-many-product', {
|
|
928
|
+
ids: products.map(p => p.id),
|
|
929
|
+
}, { accessToken: adminAccessToken, expectBroadcast: true });
|
|
930
|
+
expect(e2e_setup_1.handleSocketBroadcast).toHaveBeenCalledTimes(1);
|
|
931
|
+
expect(e2e_setup_1.handleSocketBroadcast).toHaveBeenCalledWith({
|
|
932
|
+
event: 'delete-many-product',
|
|
933
|
+
data: expect.arrayContaining(products.map(p => ({ id: p.id }))),
|
|
934
|
+
});
|
|
935
|
+
});
|
|
936
|
+
});
|
|
937
|
+
describe('HTTP Broadcast', () => {
|
|
938
|
+
let HbProductEntity = class HbProductEntity extends src_1.BaseEntity {
|
|
939
|
+
};
|
|
940
|
+
__decorate([
|
|
941
|
+
(0, mongoose_1.Prop)({ type: String, required: true }),
|
|
942
|
+
__metadata("design:type", String)
|
|
943
|
+
], HbProductEntity.prototype, "name", void 0);
|
|
944
|
+
__decorate([
|
|
945
|
+
(0, mongoose_1.Prop)({ type: Boolean, default: false }),
|
|
946
|
+
__metadata("design:type", Boolean)
|
|
947
|
+
], HbProductEntity.prototype, "inStock", void 0);
|
|
948
|
+
__decorate([
|
|
949
|
+
(0, mongoose_1.Prop)({ type: String }),
|
|
950
|
+
__metadata("design:type", String)
|
|
951
|
+
], HbProductEntity.prototype, "status", void 0);
|
|
952
|
+
HbProductEntity = __decorate([
|
|
953
|
+
(0, mongoose_1.Schema)({ collection: 'hb_products' })
|
|
954
|
+
], HbProductEntity);
|
|
955
|
+
const customEvent = 'hb-product-custom-event';
|
|
956
|
+
let firstProduct;
|
|
957
|
+
let secondProduct;
|
|
958
|
+
beforeEach(async () => {
|
|
959
|
+
const fixtures = async (_) => {
|
|
960
|
+
const model = await (0, utils_1.getModelFromEntity)(HbProductEntity);
|
|
961
|
+
await model.insertMany([
|
|
962
|
+
{ name: 'mayo', inStock: true },
|
|
963
|
+
{ name: 'ketchup', inStock: false },
|
|
964
|
+
]).then(([mayoProduct, ketchupProduct]) => {
|
|
965
|
+
firstProduct = mayoProduct;
|
|
966
|
+
secondProduct = ketchupProduct;
|
|
967
|
+
});
|
|
968
|
+
};
|
|
969
|
+
await initApp({
|
|
970
|
+
entity: HbProductEntity,
|
|
971
|
+
controllerOptions: {
|
|
972
|
+
apiTag: 'HbProduct',
|
|
973
|
+
path: 'hb-products',
|
|
974
|
+
isPublic: true,
|
|
975
|
+
},
|
|
976
|
+
routes: [
|
|
977
|
+
{ type: 'GetMany', isPublic: true },
|
|
978
|
+
{
|
|
979
|
+
type: 'CreateOne',
|
|
980
|
+
broadcast: { enabled: true },
|
|
981
|
+
},
|
|
982
|
+
{
|
|
983
|
+
type: 'CreateMany',
|
|
984
|
+
broadcast: { enabled: (data) => data.inStock === true },
|
|
985
|
+
},
|
|
986
|
+
{
|
|
987
|
+
type: 'UpdateOne',
|
|
988
|
+
broadcast: { enabled: true, eventName: customEvent },
|
|
989
|
+
},
|
|
990
|
+
{
|
|
991
|
+
type: 'UpdateMany',
|
|
992
|
+
broadcast: { enabled: false },
|
|
993
|
+
},
|
|
994
|
+
{
|
|
995
|
+
type: 'ReplaceOne',
|
|
996
|
+
broadcast: { enabled: true },
|
|
997
|
+
},
|
|
998
|
+
{
|
|
999
|
+
type: 'DuplicateOne',
|
|
1000
|
+
broadcast: { enabled: true },
|
|
1001
|
+
},
|
|
1002
|
+
{
|
|
1003
|
+
type: 'DuplicateMany',
|
|
1004
|
+
broadcast: { enabled: true },
|
|
1005
|
+
},
|
|
1006
|
+
{
|
|
1007
|
+
type: 'DeleteOne',
|
|
1008
|
+
broadcast: { enabled: true },
|
|
1009
|
+
},
|
|
1010
|
+
{
|
|
1011
|
+
type: 'DeleteMany',
|
|
1012
|
+
broadcast: { enabled: true },
|
|
1013
|
+
},
|
|
1014
|
+
],
|
|
1015
|
+
}, undefined, fixtures, async (app) => {
|
|
1016
|
+
app.useWebSocketAdapter(new e2e_setup_1.TestSocketAdapter(app));
|
|
1017
|
+
}, true);
|
|
1018
|
+
});
|
|
1019
|
+
it('[CreateOne] should broadcast created product to all WS clients after HTTP call', async () => {
|
|
1020
|
+
const { broadcastData } = await e2e_setup_1.server.httpWithBroadcast('post', '/hb-products', { name: 'mustard', inStock: true }, { broadcastEvent: 'create-one-hb-product' });
|
|
1021
|
+
expect(e2e_setup_1.handleSocketBroadcast).toHaveBeenCalledTimes(1);
|
|
1022
|
+
expect(e2e_setup_1.handleSocketBroadcast).toHaveBeenCalledWith({
|
|
1023
|
+
event: 'create-one-hb-product',
|
|
1024
|
+
data: expect.arrayContaining([
|
|
1025
|
+
expect.objectContaining({ name: 'mustard', inStock: true }),
|
|
1026
|
+
]),
|
|
1027
|
+
});
|
|
1028
|
+
expect(broadcastData).toEqual(expect.arrayContaining([
|
|
1029
|
+
expect.objectContaining({ name: 'mustard', inStock: true }),
|
|
1030
|
+
]));
|
|
1031
|
+
});
|
|
1032
|
+
it('[CreateMany] should broadcast only inStock products after HTTP call', async () => {
|
|
1033
|
+
const { broadcastData } = await e2e_setup_1.server.httpWithBroadcast('post', '/hb-products/many', { list: [{ name: 'pickles', inStock: true }, { name: 'onions', inStock: false }] }, { broadcastEvent: 'create-many-hb-product' });
|
|
1034
|
+
expect(e2e_setup_1.handleSocketBroadcast).toHaveBeenCalledTimes(1);
|
|
1035
|
+
expect(broadcastData).toEqual(expect.arrayContaining([expect.objectContaining({ name: 'pickles', inStock: true })]));
|
|
1036
|
+
expect(broadcastData).not.toEqual(expect.arrayContaining([expect.objectContaining({ name: 'onions' })]));
|
|
1037
|
+
});
|
|
1038
|
+
it('[UpdateOne] should broadcast updated product with custom event name after HTTP call', async () => {
|
|
1039
|
+
const { broadcastData } = await e2e_setup_1.server.httpWithBroadcast('patch', `/hb-products/${firstProduct.id}`, { name: 'updated mayo', inStock: false }, { broadcastEvent: customEvent });
|
|
1040
|
+
expect(e2e_setup_1.handleSocketBroadcast).toHaveBeenCalledTimes(1);
|
|
1041
|
+
expect(e2e_setup_1.handleSocketBroadcast).toHaveBeenCalledWith({
|
|
1042
|
+
event: customEvent,
|
|
1043
|
+
data: expect.arrayContaining([
|
|
1044
|
+
expect.objectContaining({ id: firstProduct.id, name: 'updated mayo', inStock: false }),
|
|
1045
|
+
]),
|
|
1046
|
+
});
|
|
1047
|
+
expect(broadcastData).toEqual(expect.arrayContaining([
|
|
1048
|
+
expect.objectContaining({ name: 'updated mayo' }),
|
|
1049
|
+
]));
|
|
1050
|
+
});
|
|
1051
|
+
it('[ReplaceOne] should broadcast replaced product after HTTP call', async () => {
|
|
1052
|
+
const { broadcastData } = await e2e_setup_1.server.httpWithBroadcast('put', `/hb-products/${firstProduct.id}`, { name: 'replaced mayo', inStock: false }, { broadcastEvent: 'replace-one-hb-product' });
|
|
1053
|
+
expect(e2e_setup_1.handleSocketBroadcast).toHaveBeenCalledTimes(1);
|
|
1054
|
+
expect(broadcastData).toEqual(expect.arrayContaining([
|
|
1055
|
+
expect.objectContaining({ id: firstProduct.id, name: 'replaced mayo', inStock: false }),
|
|
1056
|
+
]));
|
|
1057
|
+
});
|
|
1058
|
+
it('[DuplicateOne] should broadcast duplicated product after HTTP call', async () => {
|
|
1059
|
+
const { broadcastData } = await e2e_setup_1.server.httpWithBroadcast('post', `/hb-products/duplicate/${firstProduct.id}`, { name: 'duplicated mayo' }, { broadcastEvent: 'duplicate-one-hb-product' });
|
|
1060
|
+
expect(e2e_setup_1.handleSocketBroadcast).toHaveBeenCalledTimes(1);
|
|
1061
|
+
expect(broadcastData).toEqual(expect.arrayContaining([
|
|
1062
|
+
expect.objectContaining({ name: 'duplicated mayo', inStock: firstProduct.inStock }),
|
|
1063
|
+
]));
|
|
1064
|
+
});
|
|
1065
|
+
it('[DuplicateMany] should broadcast duplicated products after HTTP call', async () => {
|
|
1066
|
+
const { broadcastData } = await e2e_setup_1.server.httpWithBroadcast('post', '/hb-products/duplicate', { status: 'dup' }, {
|
|
1067
|
+
broadcastEvent: 'duplicate-many-hb-product',
|
|
1068
|
+
query: { ids: [firstProduct.id, secondProduct.id] },
|
|
1069
|
+
});
|
|
1070
|
+
expect(e2e_setup_1.handleSocketBroadcast).toHaveBeenCalledTimes(1);
|
|
1071
|
+
expect(broadcastData.length).toBeGreaterThanOrEqual(2);
|
|
1072
|
+
});
|
|
1073
|
+
it('[DeleteOne] should broadcast deleted product id to all WS clients after HTTP call', async () => {
|
|
1074
|
+
const { broadcastData } = await e2e_setup_1.server.httpWithBroadcast('delete', `/hb-products/${firstProduct.id}`, {}, { broadcastEvent: 'delete-one-hb-product' });
|
|
1075
|
+
expect(e2e_setup_1.handleSocketBroadcast).toHaveBeenCalledTimes(1);
|
|
1076
|
+
expect(e2e_setup_1.handleSocketBroadcast).toHaveBeenCalledWith({
|
|
1077
|
+
event: 'delete-one-hb-product',
|
|
1078
|
+
data: expect.arrayContaining([expect.objectContaining({ id: firstProduct.id })]),
|
|
1079
|
+
});
|
|
1080
|
+
expect(broadcastData).toEqual(expect.arrayContaining([{ id: firstProduct.id }]));
|
|
1081
|
+
});
|
|
1082
|
+
it('[DeleteMany] should broadcast deleted product ids to all WS clients after HTTP call', async () => {
|
|
1083
|
+
const ids = [firstProduct.id, secondProduct.id];
|
|
1084
|
+
const { broadcastData } = await e2e_setup_1.server.httpWithBroadcast('delete', '/hb-products', {}, {
|
|
1085
|
+
broadcastEvent: 'delete-many-hb-product',
|
|
1086
|
+
query: { ids },
|
|
1087
|
+
});
|
|
1088
|
+
expect(e2e_setup_1.handleSocketBroadcast).toHaveBeenCalledTimes(1);
|
|
1089
|
+
expect(broadcastData).toEqual(expect.arrayContaining(ids.map(id => ({ id }))));
|
|
1090
|
+
});
|
|
1091
|
+
it('[UpdateMany] should NOT broadcast when enabled is false', async () => {
|
|
1092
|
+
const { body: products } = await e2e_setup_1.server.get('/hb-products');
|
|
1093
|
+
const ids = products.map((p) => p.id);
|
|
1094
|
+
const response = await e2e_setup_1.server.patch(`/hb-products/many?ids=${ids.join('&ids=')}`, { status: 'updated' });
|
|
1095
|
+
expect(response).toBeDefined();
|
|
1096
|
+
expect(e2e_setup_1.handleSocketBroadcast).not.toHaveBeenCalled();
|
|
1097
|
+
});
|
|
1098
|
+
});
|
|
637
1099
|
});
|
|
638
1100
|
//# sourceMappingURL=dynamic-api-for-feature.e2e-spec.js.map
|