mongodb-dynamic-api
Version:
Auto generated CRUD API for MongoDB using NestJS
159 lines • 9.72 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
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;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var DynamicApiModule_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.DynamicApiModule = void 0;
const cache_manager_1 = require("@nestjs/cache-manager");
const common_1 = require("@nestjs/common");
const core_1 = require("@nestjs/core");
const http_adapter_host_1 = require("@nestjs/core/helpers/http-adapter-host");
const mongoose_1 = require("@nestjs/mongoose");
const guards_1 = require("./guards");
const helpers_1 = require("./helpers");
const interceptors_1 = require("./interceptors");
const interfaces_1 = require("./interfaces");
const modules_1 = require("./modules");
const routes_1 = require("./routes");
const services_1 = require("./services");
let DynamicApiModule = DynamicApiModule_1 = class DynamicApiModule {
static forRoot(uri, { useGlobalCache = true, cacheOptions = {}, useAuth, routesConfig, webSocket, } = {}) {
if (!uri) {
throw new Error('You must provide a valid mongodb uri in the forRoot method to use MongoDB Dynamic API');
}
this.state.set([
'partial',
this.buildStateFromOptions(uri, useGlobalCache, cacheOptions, useAuth, routesConfig, webSocket),
]);
return {
module: DynamicApiModule_1,
imports: [
modules_1.DynamicApiConfigModule.register(this.state.get()),
cache_manager_1.CacheModule.register({ isGlobal: true, ...cacheOptions }),
mongoose_1.MongooseModule.forRoot(uri, { connectionName: this.state.get('connectionName') }),
...(useAuth?.userEntity ? [
modules_1.AuthModule.forRoot({ ...useAuth, webSocket: useAuth.webSocket ?? webSocket }),
] : []),
],
exports: [modules_1.DynamicApiConfigModule],
};
}
static forFeature({ entity, controllerOptions, routes, webSocket: featureWebSocket, }) {
const schema = (0, helpers_1.buildSchemaFromEntity)(entity);
const databaseModule = mongoose_1.MongooseModule.forFeature([{ name: entity.name, schema }], this.state.get('connectionName'));
services_1.DynamicApiGlobalStateService.addEntitySchema(entity, schema);
return new Promise((resolve, reject) => {
const waitForState = setTimeout(() => {
reject(new Error('Dynamic API state could not be initialized. Please check your configuration.'));
}, 5000);
services_1.DynamicApiGlobalStateService.onInitialized().subscribe((stateInitialized) => {
if (!stateInitialized) {
return;
}
if (waitForState) {
clearTimeout(waitForState);
}
const { version: controllerVersion, validationPipeOptions: controllerValidationPipeOptions, routesConfig: controllerRoutesConfig, } = controllerOptions;
const castType = (t) => t;
const castModule = (m) => m;
const moduleByRouteType = new Map([
[castType('CreateMany'), castModule(routes_1.CreateManyModule)],
[castType('CreateOne'), castModule(routes_1.CreateOneModule)],
[castType('DeleteMany'), castModule(routes_1.DeleteManyModule)],
[castType('DeleteOne'), castModule(routes_1.DeleteOneModule)],
[castType('DuplicateMany'), castModule(routes_1.DuplicateManyModule)],
[castType('DuplicateOne'), castModule(routes_1.DuplicateOneModule)],
[castType('GetMany'), castModule(routes_1.GetManyModule)],
[castType('GetOne'), castModule(routes_1.GetOneModule)],
[castType('ReplaceOne'), castModule(routes_1.ReplaceOneModule)],
[castType('UpdateMany'), castModule(routes_1.UpdateManyModule)],
[castType('UpdateOne'), castModule(routes_1.UpdateOneModule)],
[castType('Aggregate'), castModule(routes_1.AggregateModule)],
]);
routes = this.setDefaultRoutes(this.state.get('routesConfig'), controllerRoutesConfig, routes);
const apiModule = {
module: DynamicApiModule_1,
imports: [
...routes.map((routeConfig) => {
const { type, description: routeDescription, version: routeVersion, validationPipeOptions: routeValidationPipeOptions, webSocket: routeWebSocket, } = routeConfig;
const module = moduleByRouteType.get(type);
const description = routeDescription ?? (0, helpers_1.getDefaultRouteDescription)(type, entity.name);
const version = routeVersion ?? controllerVersion;
if (version && !(0, helpers_1.isValidVersion)(version)) {
reject(new Error(`Invalid version ${version} for ${type} route.`
+ ' Version must be a string that matches numeric format, e.g. 1, 2, 3, ..., 99.'));
return;
}
const validationPipeOptions = routeValidationPipeOptions ?? controllerValidationPipeOptions;
return module.forFeature(databaseModule, entity, controllerOptions, { ...routeConfig, description }, version, validationPipeOptions ?? { transform: true }, routeWebSocket ?? featureWebSocket);
}),
],
providers: [
{
provide: core_1.APP_INTERCEPTOR,
inject: [cache_manager_1.CACHE_MANAGER, core_1.Reflector, http_adapter_host_1.HttpAdapterHost, interfaces_1.DYNAMIC_API_GLOBAL_STATE],
useFactory: (cacheManager, reflector, httpAdapterHost, state) => {
return new interceptors_1.DynamicApiCacheInterceptor(cacheManager, reflector, httpAdapterHost, state);
},
},
{
provide: core_1.APP_GUARD,
inject: [core_1.Reflector, interfaces_1.DYNAMIC_API_GLOBAL_STATE],
useFactory: (reflector, state) => {
return new guards_1.DynamicApiJwtAuthGuard(reflector, state);
},
},
],
};
resolve(apiModule);
});
});
}
static buildStateFromOptions(uri, useGlobalCache, cacheOptions, useAuth, routesConfig, webSocket) {
const routesConfigState = this.state.get('routesConfig');
return {
uri,
initialized: true,
isGlobalCacheEnabled: useGlobalCache,
...(cacheOptions?.excludePaths ? { cacheExcludedPaths: cacheOptions?.excludePaths } : {}),
...(useAuth?.userEntity ? {
isAuthEnabled: true,
credentials: {
loginField: !useAuth.login?.loginField ? 'email' : String(useAuth.login.loginField),
passwordField: !useAuth.login?.passwordField ? 'password' : String(useAuth.login.passwordField),
},
jwtSecret: useAuth.jwt?.secret ?? 'dynamic-api-jwt-secret',
jwtExpirationTime: useAuth.jwt?.expiresIn ?? '1d',
gatewayOptions: (0, helpers_1.initializeConfigFromOptions)(webSocket),
} : {}),
...(routesConfig?.excluded?.length || routesConfig?.defaults.length ? {
routesConfig: {
defaults: routesConfig.defaults?.length ? routesConfig.defaults : routesConfigState.defaults,
excluded: routesConfig.excluded ?? [],
},
} : {}),
gatewayOptions: (0, helpers_1.initializeConfigFromOptions)(webSocket),
};
}
static setDefaultRoutes(stateRoutesConfig, controllerRoutesConfig = {}, routes = []) {
const defaults = controllerRoutesConfig.defaults ?? stateRoutesConfig.defaults;
const excluded = controllerRoutesConfig.excluded ?? stateRoutesConfig.excluded;
const routesWithSubPath = routes.filter((route) => route.subPath);
const routesWithoutSubPath = routes.filter((route) => !route.subPath && !defaults.includes(route.type));
return routesWithSubPath.concat(routesWithoutSubPath, defaults.filter((type) => !excluded.includes(type))
.map((type) => {
const configuredRoute = routes.find((route) => route.type === type && !route.subPath);
return configuredRoute ?? { type };
}));
}
};
exports.DynamicApiModule = DynamicApiModule;
DynamicApiModule.state = new services_1.DynamicApiGlobalStateService();
exports.DynamicApiModule = DynamicApiModule = DynamicApiModule_1 = __decorate([
(0, common_1.Module)({})
], DynamicApiModule);
//# sourceMappingURL=dynamic-api.module.js.map