@decaf-ts/for-nest 0.11.2 → 0.12.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 +274 -2
- package/dist/for-nest.cjs +1 -1
- package/dist/for-nest.cjs.map +1 -1
- package/dist/for-nest.js +1 -1
- package/dist/for-nest.js.map +1 -1
- package/lib/cjs/auth/AuthInterceptor.cjs +93 -0
- package/lib/cjs/auth/AuthInterceptor.cjs.map +1 -0
- package/lib/cjs/auth/DecafAuthHandler.cjs +37 -0
- package/lib/cjs/auth/DecafAuthHandler.cjs.map +1 -0
- package/lib/cjs/auth/DecafAuthModule.cjs +50 -0
- package/lib/cjs/auth/DecafAuthModule.cjs.map +1 -0
- package/lib/cjs/auth/constants.cjs +10 -0
- package/lib/cjs/auth/constants.cjs.map +1 -0
- package/lib/cjs/auth/decorators.cjs +28 -0
- package/lib/cjs/auth/decorators.cjs.map +1 -0
- package/lib/cjs/{ram → auth}/index.cjs +6 -2
- package/lib/cjs/auth/index.cjs.map +1 -0
- package/lib/cjs/bin/cli.cjs +1 -1
- package/lib/cjs/bin/cli.cjs.map +1 -1
- package/lib/cjs/cli-module.cjs +63 -6
- package/lib/cjs/cli-module.cjs.map +1 -1
- package/lib/cjs/constants.cjs +4 -5
- package/lib/cjs/constants.cjs.map +1 -1
- package/lib/cjs/controllers.cjs +15 -112
- package/lib/cjs/controllers.cjs.map +1 -1
- package/lib/cjs/core-module.cjs +7 -7
- package/lib/cjs/core-module.cjs.map +1 -1
- package/lib/cjs/decaf-model/DecafModelModule.cjs +27 -3
- package/lib/cjs/decaf-model/DecafModelModule.cjs.map +1 -1
- package/lib/cjs/decaf-model/FromModelController.cjs +618 -792
- package/lib/cjs/decaf-model/FromModelController.cjs.map +1 -1
- package/lib/cjs/decaf-model/decorators/ApiOperationFromModel.cjs +52 -15
- package/lib/cjs/decaf-model/decorators/ApiOperationFromModel.cjs.map +1 -1
- package/lib/cjs/decaf-model/decorators/ApiParamsFromModel.cjs +1 -1
- package/lib/cjs/decaf-model/decorators/ApiParamsFromModel.cjs.map +1 -1
- package/lib/cjs/decaf-model/decorators/DecafBody.cjs +1 -1
- package/lib/cjs/decaf-model/decorators/DecafBody.cjs.map +1 -1
- package/lib/cjs/decaf-model/decorators/DecafParams.cjs +1 -1
- package/lib/cjs/decaf-model/decorators/DecafParams.cjs.map +1 -1
- package/lib/cjs/decaf-model/decorators/controller-config.cjs +29 -0
- package/lib/cjs/decaf-model/decorators/controller-config.cjs.map +1 -0
- package/lib/cjs/decaf-model/decorators/decorators.cjs +4 -44
- package/lib/cjs/decaf-model/decorators/decorators.cjs.map +1 -1
- package/lib/cjs/decaf-model/decorators/expose.cjs +13 -0
- package/lib/cjs/decaf-model/decorators/expose.cjs.map +1 -0
- package/lib/cjs/decaf-model/decorators/index.cjs +2 -1
- package/lib/cjs/decaf-model/decorators/index.cjs.map +1 -1
- package/lib/cjs/decaf-model/decorators/types.cjs +1 -1
- package/lib/cjs/decaf-model/decorators/types.cjs.map +1 -1
- package/lib/cjs/decaf-model/decorators/utils.cjs +3 -2
- package/lib/cjs/decaf-model/decorators/utils.cjs.map +1 -1
- package/lib/cjs/decaf-model/index.cjs +27 -18
- package/lib/cjs/decaf-model/index.cjs.map +1 -1
- package/lib/cjs/decaf-model/types.cjs +1 -1
- package/lib/cjs/decaf-model/types.cjs.map +1 -1
- package/lib/cjs/decaf-model/utils.cjs +10 -4
- package/lib/cjs/decaf-model/utils.cjs.map +1 -1
- package/lib/cjs/decoration.cjs +1 -1
- package/lib/cjs/decoration.cjs.map +1 -1
- package/lib/cjs/decorators.cjs +93 -0
- package/lib/cjs/decorators.cjs.map +1 -0
- package/lib/cjs/events-module/DecafStreamModule.cjs +1 -1
- package/lib/cjs/events-module/DecafStreamModule.cjs.map +1 -1
- package/lib/cjs/events-module/EventsController.cjs +1 -1
- package/lib/cjs/events-module/EventsController.cjs.map +1 -1
- package/lib/cjs/events-module/constant.cjs +1 -1
- package/lib/cjs/events-module/constant.cjs.map +1 -1
- package/lib/cjs/events-module/index.cjs +1 -1
- package/lib/cjs/events-module/index.cjs.map +1 -1
- package/lib/cjs/events-module/utils.cjs +1 -1
- package/lib/cjs/events-module/utils.cjs.map +1 -1
- package/lib/cjs/factory/NestBootstraper.cjs +11 -4
- package/lib/cjs/factory/NestBootstraper.cjs.map +1 -1
- package/lib/cjs/factory/errors/cors.cjs +1 -1
- package/lib/cjs/factory/errors/cors.cjs.map +1 -1
- package/lib/cjs/factory/errors/index.cjs +1 -1
- package/lib/cjs/factory/errors/index.cjs.map +1 -1
- package/lib/cjs/factory/errors/throttling.cjs +1 -1
- package/lib/cjs/factory/errors/throttling.cjs.map +1 -1
- package/lib/cjs/factory/exceptions/DecafErrorFilter.cjs +105 -6
- package/lib/cjs/factory/exceptions/DecafErrorFilter.cjs.map +1 -1
- package/lib/cjs/factory/exceptions/decorators.cjs +4 -1
- package/lib/cjs/factory/exceptions/decorators.cjs.map +1 -1
- package/lib/cjs/factory/exceptions/index.cjs +1 -1
- package/lib/cjs/factory/exceptions/index.cjs.map +1 -1
- package/lib/cjs/factory/index.cjs +1 -1
- package/lib/cjs/factory/index.cjs.map +1 -1
- package/lib/cjs/factory/openapi/DtoBuilder.cjs +36 -5
- package/lib/cjs/factory/openapi/DtoBuilder.cjs.map +1 -1
- package/lib/cjs/factory/openapi/SwaggerBuilder.cjs +1 -1
- package/lib/cjs/factory/openapi/SwaggerBuilder.cjs.map +1 -1
- package/lib/cjs/factory/openapi/SwaggerCustomUI.cjs +1 -1
- package/lib/cjs/factory/openapi/SwaggerCustomUI.cjs.map +1 -1
- package/lib/cjs/factory/openapi/constants.cjs +1 -1
- package/lib/cjs/factory/openapi/constants.cjs.map +1 -1
- package/lib/cjs/factory/openapi/index.cjs +1 -1
- package/lib/cjs/factory/openapi/index.cjs.map +1 -1
- package/lib/cjs/index.cjs +6 -5
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/interceptors/DecafRequestHandlerInterceptor.cjs +24 -20
- package/lib/cjs/interceptors/DecafRequestHandlerInterceptor.cjs.map +1 -1
- package/lib/cjs/interceptors/index.cjs +2 -3
- package/lib/cjs/interceptors/index.cjs.map +1 -1
- package/lib/cjs/module.cjs +4 -1
- package/lib/cjs/module.cjs.map +1 -1
- package/lib/cjs/overrides/Adapter.cjs +1 -1
- package/lib/cjs/overrides/Adapter.cjs.map +1 -1
- package/lib/cjs/overrides/ModelBuilderExtensions.cjs +1 -1
- package/lib/cjs/overrides/ModelBuilderExtensions.cjs.map +1 -1
- package/lib/cjs/overrides/constants.cjs +1 -1
- package/lib/cjs/overrides/constants.cjs.map +1 -1
- package/lib/cjs/overrides/decoration.cjs +1 -1
- package/lib/cjs/overrides/decoration.cjs.map +1 -1
- package/lib/cjs/overrides/helpers.cjs +1 -1
- package/lib/cjs/overrides/helpers.cjs.map +1 -1
- package/lib/cjs/overrides/index.cjs +2 -1
- package/lib/cjs/overrides/index.cjs.map +1 -1
- package/lib/cjs/overrides/logging.cjs +5 -0
- package/lib/cjs/overrides/logging.cjs.map +1 -0
- package/lib/cjs/overrides/overrides.cjs +1 -1
- package/lib/cjs/overrides/overrides.cjs.map +1 -1
- package/lib/cjs/request/DecafAuthHandler.cjs +4 -38
- package/lib/cjs/request/DecafAuthHandler.cjs.map +1 -1
- package/lib/cjs/request/DecafHandlerExecutor.cjs +1 -1
- package/lib/cjs/request/DecafHandlerExecutor.cjs.map +1 -1
- package/lib/cjs/request/DecafRequestContext.cjs +12 -19
- package/lib/cjs/request/DecafRequestContext.cjs.map +1 -1
- package/lib/cjs/request/DecafResponseInterceptor.cjs +5 -3
- package/lib/cjs/request/DecafResponseInterceptor.cjs.map +1 -1
- package/lib/cjs/request/index.cjs +1 -1
- package/lib/cjs/request/index.cjs.map +1 -1
- package/lib/cjs/swagger-types.cjs +1 -1
- package/lib/cjs/swagger-types.cjs.map +1 -1
- package/lib/cjs/types.cjs +1 -1
- package/lib/cjs/types.cjs.map +1 -1
- package/lib/cjs/utils.cjs +1 -1
- package/lib/cjs/utils.cjs.map +1 -1
- package/lib/cjs/webhooks/DecafWebhookModule.cjs +4 -3
- package/lib/cjs/webhooks/DecafWebhookModule.cjs.map +1 -1
- package/lib/cjs/webhooks/controllers.cjs +49 -54
- package/lib/cjs/webhooks/controllers.cjs.map +1 -1
- package/lib/cjs/webhooks/index.cjs +1 -1
- package/lib/cjs/webhooks/index.cjs.map +1 -1
- package/lib/cjs/webhooks/types.cjs +1 -1
- package/lib/cjs/webhooks/types.cjs.map +1 -1
- package/lib/esm/auth/AuthInterceptor.js +84 -0
- package/lib/esm/auth/AuthInterceptor.js.map +1 -0
- package/lib/esm/auth/DecafAuthHandler.js +31 -0
- package/lib/esm/auth/DecafAuthHandler.js.map +1 -0
- package/lib/esm/auth/DecafAuthModule.js +42 -0
- package/lib/esm/auth/DecafAuthModule.js.map +1 -0
- package/lib/esm/auth/constants.js +6 -0
- package/lib/esm/auth/constants.js.map +1 -0
- package/lib/esm/auth/decorators.js +22 -0
- package/lib/esm/auth/decorators.js.map +1 -0
- package/lib/esm/auth/index.js +6 -0
- package/lib/esm/auth/index.js.map +1 -0
- package/lib/esm/cli-module.js +62 -5
- package/lib/esm/cli-module.js.map +1 -1
- package/lib/esm/constants.js +2 -3
- package/lib/esm/constants.js.map +1 -1
- package/lib/esm/controllers.js +15 -112
- package/lib/esm/controllers.js.map +1 -1
- package/lib/esm/core-module.js +6 -6
- package/lib/esm/core-module.js.map +1 -1
- package/lib/esm/decaf-model/DecafModelModule.js +26 -2
- package/lib/esm/decaf-model/DecafModelModule.js.map +1 -1
- package/lib/esm/decaf-model/FromModelController.js +622 -796
- package/lib/esm/decaf-model/FromModelController.js.map +1 -1
- package/lib/esm/decaf-model/decorators/ApiOperationFromModel.js +52 -14
- package/lib/esm/decaf-model/decorators/ApiOperationFromModel.js.map +1 -1
- package/lib/esm/decaf-model/decorators/controller-config.js +25 -0
- package/lib/esm/decaf-model/decorators/controller-config.js.map +1 -0
- package/lib/esm/decaf-model/decorators/decorators.js +1 -42
- package/lib/esm/decaf-model/decorators/decorators.js.map +1 -1
- package/lib/esm/decaf-model/decorators/expose.js +9 -0
- package/lib/esm/decaf-model/decorators/expose.js.map +1 -0
- package/lib/esm/decaf-model/decorators/index.js +1 -0
- package/lib/esm/decaf-model/decorators/index.js.map +1 -1
- package/lib/esm/decaf-model/decorators/utils.js +2 -1
- package/lib/esm/decaf-model/decorators/utils.js.map +1 -1
- package/lib/esm/decaf-model/index.js +10 -3
- package/lib/esm/decaf-model/index.js.map +1 -1
- package/lib/esm/decaf-model/utils.js +9 -3
- package/lib/esm/decaf-model/utils.js.map +1 -1
- package/lib/esm/decorators.js +87 -0
- package/lib/esm/decorators.js.map +1 -0
- package/lib/esm/events-module/utils.js.map +1 -1
- package/lib/esm/factory/NestBootstraper.js +10 -3
- package/lib/esm/factory/NestBootstraper.js.map +1 -1
- package/lib/esm/factory/exceptions/DecafErrorFilter.js +105 -7
- package/lib/esm/factory/exceptions/DecafErrorFilter.js.map +1 -1
- package/lib/esm/factory/exceptions/decorators.js +3 -0
- package/lib/esm/factory/exceptions/decorators.js.map +1 -1
- package/lib/esm/factory/openapi/DtoBuilder.js +35 -4
- package/lib/esm/factory/openapi/DtoBuilder.js.map +1 -1
- package/lib/esm/index.js +5 -4
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/interceptors/DecafRequestHandlerInterceptor.js +24 -20
- package/lib/esm/interceptors/DecafRequestHandlerInterceptor.js.map +1 -1
- package/lib/esm/interceptors/index.js +1 -2
- package/lib/esm/interceptors/index.js.map +1 -1
- package/lib/esm/module.js +3 -0
- package/lib/esm/module.js.map +1 -1
- package/lib/esm/overrides/index.js +1 -0
- package/lib/esm/overrides/index.js.map +1 -1
- package/lib/esm/overrides/logging.js +2 -0
- package/lib/esm/overrides/logging.js.map +1 -0
- package/lib/esm/request/DecafAuthHandler.js +1 -35
- package/lib/esm/request/DecafAuthHandler.js.map +1 -1
- package/lib/esm/request/DecafRequestContext.js +11 -18
- package/lib/esm/request/DecafRequestContext.js.map +1 -1
- package/lib/esm/request/DecafResponseInterceptor.js +4 -2
- package/lib/esm/request/DecafResponseInterceptor.js.map +1 -1
- package/lib/esm/webhooks/DecafWebhookModule.js +2 -1
- package/lib/esm/webhooks/DecafWebhookModule.js.map +1 -1
- package/lib/esm/webhooks/controllers.js +48 -53
- package/lib/esm/webhooks/controllers.js.map +1 -1
- package/lib/types/{interceptors → auth}/AuthInterceptor.d.cts +4 -1
- package/lib/types/{interceptors → auth}/AuthInterceptor.d.mts +4 -1
- package/lib/types/auth/DecafAuthHandler.d.cts +22 -0
- package/lib/types/auth/DecafAuthHandler.d.mts +22 -0
- package/lib/types/auth/DecafAuthModule.d.cts +9 -0
- package/lib/types/auth/DecafAuthModule.d.mts +9 -0
- package/lib/types/auth/constants.d.cts +5 -0
- package/lib/types/auth/constants.d.mts +5 -0
- package/lib/types/auth/decorators.d.cts +4 -0
- package/lib/types/auth/decorators.d.mts +4 -0
- package/lib/types/auth/index.d.cts +6 -0
- package/lib/types/auth/index.d.mts +6 -0
- package/lib/types/cli-module.d.cts +1 -4
- package/lib/types/cli-module.d.mts +1 -4
- package/lib/types/constants.d.cts +2 -3
- package/lib/types/constants.d.mts +2 -3
- package/lib/types/controllers.d.cts +7 -12
- package/lib/types/controllers.d.mts +7 -12
- package/lib/types/decaf-model/DecafModelModule.d.cts +1 -0
- package/lib/types/decaf-model/DecafModelModule.d.mts +1 -0
- package/lib/types/decaf-model/FromModelController.d.cts +32 -52
- package/lib/types/decaf-model/FromModelController.d.mts +32 -52
- package/lib/types/decaf-model/decorators/ApiOperationFromModel.d.cts +0 -2
- package/lib/types/decaf-model/decorators/ApiOperationFromModel.d.mts +0 -2
- package/lib/types/decaf-model/decorators/controller-config.d.cts +18 -0
- package/lib/types/decaf-model/decorators/controller-config.d.mts +18 -0
- package/lib/types/decaf-model/decorators/decorators.d.cts +1 -29
- package/lib/types/decaf-model/decorators/decorators.d.mts +1 -29
- package/lib/types/decaf-model/decorators/expose.d.cts +1 -0
- package/lib/types/decaf-model/decorators/expose.d.mts +1 -0
- package/lib/types/decaf-model/decorators/index.d.cts +1 -0
- package/lib/types/decaf-model/decorators/index.d.mts +1 -0
- package/lib/types/decaf-model/decorators/types.d.cts +8 -21
- package/lib/types/decaf-model/decorators/types.d.mts +8 -21
- package/lib/types/decaf-model/index.d.cts +11 -3
- package/lib/types/decaf-model/index.d.mts +11 -3
- package/lib/types/decaf-model/utils.d.cts +2 -1
- package/lib/types/decaf-model/utils.d.mts +2 -1
- package/lib/types/decorators.d.cts +35 -0
- package/lib/types/decorators.d.mts +35 -0
- package/lib/types/factory/NestBootstraper.d.cts +5 -0
- package/lib/types/factory/NestBootstraper.d.mts +5 -0
- package/lib/types/factory/exceptions/DecafErrorFilter.d.cts +33 -1
- package/lib/types/factory/exceptions/DecafErrorFilter.d.mts +33 -1
- package/lib/types/factory/openapi/DtoBuilder.d.cts +7 -3
- package/lib/types/factory/openapi/DtoBuilder.d.mts +7 -3
- package/lib/types/index.d.cts +5 -4
- package/lib/types/index.d.mts +5 -4
- package/lib/types/interceptors/DecafRequestHandlerInterceptor.d.cts +1 -2
- package/lib/types/interceptors/DecafRequestHandlerInterceptor.d.mts +1 -2
- package/lib/types/interceptors/index.d.cts +1 -2
- package/lib/types/interceptors/index.d.mts +1 -2
- package/lib/types/overrides/Adapter.d.cts +1 -1
- package/lib/types/overrides/Adapter.d.mts +1 -1
- package/lib/types/overrides/index.d.cts +1 -0
- package/lib/types/overrides/index.d.mts +1 -0
- package/lib/types/overrides/logging.d.cts +1 -0
- package/lib/types/overrides/logging.d.mts +1 -0
- package/lib/types/request/DecafAuthHandler.d.cts +1 -10
- package/lib/types/request/DecafAuthHandler.d.mts +1 -10
- package/lib/types/request/DecafRequestContext.d.cts +5 -5
- package/lib/types/request/DecafRequestContext.d.mts +5 -5
- package/lib/types/types.d.cts +12 -24
- package/lib/types/types.d.mts +12 -24
- package/package.json +12 -13
- package/lib/cjs/interceptors/AuthInterceptor.cjs +0 -52
- package/lib/cjs/interceptors/AuthInterceptor.cjs.map +0 -1
- package/lib/cjs/interceptors/context.cjs +0 -18
- package/lib/cjs/interceptors/context.cjs.map +0 -1
- package/lib/cjs/migrations/index.cjs +0 -35
- package/lib/cjs/migrations/index.cjs.map +0 -1
- package/lib/cjs/migrations/migration-module.cjs +0 -60
- package/lib/cjs/migrations/migration-module.cjs.map +0 -1
- package/lib/cjs/migrations/migration-options.cjs +0 -4
- package/lib/cjs/migrations/migration-options.cjs.map +0 -1
- package/lib/cjs/ram/RamRequestTransformer.cjs +0 -26
- package/lib/cjs/ram/RamRequestTransformer.cjs.map +0 -1
- package/lib/cjs/ram/index.cjs.map +0 -1
- package/lib/esm/interceptors/AuthInterceptor.js +0 -43
- package/lib/esm/interceptors/AuthInterceptor.js.map +0 -1
- package/lib/esm/interceptors/context.js +0 -12
- package/lib/esm/interceptors/context.js.map +0 -1
- package/lib/esm/migrations/index.js +0 -18
- package/lib/esm/migrations/index.js.map +0 -1
- package/lib/esm/migrations/migration-module.js +0 -52
- package/lib/esm/migrations/migration-module.js.map +0 -1
- package/lib/esm/migrations/migration-options.js +0 -7
- package/lib/esm/migrations/migration-options.js.map +0 -1
- package/lib/esm/ram/RamRequestTransformer.js +0 -21
- package/lib/esm/ram/RamRequestTransformer.js.map +0 -1
- package/lib/esm/ram/index.js +0 -2
- package/lib/esm/ram/index.js.map +0 -1
- package/lib/types/interceptors/context.d.cts +0 -6
- package/lib/types/interceptors/context.d.mts +0 -6
- package/lib/types/migrations/index.d.cts +0 -17
- package/lib/types/migrations/index.d.mts +0 -17
- package/lib/types/migrations/migration-module.d.cts +0 -15
- package/lib/types/migrations/migration-module.d.mts +0 -15
- package/lib/types/migrations/migration-options.d.cts +0 -11
- package/lib/types/migrations/migration-options.d.mts +0 -11
- package/lib/types/ram/RamRequestTransformer.d.cts +0 -5
- package/lib/types/ram/RamRequestTransformer.d.mts +0 -5
- package/lib/types/ram/index.d.cts +0 -1
- package/lib/types/ram/index.d.mts +0 -1
|
@@ -15,9 +15,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
15
15
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function")
|
|
16
16
|
return Reflect.metadata(k, v);
|
|
17
17
|
};
|
|
18
|
-
var
|
|
19
|
-
return function (target, key) { decorator(target, key, paramIndex); };
|
|
20
|
-
};
|
|
18
|
+
var DynamicModelController_1;
|
|
21
19
|
const common_1 = require("@nestjs/common");
|
|
22
20
|
const swagger_1 = require("@nestjs/swagger");
|
|
23
21
|
const core_1 = require("@decaf-ts/core");
|
|
@@ -26,64 +24,24 @@ const logging_1 = require("@decaf-ts/logging");
|
|
|
26
24
|
const db_decorators_1 = require("@decaf-ts/db-decorators");
|
|
27
25
|
const decoration_1 = require("@decaf-ts/decoration");
|
|
28
26
|
const index_js_1 = require("./decorators/index.cjs");
|
|
27
|
+
const utils_js_1 = require("./decorators/utils.cjs");
|
|
29
28
|
const index_js_2 = require("./../request/index.cjs");
|
|
30
29
|
const constants_js_1 = require("./../constants.cjs");
|
|
31
|
-
const
|
|
32
|
-
const decorators_js_1 = require("
|
|
30
|
+
const constants_js_2 = require("./../auth/constants.cjs");
|
|
31
|
+
const decorators_js_1 = require("./../auth/decorators.cjs");
|
|
33
32
|
const controllers_js_1 = require("./../controllers.cjs");
|
|
34
33
|
const DtoBuilder_js_1 = require("./../factory/openapi/DtoBuilder.cjs");
|
|
35
34
|
require("./../overrides/index.cjs");
|
|
36
|
-
|
|
37
|
-
* @description
|
|
38
|
-
* Factory and utilities for generating dynamic NestJS controllers from Decaf {@link Model} definitions.
|
|
39
|
-
*
|
|
40
|
-
* @summary
|
|
41
|
-
* The `FromModelController` class provides the infrastructure necessary to automatically generate
|
|
42
|
-
* strongly-typed CRUD controllers based on a given {@link ModelConstructor}. It inspects metadata from
|
|
43
|
-
* the model, derives route paths, parameters, and generates a dynamic controller class at runtime with
|
|
44
|
-
* full support for querying, creation, update, and deletion of model entities through a {@link Repo}.
|
|
45
|
-
*
|
|
46
|
-
* @template T The {@link Model} type associated with the generated controller.
|
|
47
|
-
*
|
|
48
|
-
* @param ModelClazz The model class to generate the controller from.
|
|
49
|
-
*
|
|
50
|
-
* @class FromModelController
|
|
51
|
-
*
|
|
52
|
-
* @example
|
|
53
|
-
* ```ts
|
|
54
|
-
* // Given a Decaf Model:
|
|
55
|
-
* class User extends Model<User> {
|
|
56
|
-
* id!: string;
|
|
57
|
-
* name!: string;
|
|
58
|
-
* }
|
|
59
|
-
*
|
|
60
|
-
* // Register controller:
|
|
61
|
-
* const UserController = FromModelController.create(User);
|
|
62
|
-
*
|
|
63
|
-
* // NestJS will expose:
|
|
64
|
-
* // POST /user
|
|
65
|
-
* // GET /user/:id
|
|
66
|
-
* // GET /user/query/:method
|
|
67
|
-
* // PUT /user/:id
|
|
68
|
-
* // DELETE /user/:id
|
|
69
|
-
* ```
|
|
70
|
-
*
|
|
71
|
-
* @mermaid
|
|
72
|
-
* sequenceDiagram
|
|
73
|
-
* participant Client
|
|
74
|
-
* participant Controller
|
|
75
|
-
* participant Repo
|
|
76
|
-
* participant DB
|
|
77
|
-
*
|
|
78
|
-
* Client->>Controller: HTTP Request
|
|
79
|
-
* Controller->>Repo: Resolve repository for Model
|
|
80
|
-
* Repo->>DB: Execute DB operation
|
|
81
|
-
* DB-->>Repo: DB Result
|
|
82
|
-
* Repo-->>Controller: Model Instance(s)
|
|
83
|
-
* Controller-->>Client: JSON Response
|
|
84
|
-
*/
|
|
35
|
+
const server_1 = require("@decaf-ts/for-http/server");
|
|
85
36
|
class FromModelController {
|
|
86
37
|
static { this.log = logging_1.Logging.for(FromModelController.name); }
|
|
38
|
+
static toDecafError(error, fallbackMessage) {
|
|
39
|
+
if (error instanceof db_decorators_1.BaseError)
|
|
40
|
+
return error;
|
|
41
|
+
return new db_decorators_1.InternalError(error instanceof Error
|
|
42
|
+
? `${fallbackMessage}: ${error.message}`
|
|
43
|
+
: fallbackMessage);
|
|
44
|
+
}
|
|
87
45
|
static getPersistence(ModelClazz) {
|
|
88
46
|
try {
|
|
89
47
|
return core_1.Service.get(ModelClazz);
|
|
@@ -91,756 +49,537 @@ class FromModelController {
|
|
|
91
49
|
catch (e) {
|
|
92
50
|
try {
|
|
93
51
|
return core_1.ModelService.getService(ModelClazz);
|
|
94
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
95
52
|
}
|
|
96
|
-
catch (
|
|
53
|
+
catch (e2) {
|
|
97
54
|
return core_1.Repository.forModel(ModelClazz);
|
|
98
55
|
}
|
|
99
56
|
}
|
|
100
57
|
}
|
|
101
58
|
static createQueryRoutesFromRepository(persistence, prefix = core_1.PersistenceKeys.QUERY) {
|
|
102
|
-
const log = FromModelController.log.for(FromModelController.createQueryRoutesFromRepository);
|
|
103
59
|
const repo = persistence instanceof core_1.ModelService ? persistence.repo : persistence;
|
|
104
|
-
const ModelConstr = repo.class;
|
|
105
60
|
const queryMethods = decoration_1.Metadata.get(repo.constructor, decoration_1.Metadata.key(core_1.PersistenceKeys.QUERY)) ?? {};
|
|
106
61
|
const routeMethods = decoration_1.Metadata.get(persistence.constructor, decoration_1.Metadata.key(constants_js_1.DECAF_ROUTE)) ?? {};
|
|
107
|
-
// create base class
|
|
108
62
|
class QueryController extends controllers_js_1.DecafModelController {
|
|
109
63
|
get class() {
|
|
110
|
-
throw new
|
|
64
|
+
throw new db_decorators_1.InternalError("Method not implemented.");
|
|
111
65
|
}
|
|
112
66
|
constructor(clientContext, name) {
|
|
113
67
|
super(clientContext, name);
|
|
114
68
|
}
|
|
115
69
|
}
|
|
116
70
|
for (const [methodName, params] of Object.entries(routeMethods)) {
|
|
117
|
-
// regex to trim slashes from start and end
|
|
118
71
|
const routePath = [params.path.replace(/^\/+|\/+$/g, "")]
|
|
119
72
|
.filter((segment) => segment && segment.trim())
|
|
120
73
|
.join("/");
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (descriptor) {
|
|
130
|
-
const decorators = (0, utils_js_1.getApiDecorators)(methodName, routePath, params.httpMethod);
|
|
131
|
-
(0, utils_js_1.applyApiDecorators)(QueryController, methodName, descriptor, decorators);
|
|
132
|
-
}
|
|
74
|
+
const handler = FromModelController.createComplexQueryHandler(methodName);
|
|
75
|
+
FromModelController.defineMethod(QueryController, methodName, handler);
|
|
76
|
+
const httpDecorator = (0, utils_js_1.HttpVerbToDecorator)(params.httpMethod)(routePath || undefined);
|
|
77
|
+
const decorators = FromModelController.getQueryDecorators(methodName, routePath, params.httpMethod);
|
|
78
|
+
FromModelController.applyDecorators(QueryController, methodName, [
|
|
79
|
+
httpDecorator,
|
|
80
|
+
...decorators,
|
|
81
|
+
]);
|
|
133
82
|
}
|
|
134
83
|
for (const [methodName, objValues] of Object.entries(queryMethods)) {
|
|
135
84
|
const fields = objValues.fields ?? [];
|
|
136
85
|
const routePath = [prefix, methodName, ...fields.map((f) => `:${f}`)]
|
|
137
86
|
.filter((segment) => segment && segment.trim())
|
|
138
87
|
.join("/");
|
|
139
|
-
const handler =
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
88
|
+
const handler = FromModelController.createComplexQueryHandler(methodName);
|
|
89
|
+
FromModelController.defineMethod(QueryController, methodName, handler);
|
|
90
|
+
const httpDecorator = (0, utils_js_1.HttpVerbToDecorator)("GET")(routePath || undefined);
|
|
91
|
+
const decorators = FromModelController.getQueryDecorators(methodName, routePath, "GET", true);
|
|
92
|
+
FromModelController.applyDecorators(QueryController, methodName, [
|
|
93
|
+
httpDecorator,
|
|
94
|
+
...decorators,
|
|
95
|
+
]);
|
|
145
96
|
}
|
|
146
97
|
return QueryController;
|
|
147
98
|
}
|
|
148
|
-
static create(ModelConstr) {
|
|
99
|
+
static create(ModelConstr, moduleConfigOverrides, globalDefaults) {
|
|
149
100
|
const log = FromModelController.log.for(FromModelController.create);
|
|
150
101
|
const tableName = decorator_validation_1.Model.tableName(ModelConstr);
|
|
151
102
|
const routePath = (0, logging_1.toKebabCase)(tableName);
|
|
152
103
|
const modelClazzName = ModelConstr.name;
|
|
153
104
|
const persistence = FromModelController.getPersistence(ModelConstr);
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const
|
|
158
|
-
);
|
|
159
|
-
|
|
105
|
+
// When persistence is a ModelService, the @query/@route metadata lives on
|
|
106
|
+
// the underlying repository class (custom repo), not on ModelService itself.
|
|
107
|
+
// Pass the repo to the factory so addComplexQueries() can discover them.
|
|
108
|
+
const factoryPersistence = persistence instanceof core_1.ModelService ? persistence.repo : persistence;
|
|
109
|
+
const decoratorConfig = decoration_1.Metadata.get(ModelConstr, decoration_1.Metadata.key(constants_js_1.DECAF_CONTROLLER_CONFIG));
|
|
110
|
+
const moduleOverride = moduleConfigOverrides?.[ModelConstr.name];
|
|
111
|
+
const mergedConfig = {
|
|
112
|
+
...(globalDefaults || {}),
|
|
113
|
+
...(decoratorConfig || {}),
|
|
114
|
+
...(moduleOverride || {}),
|
|
115
|
+
};
|
|
116
|
+
const FactoryController = server_1.ModelControllerFactory.create(ModelConstr, factoryPersistence, mergedConfig);
|
|
117
|
+
const factoryRoutes = FactoryController.__routes__;
|
|
118
|
+
const { getPK, apiProperties, path: pkPath, } = FromModelController.getRouteParametersFromModel(ModelConstr);
|
|
119
|
+
log.debug(`Creating controller for model: ${modelClazzName} with ${factoryRoutes?.length ?? 0} factory routes`);
|
|
120
|
+
const authConfig = mergedConfig.auth;
|
|
121
|
+
function applyClassAuth(target) {
|
|
122
|
+
if (authConfig?.public) {
|
|
123
|
+
(0, decorators_js_1.Public)()(target);
|
|
124
|
+
}
|
|
125
|
+
else if (authConfig?.roles?.length) {
|
|
126
|
+
(0, decorators_js_1.RequireRoles)(...authConfig.roles)(target);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
(0, decorators_js_1.Auth)(ModelConstr)(target);
|
|
130
|
+
}
|
|
131
|
+
if (authConfig?.skipModelRoles) {
|
|
132
|
+
(0, common_1.SetMetadata)(constants_js_2.SKIP_MODEL_ROLES_KEY, true)(target);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
let DynamicModelController = DynamicModelController_1 = class DynamicModelController extends controllers_js_1.DecafModelController {
|
|
160
136
|
static get class() {
|
|
161
137
|
return ModelConstr;
|
|
162
138
|
}
|
|
163
139
|
get class() {
|
|
164
140
|
return ModelConstr;
|
|
165
|
-
// return DynamicModelController.class;
|
|
166
141
|
}
|
|
167
142
|
constructor(clientContext) {
|
|
168
|
-
super(clientContext);
|
|
143
|
+
super(clientContext, DynamicModelController_1.name);
|
|
169
144
|
this.pk = decorator_validation_1.Model.pk(ModelConstr);
|
|
170
145
|
log.info(`Registering dynamic controller for model: ${this.class.name} route: /${routePath}`);
|
|
171
146
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
return
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const {
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
async page(value, details) {
|
|
205
|
-
const { ctx } = (await this.logCtx([], core_1.PreparedStatementKeys.PAGE, true)).for(this.page);
|
|
206
|
-
const { direction = core_1.OrderDirection.ASC, limit, offset, bookmark, } = details;
|
|
207
|
-
const ref = {
|
|
208
|
-
offset: offset ?? 1,
|
|
209
|
-
limit: limit ?? 10,
|
|
210
|
-
bookmark,
|
|
211
|
-
};
|
|
212
|
-
return (0, utils_js_1.resolvePersistenceMethod)(this.persistence(ctx), this.page.name, value, direction, ref, ctx);
|
|
213
|
-
}
|
|
214
|
-
async findOneBy(key, value) {
|
|
215
|
-
const { ctx } = (await this.logCtx([], core_1.PreparedStatementKeys.FIND_ONE_BY, true)).for(this.findOneBy);
|
|
216
|
-
return this.persistence(ctx).findOneBy(key, value, ctx);
|
|
217
|
-
}
|
|
218
|
-
async findBy(key, value, details) {
|
|
219
|
-
const { ctx } = (await this.logCtx([], core_1.PreparedStatementKeys.FIND_BY, true)).for(this.findBy);
|
|
220
|
-
return this.persistence(ctx)
|
|
221
|
-
.for(ctx.toOverrides())
|
|
222
|
-
.findBy(key, value, ctx);
|
|
223
|
-
}
|
|
224
|
-
async statement(name, args, details) {
|
|
225
|
-
const { ctx } = (await this.logCtx([], core_1.PersistenceKeys.STATEMENT, true)).for(this.statement);
|
|
226
|
-
const { direction, offset, limit, bookmark } = details;
|
|
227
|
-
args = args.map((a) => (typeof a === "string" ? parseInt(a) : a) || a);
|
|
228
|
-
const pathDirection = args.length > 1 ? args[1] : undefined;
|
|
229
|
-
const resolvedDirection = (direction ?? pathDirection);
|
|
230
|
-
if (resolvedDirection && args.length > 1)
|
|
231
|
-
args[1] = resolvedDirection;
|
|
232
|
-
switch (name) {
|
|
233
|
-
case core_1.PreparedStatementKeys.FIND:
|
|
234
|
-
case core_1.PreparedStatementKeys.FIND_BY:
|
|
235
|
-
break;
|
|
236
|
-
case core_1.PreparedStatementKeys.LIST_BY:
|
|
237
|
-
args.push(direction);
|
|
238
|
-
break;
|
|
239
|
-
case core_1.PreparedStatementKeys.PAGE:
|
|
240
|
-
case core_1.PreparedStatementKeys.PAGE_BY:
|
|
241
|
-
args = [
|
|
242
|
-
args[0],
|
|
243
|
-
resolvedDirection,
|
|
244
|
-
{
|
|
245
|
-
limit: limit,
|
|
246
|
-
offset: offset,
|
|
247
|
-
bookmark: bookmark,
|
|
248
|
-
},
|
|
249
|
-
];
|
|
250
|
-
break;
|
|
251
|
-
case core_1.PreparedStatementKeys.FIND_ONE_BY:
|
|
252
|
-
break;
|
|
253
|
-
case core_1.PreparedStatementKeys.COUNT_OF:
|
|
254
|
-
case core_1.PreparedStatementKeys.MAX_OF:
|
|
255
|
-
case core_1.PreparedStatementKeys.MIN_OF:
|
|
256
|
-
case core_1.PreparedStatementKeys.AVG_OF:
|
|
257
|
-
case core_1.PreparedStatementKeys.SUM_OF:
|
|
258
|
-
case core_1.PreparedStatementKeys.DISTINCT_OF:
|
|
259
|
-
case core_1.PreparedStatementKeys.GROUP_OF:
|
|
260
|
-
// Aggregation methods - args[0] is the field name (if provided)
|
|
261
|
-
break;
|
|
147
|
+
};
|
|
148
|
+
DynamicModelController = DynamicModelController_1 = __decorate([
|
|
149
|
+
(0, common_1.Controller)(routePath),
|
|
150
|
+
(0, swagger_1.ApiTags)(modelClazzName),
|
|
151
|
+
(0, swagger_1.ApiExtraModels)(ModelConstr),
|
|
152
|
+
__metadata("design:paramtypes", [index_js_2.DecafRequestContext])
|
|
153
|
+
], DynamicModelController);
|
|
154
|
+
applyClassAuth(DynamicModelController);
|
|
155
|
+
if (factoryRoutes) {
|
|
156
|
+
const sortedRoutes = [...factoryRoutes].sort((a, b) => {
|
|
157
|
+
const aSegments = a.path.split("/").filter(Boolean);
|
|
158
|
+
const bSegments = b.path.split("/").filter(Boolean);
|
|
159
|
+
const aParamCount = aSegments.filter((s) => s.startsWith(":")).length;
|
|
160
|
+
const bParamCount = bSegments.filter((s) => s.startsWith(":")).length;
|
|
161
|
+
const aLiteralCount = aSegments.length - aParamCount;
|
|
162
|
+
const bLiteralCount = bSegments.length - bParamCount;
|
|
163
|
+
if (aLiteralCount !== bLiteralCount)
|
|
164
|
+
return bLiteralCount - aLiteralCount;
|
|
165
|
+
if (aParamCount !== bParamCount)
|
|
166
|
+
return aParamCount - bParamCount;
|
|
167
|
+
return 0;
|
|
168
|
+
});
|
|
169
|
+
for (const route of sortedRoutes) {
|
|
170
|
+
const methodName = route.implementation?.name || route.method.toLowerCase();
|
|
171
|
+
const registration = FromModelController.matchRoute(methodName, route, pkPath, apiProperties, getPK, ModelConstr, modelClazzName);
|
|
172
|
+
if (!registration)
|
|
173
|
+
continue;
|
|
174
|
+
const { methodName: registeredMethodName, handler, decorators, paramDecorators, } = registration;
|
|
175
|
+
const descriptor = FromModelController.defineMethod(DynamicModelController, registeredMethodName, handler);
|
|
176
|
+
if (descriptor) {
|
|
177
|
+
const httpDecorator = (0, utils_js_1.HttpVerbToDecorator)(route.method)(route.path.replace(/^\/+|\/+$/g, "") || undefined);
|
|
178
|
+
FromModelController.applyDecorators(DynamicModelController, registeredMethodName, [httpDecorator, ...decorators], paramDecorators);
|
|
262
179
|
}
|
|
263
|
-
return this.persistence(ctx).statement(name, ...args, ctx);
|
|
264
180
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
// summary: `Find maximum value of a field in ${modelClazzName}.`,
|
|
303
|
-
// })
|
|
304
|
-
// @ApiParam({ name: "field", description: "The field to find max of" })
|
|
305
|
-
// @ApiOkResponse({
|
|
306
|
-
// description: `Maximum value of the field in ${modelClazzName}.`,
|
|
307
|
-
// })
|
|
308
|
-
// async maxOf(@Param("field") field: string) {
|
|
309
|
-
// const { ctx } = (
|
|
310
|
-
// await this.logCtx([], PreparedStatementKeys.MAX_OF, true)
|
|
311
|
-
// ).for(this.maxOf);
|
|
312
|
-
// return this.persistence(ctx).statement(
|
|
313
|
-
// PreparedStatementKeys.MAX_OF,
|
|
314
|
-
// field,
|
|
315
|
-
// ctx
|
|
316
|
-
// );
|
|
317
|
-
// }
|
|
318
|
-
//
|
|
319
|
-
// @ApiOperationFromModel(ModelConstr, "GET", "minOf/:field")
|
|
320
|
-
// @ApiOperation({
|
|
321
|
-
// summary: `Find minimum value of a field in ${modelClazzName}.`,
|
|
322
|
-
// })
|
|
323
|
-
// @ApiParam({ name: "field", description: "The field to find min of" })
|
|
324
|
-
// @ApiOkResponse({
|
|
325
|
-
// description: `Minimum value of the field in ${modelClazzName}.`,
|
|
326
|
-
// })
|
|
327
|
-
// async minOf(@Param("field") field: string) {
|
|
328
|
-
// const { ctx } = (
|
|
329
|
-
// await this.logCtx([], PreparedStatementKeys.MIN_OF, true)
|
|
330
|
-
// ).for(this.minOf);
|
|
331
|
-
// return this.persistence(ctx).statement(
|
|
332
|
-
// PreparedStatementKeys.MIN_OF,
|
|
333
|
-
// field,
|
|
334
|
-
// ctx
|
|
335
|
-
// );
|
|
336
|
-
// }
|
|
337
|
-
//
|
|
338
|
-
// @ApiOperationFromModel(ModelConstr, "GET", "avgOf/:field")
|
|
339
|
-
// @ApiOperation({
|
|
340
|
-
// summary: `Calculate average of a field in ${modelClazzName}.`,
|
|
341
|
-
// })
|
|
342
|
-
// @ApiParam({
|
|
343
|
-
// name: "field",
|
|
344
|
-
// description: "The field to calculate average of",
|
|
345
|
-
// })
|
|
346
|
-
// @ApiOkResponse({
|
|
347
|
-
// description: `Average value of the field in ${modelClazzName}.`,
|
|
348
|
-
// type: Number,
|
|
349
|
-
// })
|
|
350
|
-
// async avgOf(@Param("field") field: string) {
|
|
351
|
-
// const { ctx } = (
|
|
352
|
-
// await this.logCtx([], PreparedStatementKeys.AVG_OF, true)
|
|
353
|
-
// ).for(this.avgOf);
|
|
354
|
-
// return this.persistence(ctx).statement(
|
|
355
|
-
// PreparedStatementKeys.AVG_OF,
|
|
356
|
-
// field,
|
|
357
|
-
// ctx
|
|
358
|
-
// );
|
|
359
|
-
// }
|
|
360
|
-
//
|
|
361
|
-
// @ApiOperationFromModel(ModelConstr, "GET", "sumOf/:field")
|
|
362
|
-
// @ApiOperation({
|
|
363
|
-
// summary: `Calculate sum of a field in ${modelClazzName}.`,
|
|
364
|
-
// })
|
|
365
|
-
// @ApiParam({ name: "field", description: "The field to calculate sum of" })
|
|
366
|
-
// @ApiOkResponse({
|
|
367
|
-
// description: `Sum of the field in ${modelClazzName}.`,
|
|
368
|
-
// type: Number,
|
|
369
|
-
// })
|
|
370
|
-
// async sumOf(@Param("field") field: string) {
|
|
371
|
-
// const { ctx } = (
|
|
372
|
-
// await this.logCtx([], PreparedStatementKeys.SUM_OF, true)
|
|
373
|
-
// ).for(this.sumOf);
|
|
374
|
-
// return this.persistence(ctx).statement(
|
|
375
|
-
// PreparedStatementKeys.SUM_OF,
|
|
376
|
-
// field,
|
|
377
|
-
// ctx
|
|
378
|
-
// );
|
|
379
|
-
// }
|
|
380
|
-
//
|
|
381
|
-
// @ApiOperationFromModel(ModelConstr, "GET", "distinctOf/:field")
|
|
382
|
-
// @ApiOperation({
|
|
383
|
-
// summary: `Find distinct values of a field in ${modelClazzName}.`,
|
|
384
|
-
// })
|
|
385
|
-
// @ApiParam({
|
|
386
|
-
// name: "field",
|
|
387
|
-
// description: "The field to find distinct values of",
|
|
388
|
-
// })
|
|
389
|
-
// @ApiOkResponse({
|
|
390
|
-
// description: `Distinct values of the field in ${modelClazzName}.`,
|
|
391
|
-
// type: [String],
|
|
392
|
-
// })
|
|
393
|
-
// async distinctOf(@Param("field") field: string) {
|
|
394
|
-
// const { ctx } = (
|
|
395
|
-
// await this.logCtx([], PreparedStatementKeys.DISTINCT_OF, true)
|
|
396
|
-
// ).for(this.distinctOf);
|
|
397
|
-
// return this.persistence(ctx).statement(
|
|
398
|
-
// PreparedStatementKeys.DISTINCT_OF,
|
|
399
|
-
// field,
|
|
400
|
-
// ctx
|
|
401
|
-
// );
|
|
402
|
-
// }
|
|
403
|
-
//
|
|
404
|
-
// @ApiOperationFromModel(ModelConstr, "GET", "groupOf/:field")
|
|
405
|
-
// @ApiOperation({
|
|
406
|
-
// summary: `Group ${modelClazzName} records by a field.`,
|
|
407
|
-
// })
|
|
408
|
-
// @ApiParam({ name: "field", description: "The field to group by" })
|
|
409
|
-
// @ApiOkResponse({
|
|
410
|
-
// description: `${modelClazzName} records grouped by the field.`,
|
|
411
|
-
// })
|
|
412
|
-
// async groupOf(@Param("field") field: string) {
|
|
413
|
-
// const { ctx } = (
|
|
414
|
-
// await this.logCtx([], PreparedStatementKeys.GROUP_OF, true)
|
|
415
|
-
// ).for(this.groupOf);
|
|
416
|
-
// return this.persistence(ctx).statement(
|
|
417
|
-
// PreparedStatementKeys.GROUP_OF,
|
|
418
|
-
// field,
|
|
419
|
-
// ctx
|
|
420
|
-
// );
|
|
421
|
-
// }
|
|
422
|
-
async createAll(data, resp) {
|
|
423
|
-
const { ctx, log } = (await this.logCtx([], db_decorators_1.BulkCrudOperationKeys.CREATE_ALL, true)).for(this.createAll);
|
|
424
|
-
log.verbose(`creating new ${modelClazzName}`);
|
|
425
|
-
let created;
|
|
426
|
-
try {
|
|
427
|
-
created = await this.persistence(ctx).createAll(data.map((d) => new ModelConstr(d)), ctx);
|
|
428
|
-
}
|
|
429
|
-
catch (e) {
|
|
430
|
-
log.error(`Failed to create new ${modelClazzName}`, e);
|
|
431
|
-
throw e;
|
|
432
|
-
}
|
|
433
|
-
log.info(`created new ${modelClazzName} with id ${created[this.pk]}`);
|
|
434
|
-
ctx.toResponse(resp);
|
|
435
|
-
return created;
|
|
181
|
+
}
|
|
182
|
+
return DynamicModelController;
|
|
183
|
+
}
|
|
184
|
+
static getRouteParametersFromModel(ModelClazz) {
|
|
185
|
+
const pk = decorator_validation_1.Model.pk(ModelClazz);
|
|
186
|
+
const composed = decoration_1.Metadata.get(ModelClazz, decoration_1.Metadata.key(db_decorators_1.DBKeys.COMPOSED, pk));
|
|
187
|
+
const composedKeys = composed?.args ?? [];
|
|
188
|
+
const uniqueKeys = Array.isArray(composedKeys) && composedKeys.length > 0
|
|
189
|
+
? Array.from(new Set([...composedKeys]))
|
|
190
|
+
: Array.from(new Set([pk]));
|
|
191
|
+
const description = decoration_1.Metadata.description(ModelClazz) ?? "";
|
|
192
|
+
const path = `:${uniqueKeys.join("/:")}`;
|
|
193
|
+
const apiProperties = uniqueKeys.map((key) => {
|
|
194
|
+
return {
|
|
195
|
+
name: key,
|
|
196
|
+
description: decoration_1.Metadata.description(ModelClazz, key),
|
|
197
|
+
required: true,
|
|
198
|
+
type: String,
|
|
199
|
+
};
|
|
200
|
+
});
|
|
201
|
+
return {
|
|
202
|
+
path,
|
|
203
|
+
description,
|
|
204
|
+
apiProperties,
|
|
205
|
+
getPK: (...params) => composed?.separator ? params.join(composed.separator) : params.join(""),
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
static routeParamDecorators(path) {
|
|
209
|
+
let paramIndex = 0;
|
|
210
|
+
return path
|
|
211
|
+
.split("/")
|
|
212
|
+
.filter(Boolean)
|
|
213
|
+
.flatMap((segment) => {
|
|
214
|
+
if (segment.startsWith(":")) {
|
|
215
|
+
return [
|
|
216
|
+
{ decorator: (0, common_1.Param)(segment.slice(1)), index: paramIndex++ },
|
|
217
|
+
];
|
|
436
218
|
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
try {
|
|
442
|
-
const persistence = this.persistence(ctx);
|
|
443
|
-
created = await persistence.create(data, ctx);
|
|
444
|
-
}
|
|
445
|
-
catch (e) {
|
|
446
|
-
log.error(`Failed to create new ${modelClazzName}`, e);
|
|
447
|
-
throw e;
|
|
448
|
-
}
|
|
449
|
-
log.info(`created new ${modelClazzName} with id ${created[this.pk]}`);
|
|
450
|
-
ctx.toResponse(resp);
|
|
451
|
-
return created;
|
|
219
|
+
if (segment.startsWith("*")) {
|
|
220
|
+
return [
|
|
221
|
+
{ decorator: (0, common_1.Param)(segment.slice(1)), index: paramIndex++ },
|
|
222
|
+
];
|
|
452
223
|
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
224
|
+
return [];
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
static defineMethod(target, methodName, handler) {
|
|
228
|
+
Object.defineProperty(target.prototype || target, methodName, {
|
|
229
|
+
value: handler,
|
|
230
|
+
writable: false,
|
|
231
|
+
configurable: true,
|
|
232
|
+
enumerable: false,
|
|
233
|
+
});
|
|
234
|
+
return Object.getOwnPropertyDescriptor(target.prototype || target, methodName);
|
|
235
|
+
}
|
|
236
|
+
static applyDecorators(target, methodName, methodDecorators, paramDecorators = []) {
|
|
237
|
+
const proto = target?.prototype ?? target;
|
|
238
|
+
const descriptor = Object.getOwnPropertyDescriptor(proto, methodName);
|
|
239
|
+
methodDecorators.forEach((d) => d(proto, methodName, descriptor));
|
|
240
|
+
paramDecorators.forEach(({ decorator, index }) => decorator(proto, methodName, index));
|
|
241
|
+
}
|
|
242
|
+
static matchRoute(methodName, route, pkPath, apiProperties, getPK, ModelConstr, modelClazzName) {
|
|
243
|
+
const { method, path } = route;
|
|
244
|
+
const normalizedPath = path.replace(/^\/+|\/+$/g, "");
|
|
245
|
+
const handler = route.implementation;
|
|
246
|
+
if (method === "POST" && normalizedPath === "") {
|
|
247
|
+
return FromModelController.createRegistration("create", handler, FromModelController.createCreateDecorators(ModelConstr, modelClazzName), [
|
|
248
|
+
{ decorator: (0, index_js_1.DecafBody)(), index: 0 },
|
|
249
|
+
{ decorator: (0, common_1.Response)({ passthrough: true }), index: 1 },
|
|
250
|
+
]);
|
|
251
|
+
}
|
|
252
|
+
if (method === "POST" && normalizedPath === "bulk") {
|
|
253
|
+
return FromModelController.createRegistration("createAll", handler, FromModelController.bulkCreateDecorators(ModelConstr, modelClazzName), [
|
|
254
|
+
{ decorator: (0, index_js_1.DecafBody)(), index: 0 },
|
|
255
|
+
{ decorator: (0, common_1.Response)({ passthrough: true }), index: 1 },
|
|
256
|
+
]);
|
|
257
|
+
}
|
|
258
|
+
if (method === "GET" && normalizedPath === "bulk") {
|
|
259
|
+
return FromModelController.createRegistration("readAll", handler, FromModelController.bulkReadDecorators(ModelConstr, modelClazzName), [{ decorator: (0, common_1.Query)("ids"), index: 0 }]);
|
|
260
|
+
}
|
|
261
|
+
if (method === "PUT" && normalizedPath === "bulk") {
|
|
262
|
+
return FromModelController.createRegistration("updateAll", handler, FromModelController.bulkUpdateDecorators(ModelConstr, modelClazzName, apiProperties), [
|
|
263
|
+
{ decorator: (0, index_js_1.DecafBody)(), index: 0 },
|
|
264
|
+
{ decorator: (0, common_1.Response)({ passthrough: true }), index: 1 },
|
|
265
|
+
]);
|
|
266
|
+
}
|
|
267
|
+
if (method === "DELETE" && normalizedPath === "bulk") {
|
|
268
|
+
return FromModelController.createRegistration("deleteAll", handler, FromModelController.bulkDeleteDecorators(ModelConstr, modelClazzName, apiProperties), [
|
|
269
|
+
{ decorator: (0, common_1.Query)("ids"), index: 0 },
|
|
270
|
+
{ decorator: (0, common_1.Response)({ passthrough: true }), index: 1 },
|
|
271
|
+
]);
|
|
272
|
+
}
|
|
273
|
+
if (method === "GET" && normalizedPath === pkPath) {
|
|
274
|
+
return FromModelController.createRegistration("read", handler, FromModelController.readDecorators(ModelConstr, modelClazzName, apiProperties, pkPath), FromModelController.routeParamDecorators(normalizedPath));
|
|
275
|
+
}
|
|
276
|
+
if (method === "PUT" && normalizedPath === pkPath) {
|
|
277
|
+
const routeParams = FromModelController.routeParamDecorators(normalizedPath);
|
|
278
|
+
return FromModelController.createRegistration("update", handler, FromModelController.updateDecorators(ModelConstr, modelClazzName, apiProperties, pkPath), [
|
|
279
|
+
{ decorator: (0, index_js_1.DecafBody)(), index: 0 },
|
|
280
|
+
...routeParams.map(({ decorator, index }) => ({
|
|
281
|
+
decorator,
|
|
282
|
+
index: index + 1,
|
|
283
|
+
})),
|
|
284
|
+
]);
|
|
285
|
+
}
|
|
286
|
+
if (method === "DELETE" && normalizedPath === pkPath) {
|
|
287
|
+
const routeParams = FromModelController.routeParamDecorators(normalizedPath);
|
|
288
|
+
return FromModelController.createRegistration("delete", handler, FromModelController.deleteDecorators(ModelConstr, modelClazzName, apiProperties, pkPath), routeParams);
|
|
289
|
+
}
|
|
290
|
+
// Composed PK fallback routes (filterEmpty) — path differs from pkPath
|
|
291
|
+
const fallbackSegments = normalizedPath.split("/").filter(Boolean);
|
|
292
|
+
const isAllParams = fallbackSegments.length > 0 &&
|
|
293
|
+
fallbackSegments.every((s) => s.startsWith(":"));
|
|
294
|
+
if (isAllParams && normalizedPath !== pkPath) {
|
|
295
|
+
const fallbackApiProps = fallbackSegments
|
|
296
|
+
.map((s) => s.slice(1))
|
|
297
|
+
.map((name) => ({
|
|
298
|
+
name,
|
|
299
|
+
description: `${name} parameter`,
|
|
300
|
+
required: true,
|
|
301
|
+
type: String,
|
|
302
|
+
}));
|
|
303
|
+
const suffix = fallbackSegments.map((s) => s.slice(1)).join("And");
|
|
304
|
+
const routeParams = FromModelController.routeParamDecorators(normalizedPath);
|
|
305
|
+
if (method === "GET") {
|
|
306
|
+
return FromModelController.createRegistration(`readBy${suffix}`, handler, FromModelController.readDecorators(ModelConstr, modelClazzName, fallbackApiProps, normalizedPath), routeParams);
|
|
468
307
|
}
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
const persistence = this.persistence(ctx);
|
|
478
|
-
read = await persistence.read(id, ctx);
|
|
479
|
-
}
|
|
480
|
-
catch (e) {
|
|
481
|
-
log.error(`Failed to read ${modelClazzName} with id ${id}`, e);
|
|
482
|
-
throw e;
|
|
483
|
-
}
|
|
484
|
-
log.info(`read ${modelClazzName} with id ${read[this.pk]}`);
|
|
485
|
-
return read;
|
|
308
|
+
if (method === "PUT") {
|
|
309
|
+
return FromModelController.createRegistration(`updateBy${suffix}`, handler, FromModelController.updateDecorators(ModelConstr, modelClazzName, fallbackApiProps, normalizedPath), [
|
|
310
|
+
{ decorator: (0, index_js_1.DecafBody)(), index: 0 },
|
|
311
|
+
...routeParams.map(({ decorator, index }) => ({
|
|
312
|
+
decorator,
|
|
313
|
+
index: index + 1,
|
|
314
|
+
})),
|
|
315
|
+
]);
|
|
486
316
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
let updated;
|
|
490
|
-
try {
|
|
491
|
-
log.info(`updating ${body.length} ${modelClazzName}`);
|
|
492
|
-
updated = await this.persistence(ctx).updateAll(body, ctx);
|
|
493
|
-
}
|
|
494
|
-
catch (e) {
|
|
495
|
-
log.error(e);
|
|
496
|
-
throw e;
|
|
497
|
-
}
|
|
498
|
-
ctx.toResponse(resp);
|
|
499
|
-
return updated;
|
|
317
|
+
if (method === "DELETE") {
|
|
318
|
+
return FromModelController.createRegistration(`deleteBy${suffix}`, handler, FromModelController.deleteDecorators(ModelConstr, modelClazzName, fallbackApiProps, normalizedPath), routeParams);
|
|
500
319
|
}
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
320
|
+
}
|
|
321
|
+
if (method === "GET" && normalizedPath === "statement/:method/*args") {
|
|
322
|
+
return FromModelController.createRegistration("statement", handler, FromModelController.statementDecorators(ModelConstr, modelClazzName), [
|
|
323
|
+
{ decorator: (0, common_1.Param)("method"), index: 0 },
|
|
324
|
+
{ decorator: (0, common_1.Param)("args"), index: 1 },
|
|
325
|
+
{ decorator: (0, index_js_1.DecafQuery)(), index: 2 },
|
|
326
|
+
]);
|
|
327
|
+
}
|
|
328
|
+
if (method === "GET" && normalizedPath === "findOneBy/:key/:value") {
|
|
329
|
+
return FromModelController.createRegistration("findOneBy", handler, FromModelController.statementShortcutDecorators(ModelConstr, modelClazzName, normalizedPath, core_1.PreparedStatementKeys.FIND_ONE_BY), FromModelController.statementShortcutParams(normalizedPath));
|
|
330
|
+
}
|
|
331
|
+
const statementRoutes = {
|
|
332
|
+
"listBy/:key": core_1.PreparedStatementKeys.LIST_BY,
|
|
333
|
+
"paginateBy/:key/:page": core_1.PreparedStatementKeys.PAGE_BY,
|
|
334
|
+
"find/:value": core_1.PreparedStatementKeys.FIND,
|
|
335
|
+
"page/:value": core_1.PreparedStatementKeys.PAGE,
|
|
336
|
+
"findOneBy/:key/:value": core_1.PreparedStatementKeys.FIND_ONE_BY,
|
|
337
|
+
"findBy/:key/:value": core_1.PreparedStatementKeys.FIND_BY,
|
|
338
|
+
"countOf/:field": core_1.PreparedStatementKeys.COUNT_OF,
|
|
339
|
+
"maxOf/:field": core_1.PreparedStatementKeys.MAX_OF,
|
|
340
|
+
"minOf/:field": core_1.PreparedStatementKeys.MIN_OF,
|
|
341
|
+
"avgOf/:field": core_1.PreparedStatementKeys.AVG_OF,
|
|
342
|
+
"sumOf/:field": core_1.PreparedStatementKeys.SUM_OF,
|
|
343
|
+
"distinctOf/:field": core_1.PreparedStatementKeys.DISTINCT_OF,
|
|
344
|
+
"groupOf/:field": core_1.PreparedStatementKeys.GROUP_OF,
|
|
345
|
+
};
|
|
346
|
+
const statementKey = statementRoutes[normalizedPath];
|
|
347
|
+
if (statementKey && method === "GET") {
|
|
348
|
+
return FromModelController.createRegistration(FromModelController.statementMethodName(normalizedPath), handler, FromModelController.statementShortcutDecorators(ModelConstr, modelClazzName, normalizedPath, statementKey), FromModelController.statementShortcutParams(normalizedPath));
|
|
349
|
+
}
|
|
350
|
+
if (method === "GET" && normalizedPath.startsWith("query/")) {
|
|
351
|
+
const queryMethod = normalizedPath.replace(/^query\//, "").split("/")[0];
|
|
352
|
+
return FromModelController.createRegistration(queryMethod, handler, FromModelController.getQueryDecorators(queryMethod, normalizedPath, "GET", true), FromModelController.complexQueryParams(normalizedPath));
|
|
353
|
+
}
|
|
354
|
+
const pathSegments = normalizedPath.split("/").filter(Boolean);
|
|
355
|
+
const knownPrefixes = new Set([
|
|
356
|
+
"listBy",
|
|
357
|
+
"findBy",
|
|
358
|
+
"findByPaginate",
|
|
359
|
+
"findOneBy",
|
|
360
|
+
"paginateBy",
|
|
361
|
+
"find",
|
|
362
|
+
"page",
|
|
363
|
+
"countOf",
|
|
364
|
+
"maxOf",
|
|
365
|
+
"minOf",
|
|
366
|
+
"avgOf",
|
|
367
|
+
"sumOf",
|
|
368
|
+
"distinctOf",
|
|
369
|
+
"groupOf",
|
|
370
|
+
"statement",
|
|
371
|
+
"bulk",
|
|
372
|
+
"query",
|
|
373
|
+
]);
|
|
374
|
+
if (pathSegments.length > 0 &&
|
|
375
|
+
!normalizedPath.startsWith("query/") &&
|
|
376
|
+
!normalizedPath.startsWith("statement/") &&
|
|
377
|
+
!knownPrefixes.has(pathSegments[0])) {
|
|
378
|
+
const apiPathParams = pathSegments
|
|
379
|
+
.filter((s) => s.startsWith(":"))
|
|
380
|
+
.map((s) => s.slice(1))
|
|
381
|
+
.map((name) => ({
|
|
382
|
+
name,
|
|
383
|
+
description: `${name} parameter for the query`,
|
|
384
|
+
required: true,
|
|
385
|
+
type: String,
|
|
386
|
+
}));
|
|
387
|
+
return FromModelController.createRegistration(methodName, handler, [
|
|
388
|
+
...apiPathParams.map((p) => (0, swagger_1.ApiParam)(p)),
|
|
389
|
+
(0, swagger_1.ApiOperation)({ summary: `Retrieve records using "${methodName}".` }),
|
|
390
|
+
(0, swagger_1.ApiOkResponse)({ description: "Result successfully retrieved." }),
|
|
391
|
+
(0, swagger_1.ApiNoContentResponse)({
|
|
392
|
+
description: "No content returned by the method.",
|
|
393
|
+
}),
|
|
394
|
+
], FromModelController.routeParamDecorators(normalizedPath));
|
|
395
|
+
}
|
|
396
|
+
return undefined;
|
|
397
|
+
}
|
|
398
|
+
static createRegistration(methodName, handler, decorators, paramDecorators) {
|
|
399
|
+
return { methodName, handler, decorators, paramDecorators };
|
|
400
|
+
}
|
|
401
|
+
static statementMethodName(path) {
|
|
402
|
+
const firstSegment = path.split("/")[0];
|
|
403
|
+
return firstSegment;
|
|
404
|
+
}
|
|
405
|
+
static statementShortcutParams(path) {
|
|
406
|
+
const segments = path.split("/").filter((s) => s.startsWith(":"));
|
|
407
|
+
const params = [];
|
|
408
|
+
segments.forEach((seg, i) => {
|
|
409
|
+
const name = seg.replace(":", "");
|
|
410
|
+
params.push({ decorator: (0, common_1.Param)(name), index: i });
|
|
411
|
+
});
|
|
412
|
+
if (path.startsWith("listBy/") ||
|
|
413
|
+
path.startsWith("paginateBy/") ||
|
|
414
|
+
path.startsWith("find/") ||
|
|
415
|
+
path.startsWith("page/")) {
|
|
416
|
+
params.push({ decorator: (0, index_js_1.DecafQuery)(), index: segments.length });
|
|
417
|
+
}
|
|
418
|
+
return params;
|
|
419
|
+
}
|
|
420
|
+
static complexQueryParams(path) {
|
|
421
|
+
const segments = path.split("/").filter((s) => s.startsWith(":"));
|
|
422
|
+
const params = [];
|
|
423
|
+
segments.forEach((seg, i) => {
|
|
424
|
+
const name = seg.replace(":", "");
|
|
425
|
+
params.push({ decorator: (0, common_1.Param)(name), index: i });
|
|
426
|
+
});
|
|
427
|
+
if (path.startsWith("query/")) {
|
|
428
|
+
params.push({ decorator: (0, index_js_1.DecafQuery)(), index: segments.length });
|
|
429
|
+
}
|
|
430
|
+
return params;
|
|
431
|
+
}
|
|
432
|
+
static createComplexQueryHandler(methodName) {
|
|
433
|
+
return async function complexQuery(...args) {
|
|
434
|
+
const log = this.log?.for?.(complexQuery);
|
|
435
|
+
try {
|
|
436
|
+
if (log)
|
|
437
|
+
log.debug(`Invoking custom query "${methodName}"`);
|
|
438
|
+
const { ctx } = (await this.logCtx([], methodName, true)).for(complexQuery);
|
|
439
|
+
const persistence = this.persistence(ctx);
|
|
440
|
+
const spreadArgs = FromModelController.normalizeQueryArgs(args);
|
|
441
|
+
if (persistence?.repo &&
|
|
442
|
+
typeof persistence.repo[methodName] === "function") {
|
|
443
|
+
return persistence.repo[methodName](...spreadArgs);
|
|
515
444
|
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
throw e;
|
|
445
|
+
if (typeof persistence[methodName] === "function") {
|
|
446
|
+
return persistence[methodName](...spreadArgs);
|
|
519
447
|
}
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
}
|
|
523
|
-
async deleteAll(ids, resp) {
|
|
524
|
-
const { ctx, log } = (await this.logCtx([], db_decorators_1.BulkCrudOperationKeys.DELETE_ALL, true)).for(this.deleteAll);
|
|
525
|
-
const normalizedIds = Array.isArray(ids) ? ids : [ids];
|
|
526
|
-
let read;
|
|
527
|
-
try {
|
|
528
|
-
log.debug(`deleting ${normalizedIds.length} ${modelClazzName}: ${normalizedIds}`);
|
|
529
|
-
read = await this.persistence(ctx).deleteAll(normalizedIds, ctx);
|
|
448
|
+
if (persistence?.query && typeof persistence.query === "function") {
|
|
449
|
+
return persistence.query(methodName, ...spreadArgs);
|
|
530
450
|
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
throw e;
|
|
451
|
+
if (typeof persistence.statement === "function") {
|
|
452
|
+
return persistence.statement(methodName, ...spreadArgs, ctx);
|
|
534
453
|
}
|
|
535
|
-
|
|
536
|
-
ctx.toResponse(resp);
|
|
537
|
-
return read;
|
|
454
|
+
throw new db_decorators_1.InternalError(`Persistence method "${methodName}" not found on ${persistence?.constructor?.name}`);
|
|
538
455
|
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
throw new db_decorators_1.ValidationError(`No ${this.pk} provided`);
|
|
544
|
-
let del;
|
|
545
|
-
try {
|
|
546
|
-
log.debug(`deleting ${modelClazzName} with ${this.pk} ${id}`);
|
|
547
|
-
del = await this.persistence(ctx).delete(id, ctx);
|
|
548
|
-
}
|
|
549
|
-
catch (e) {
|
|
550
|
-
log.error(`Failed to delete ${modelClazzName} with id ${id}`, e);
|
|
551
|
-
throw e;
|
|
552
|
-
}
|
|
553
|
-
log.info(`deleted ${modelClazzName} with id ${id}`);
|
|
554
|
-
ctx.toResponse(resp);
|
|
555
|
-
return del;
|
|
456
|
+
catch (e) {
|
|
457
|
+
if (log)
|
|
458
|
+
log.error(`Custom query "${methodName}" failed`, e);
|
|
459
|
+
throw FromModelController.toDecafError(e, `Custom query "${methodName}" failed`);
|
|
556
460
|
}
|
|
557
461
|
};
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
(
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
(
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
}),
|
|
615
|
-
__param(0, (0, common_1.Param)("value")),
|
|
616
|
-
__param(1, (0, index_js_1.DecafQuery)()),
|
|
617
|
-
__metadata("design:type", Function),
|
|
618
|
-
__metadata("design:paramtypes", [String, Object]),
|
|
619
|
-
__metadata("design:returntype", Promise)
|
|
620
|
-
], DynamicModelController.prototype, "find", null);
|
|
621
|
-
__decorate([
|
|
622
|
-
(0, index_js_1.ApiOperationFromModel)(ModelConstr, "GET", "page/:value"),
|
|
623
|
-
(0, swagger_1.ApiOperation)({
|
|
624
|
-
summary: `Page ${modelClazzName} records using the default query attributes.`,
|
|
625
|
-
}),
|
|
626
|
-
(0, swagger_1.ApiParam)({
|
|
627
|
-
name: "value",
|
|
628
|
-
description: "The string to match against the default query attributes",
|
|
629
|
-
}),
|
|
630
|
-
(0, swagger_1.ApiQuery)({
|
|
631
|
-
name: "direction",
|
|
632
|
-
required: false,
|
|
633
|
-
enum: core_1.OrderDirection,
|
|
634
|
-
description: "the sort order for the paged results",
|
|
635
|
-
}),
|
|
636
|
-
(0, swagger_1.ApiQuery)({
|
|
637
|
-
name: "limit",
|
|
638
|
-
required: false,
|
|
639
|
-
description: "page size",
|
|
640
|
-
}),
|
|
641
|
-
(0, swagger_1.ApiQuery)({
|
|
642
|
-
name: "offset",
|
|
643
|
-
required: false,
|
|
644
|
-
description: "page number",
|
|
645
|
-
}),
|
|
646
|
-
(0, swagger_1.ApiQuery)({
|
|
647
|
-
name: "bookmark",
|
|
648
|
-
required: false,
|
|
649
|
-
description: "bookmark for cursor pagination",
|
|
650
|
-
}),
|
|
651
|
-
(0, swagger_1.ApiOkResponse)({
|
|
652
|
-
description: `${modelClazzName} records paged by the provided prefix.`,
|
|
653
|
-
}),
|
|
654
|
-
__param(0, (0, common_1.Param)("value")),
|
|
655
|
-
__param(1, (0, index_js_1.DecafQuery)()),
|
|
656
|
-
__metadata("design:type", Function),
|
|
657
|
-
__metadata("design:paramtypes", [String, Object]),
|
|
658
|
-
__metadata("design:returntype", Promise)
|
|
659
|
-
], DynamicModelController.prototype, "page", null);
|
|
660
|
-
__decorate([
|
|
661
|
-
(0, index_js_1.ApiOperationFromModel)(ModelConstr, "GET", "findOneBy/:key"),
|
|
662
|
-
(0, swagger_1.ApiOperation)({ summary: `Retrieve ${modelClazzName} records by query.` }),
|
|
663
|
-
(0, swagger_1.ApiParam)({ name: "key", description: "the model key to sort by" }),
|
|
664
|
-
(0, swagger_1.ApiOkResponse)({
|
|
665
|
-
description: `${modelClazzName} listed found.`,
|
|
666
|
-
}),
|
|
667
|
-
(0, swagger_1.ApiNotFoundResponse)({
|
|
668
|
-
description: `No ${modelClazzName} record matches the provided identifier.`,
|
|
669
|
-
}),
|
|
670
|
-
__param(0, (0, common_1.Param)("key")),
|
|
671
|
-
__param(1, (0, common_1.Param)("value")),
|
|
672
|
-
__metadata("design:type", Function),
|
|
673
|
-
__metadata("design:paramtypes", [String, Object]),
|
|
674
|
-
__metadata("design:returntype", Promise)
|
|
675
|
-
], DynamicModelController.prototype, "findOneBy", null);
|
|
676
|
-
__decorate([
|
|
677
|
-
(0, index_js_1.ApiOperationFromModel)(ModelConstr, "GET", "findBy/:key/:value"),
|
|
678
|
-
(0, swagger_1.ApiOperation)({ summary: `Retrieve ${modelClazzName} records by query.` }),
|
|
679
|
-
(0, swagger_1.ApiParam)({ name: "key", description: "the model key to compare" }),
|
|
680
|
-
(0, swagger_1.ApiParam)({ name: "value", description: "the value to match" }),
|
|
681
|
-
(0, swagger_1.ApiQuery)({
|
|
682
|
-
name: "direction",
|
|
683
|
-
required: true,
|
|
684
|
-
enum: core_1.OrderDirection,
|
|
685
|
-
description: "the sort order when applicable",
|
|
686
|
-
}),
|
|
687
|
-
(0, swagger_1.ApiOkResponse)({
|
|
688
|
-
description: `${modelClazzName} listed found.`,
|
|
689
|
-
}),
|
|
690
|
-
(0, swagger_1.ApiNotFoundResponse)({
|
|
691
|
-
description: `No ${modelClazzName} record matches the provided identifier.`,
|
|
692
|
-
}),
|
|
693
|
-
__param(0, (0, common_1.Param)("key")),
|
|
694
|
-
__param(1, (0, common_1.Param)("value")),
|
|
695
|
-
__param(2, (0, index_js_1.DecafQuery)()),
|
|
696
|
-
__metadata("design:type", Function),
|
|
697
|
-
__metadata("design:paramtypes", [String, Object, Object]),
|
|
698
|
-
__metadata("design:returntype", Promise)
|
|
699
|
-
], DynamicModelController.prototype, "findBy", null);
|
|
700
|
-
__decorate([
|
|
701
|
-
(0, index_js_1.ApiOperationFromModel)(ModelConstr, "GET", "statement/:method/*args"),
|
|
702
|
-
(0, swagger_1.ApiOperation)({
|
|
703
|
-
summary: `Executes a prepared statement on ${modelClazzName}.`,
|
|
704
|
-
}),
|
|
705
|
-
(0, swagger_1.ApiParam)({
|
|
706
|
-
name: "method",
|
|
707
|
-
description: "the prepared statement to execute",
|
|
708
|
-
}),
|
|
709
|
-
(0, swagger_1.ApiParam)({
|
|
710
|
-
name: "args",
|
|
711
|
-
description: "concatenated list of arguments the prepared statement can accept",
|
|
712
|
-
}),
|
|
713
|
-
(0, swagger_1.ApiQuery)({
|
|
714
|
-
name: "direction",
|
|
715
|
-
required: true,
|
|
716
|
-
enum: core_1.OrderDirection,
|
|
717
|
-
description: "the sort order when applicable",
|
|
718
|
-
}),
|
|
719
|
-
(0, swagger_1.ApiQuery)({
|
|
720
|
-
name: "limit",
|
|
721
|
-
required: true,
|
|
722
|
-
description: "limit or page size when applicable",
|
|
723
|
-
}),
|
|
724
|
-
(0, swagger_1.ApiQuery)({
|
|
725
|
-
name: "offset",
|
|
726
|
-
required: true,
|
|
727
|
-
description: "offset or bookmark when applicable",
|
|
728
|
-
}),
|
|
729
|
-
(0, swagger_1.ApiOkResponse)({
|
|
730
|
-
description: `${modelClazzName} listed found.`,
|
|
731
|
-
}),
|
|
732
|
-
(0, swagger_1.ApiNotFoundResponse)({
|
|
733
|
-
description: `No ${modelClazzName} record matches the provided identifier.`,
|
|
734
|
-
}),
|
|
735
|
-
__param(0, (0, common_1.Param)("method")),
|
|
736
|
-
__param(1, (0, common_1.Param)("args")),
|
|
737
|
-
__param(2, (0, index_js_1.DecafQuery)()),
|
|
738
|
-
__metadata("design:type", Function),
|
|
739
|
-
__metadata("design:paramtypes", [String, Array, Object]),
|
|
740
|
-
__metadata("design:returntype", Promise)
|
|
741
|
-
], DynamicModelController.prototype, "statement", null);
|
|
742
|
-
__decorate([
|
|
743
|
-
(0, index_js_1.ApiOperationFromModel)(ModelConstr, "POST", "bulk"),
|
|
462
|
+
}
|
|
463
|
+
static extractQueryArgs(args) {
|
|
464
|
+
if (args.length === 0)
|
|
465
|
+
return args;
|
|
466
|
+
const last = args[args.length - 1];
|
|
467
|
+
if (last && typeof last === "object" && !Array.isArray(last)) {
|
|
468
|
+
const queryObj = args.pop();
|
|
469
|
+
const hasDirection = queryObj.direction !== undefined;
|
|
470
|
+
const hasLimit = queryObj.limit !== undefined;
|
|
471
|
+
const hasOffset = queryObj.offset !== undefined;
|
|
472
|
+
if (!hasDirection && !hasLimit && !hasOffset)
|
|
473
|
+
return args;
|
|
474
|
+
const extras = [];
|
|
475
|
+
if (hasDirection)
|
|
476
|
+
extras.push(queryObj.direction);
|
|
477
|
+
else if (hasLimit || hasOffset)
|
|
478
|
+
extras.push(undefined);
|
|
479
|
+
if (hasLimit)
|
|
480
|
+
extras.push(queryObj.limit);
|
|
481
|
+
if (hasOffset)
|
|
482
|
+
extras.push(queryObj.offset);
|
|
483
|
+
return [...args, ...extras];
|
|
484
|
+
}
|
|
485
|
+
return args;
|
|
486
|
+
}
|
|
487
|
+
static normalizeQueryArgs(args) {
|
|
488
|
+
const normalized = FromModelController.extractQueryArgs(args);
|
|
489
|
+
if (normalized.length === 0)
|
|
490
|
+
return normalized;
|
|
491
|
+
const last = normalized[normalized.length - 1];
|
|
492
|
+
if (!last || typeof last !== "object" || Array.isArray(last)) {
|
|
493
|
+
return normalized;
|
|
494
|
+
}
|
|
495
|
+
const queryObj = last;
|
|
496
|
+
const hasQueryFields = queryObj.direction !== undefined ||
|
|
497
|
+
queryObj.limit !== undefined ||
|
|
498
|
+
queryObj.offset !== undefined ||
|
|
499
|
+
queryObj.bookmark !== undefined;
|
|
500
|
+
if (!hasQueryFields) {
|
|
501
|
+
normalized.pop();
|
|
502
|
+
return normalized;
|
|
503
|
+
}
|
|
504
|
+
normalized.pop();
|
|
505
|
+
if (queryObj.direction !== undefined)
|
|
506
|
+
normalized.push(queryObj.direction);
|
|
507
|
+
if (queryObj.limit !== undefined)
|
|
508
|
+
normalized.push(queryObj.limit);
|
|
509
|
+
if (queryObj.offset !== undefined)
|
|
510
|
+
normalized.push(queryObj.offset);
|
|
511
|
+
if (queryObj.bookmark !== undefined)
|
|
512
|
+
normalized.push(queryObj.bookmark);
|
|
513
|
+
return normalized;
|
|
514
|
+
}
|
|
515
|
+
static createCreateDecorators(ModelConstr, modelClazzName) {
|
|
516
|
+
return [
|
|
517
|
+
(0, index_js_1.ApiOperationFromModel)(ModelConstr, "POST"),
|
|
744
518
|
(0, swagger_1.ApiOperation)({ summary: `Create a new ${modelClazzName}.` }),
|
|
745
519
|
(0, swagger_1.ApiBody)({
|
|
746
520
|
description: `Payload for ${modelClazzName}`,
|
|
747
|
-
|
|
748
|
-
type: "array",
|
|
749
|
-
items: {
|
|
750
|
-
$ref: (0, swagger_1.getSchemaPath)(ModelConstr),
|
|
751
|
-
// $ref: getSchemaPath(DtoFor(OperationKeys.CREATE, ModelConstr)),
|
|
752
|
-
},
|
|
753
|
-
},
|
|
521
|
+
type: (0, DtoBuilder_js_1.DtoFor)(db_decorators_1.OperationKeys.CREATE, ModelConstr),
|
|
754
522
|
}),
|
|
755
523
|
(0, swagger_1.ApiCreatedResponse)({
|
|
756
524
|
description: `${modelClazzName} created successfully.`,
|
|
757
|
-
schema: {
|
|
758
|
-
type: "array",
|
|
759
|
-
items: {
|
|
760
|
-
$ref: (0, swagger_1.getSchemaPath)(ModelConstr),
|
|
761
|
-
},
|
|
762
|
-
},
|
|
525
|
+
schema: { $ref: (0, swagger_1.getSchemaPath)(ModelConstr) },
|
|
763
526
|
}),
|
|
764
527
|
(0, swagger_1.ApiBadRequestResponse)({ description: "Payload validation failed." }),
|
|
765
528
|
(0, swagger_1.ApiUnprocessableEntityResponse)({
|
|
766
529
|
description: "Repository rejected the provided payload.",
|
|
767
530
|
}),
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
], DynamicModelController.prototype, "createAll", null);
|
|
774
|
-
__decorate([
|
|
775
|
-
(0, index_js_1.ApiOperationFromModel)(ModelConstr, "POST"),
|
|
531
|
+
];
|
|
532
|
+
}
|
|
533
|
+
static bulkCreateDecorators(ModelConstr, modelClazzName) {
|
|
534
|
+
return [
|
|
535
|
+
(0, index_js_1.ApiOperationFromModel)(ModelConstr, "POST", "bulk"),
|
|
776
536
|
(0, swagger_1.ApiOperation)({ summary: `Create a new ${modelClazzName}.` }),
|
|
777
537
|
(0, swagger_1.ApiBody)({
|
|
778
538
|
description: `Payload for ${modelClazzName}`,
|
|
779
|
-
|
|
539
|
+
schema: {
|
|
540
|
+
type: "array",
|
|
541
|
+
items: { $ref: (0, swagger_1.getSchemaPath)(ModelConstr) },
|
|
542
|
+
},
|
|
780
543
|
}),
|
|
781
544
|
(0, swagger_1.ApiCreatedResponse)({
|
|
782
545
|
description: `${modelClazzName} created successfully.`,
|
|
783
546
|
schema: {
|
|
784
|
-
|
|
547
|
+
type: "array",
|
|
548
|
+
items: { $ref: (0, swagger_1.getSchemaPath)(ModelConstr) },
|
|
785
549
|
},
|
|
786
550
|
}),
|
|
787
551
|
(0, swagger_1.ApiBadRequestResponse)({ description: "Payload validation failed." }),
|
|
788
552
|
(0, swagger_1.ApiUnprocessableEntityResponse)({
|
|
789
553
|
description: "Repository rejected the provided payload.",
|
|
790
554
|
}),
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
__metadata("design:returntype", Promise)
|
|
796
|
-
], DynamicModelController.prototype, "create", null);
|
|
797
|
-
__decorate([
|
|
555
|
+
];
|
|
556
|
+
}
|
|
557
|
+
static bulkReadDecorators(ModelConstr, modelClazzName) {
|
|
558
|
+
return [
|
|
798
559
|
(0, index_js_1.ApiOperationFromModel)(ModelConstr, "GET", "bulk"),
|
|
799
|
-
(0, swagger_1.ApiOperation)({ summary: `Retrieve
|
|
560
|
+
(0, swagger_1.ApiOperation)({ summary: `Retrieve ${modelClazzName} records by ids.` }),
|
|
800
561
|
(0, swagger_1.ApiQuery)({ name: "ids", required: true, type: "array" }),
|
|
801
562
|
(0, swagger_1.ApiOkResponse)({
|
|
802
563
|
description: `${modelClazzName} retrieved successfully.`,
|
|
803
564
|
schema: {
|
|
804
565
|
type: "array",
|
|
805
|
-
items: {
|
|
806
|
-
$ref: (0, swagger_1.getSchemaPath)(ModelConstr),
|
|
807
|
-
},
|
|
808
|
-
},
|
|
809
|
-
}),
|
|
810
|
-
(0, swagger_1.ApiNotFoundResponse)({
|
|
811
|
-
description: `No ${modelClazzName} record matches the provided identifier.`,
|
|
812
|
-
}),
|
|
813
|
-
__param(0, (0, common_1.Query)("ids")),
|
|
814
|
-
__metadata("design:type", Function),
|
|
815
|
-
__metadata("design:paramtypes", [Array]),
|
|
816
|
-
__metadata("design:returntype", Promise)
|
|
817
|
-
], DynamicModelController.prototype, "readAll", null);
|
|
818
|
-
__decorate([
|
|
819
|
-
(0, index_js_1.ApiOperationFromModel)(ModelConstr, "GET", path),
|
|
820
|
-
(0, index_js_1.ApiParamsFromModel)(apiProperties),
|
|
821
|
-
(0, swagger_1.ApiOperation)({ summary: `Retrieve a ${modelClazzName} record by id.` }),
|
|
822
|
-
(0, swagger_1.ApiOkResponse)({
|
|
823
|
-
description: `${modelClazzName} retrieved successfully.`,
|
|
824
|
-
schema: {
|
|
825
|
-
$ref: (0, swagger_1.getSchemaPath)(ModelConstr),
|
|
566
|
+
items: { $ref: (0, swagger_1.getSchemaPath)(ModelConstr) },
|
|
826
567
|
},
|
|
827
568
|
}),
|
|
828
569
|
(0, swagger_1.ApiNotFoundResponse)({
|
|
829
570
|
description: `No ${modelClazzName} record matches the provided identifier.`,
|
|
830
571
|
}),
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
__decorate([
|
|
837
|
-
(0, index_js_1.ApiOperationFromModel)(ModelConstr, "PUT", `bulk`),
|
|
572
|
+
];
|
|
573
|
+
}
|
|
574
|
+
static bulkUpdateDecorators(ModelConstr, modelClazzName, apiProperties) {
|
|
575
|
+
return [
|
|
576
|
+
(0, index_js_1.ApiOperationFromModel)(ModelConstr, "PUT", "bulk"),
|
|
838
577
|
(0, index_js_1.ApiParamsFromModel)(apiProperties),
|
|
839
578
|
(0, swagger_1.ApiOperation)({
|
|
840
|
-
summary: `Replace
|
|
579
|
+
summary: `Replace existing ${modelClazzName} records with new payloads.`,
|
|
841
580
|
}),
|
|
842
581
|
(0, swagger_1.ApiBody)({
|
|
843
|
-
description: `Payload for
|
|
582
|
+
description: `Payload for replacing existing records of ${modelClazzName}`,
|
|
844
583
|
schema: {
|
|
845
584
|
type: "array",
|
|
846
585
|
$ref: (0, swagger_1.getSchemaPath)((0, DtoBuilder_js_1.DtoFor)(db_decorators_1.OperationKeys.UPDATE, ModelConstr)),
|
|
@@ -850,125 +589,212 @@ class FromModelController {
|
|
|
850
589
|
description: `${modelClazzName} updated successfully.`,
|
|
851
590
|
schema: {
|
|
852
591
|
type: "array",
|
|
853
|
-
items: {
|
|
854
|
-
$ref: (0, swagger_1.getSchemaPath)(ModelConstr),
|
|
855
|
-
},
|
|
592
|
+
items: { $ref: (0, swagger_1.getSchemaPath)(ModelConstr) },
|
|
856
593
|
},
|
|
857
594
|
}),
|
|
858
595
|
(0, swagger_1.ApiNotFoundResponse)({
|
|
859
596
|
description: `No ${modelClazzName} record matches the provided identifier.`,
|
|
860
597
|
}),
|
|
861
598
|
(0, swagger_1.ApiBadRequestResponse)({ description: "Payload validation failed." }),
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
(0,
|
|
599
|
+
];
|
|
600
|
+
}
|
|
601
|
+
static bulkDeleteDecorators(ModelConstr, modelClazzName, apiProperties) {
|
|
602
|
+
return [
|
|
603
|
+
(0, index_js_1.ApiOperationFromModel)(ModelConstr, "DELETE", "bulk"),
|
|
604
|
+
(0, index_js_1.ApiParamsFromModel)(apiProperties),
|
|
605
|
+
(0, swagger_1.ApiOperation)({ summary: `Delete ${modelClazzName} records by ids.` }),
|
|
606
|
+
(0, swagger_1.ApiQuery)({ name: "ids", required: true, type: "array" }),
|
|
607
|
+
(0, swagger_1.ApiOkResponse)({
|
|
608
|
+
description: `${modelClazzName} deleted successfully.`,
|
|
609
|
+
schema: {
|
|
610
|
+
type: "array",
|
|
611
|
+
items: { $ref: (0, swagger_1.getSchemaPath)(ModelConstr) },
|
|
612
|
+
},
|
|
613
|
+
}),
|
|
614
|
+
(0, swagger_1.ApiNotFoundResponse)({
|
|
615
|
+
description: `No ${modelClazzName} record matches the provided identifier.`,
|
|
616
|
+
}),
|
|
617
|
+
];
|
|
618
|
+
}
|
|
619
|
+
static readDecorators(ModelConstr, modelClazzName, apiProperties, pkPath) {
|
|
620
|
+
return [
|
|
621
|
+
(0, index_js_1.ApiOperationFromModel)(ModelConstr, "GET", pkPath),
|
|
622
|
+
(0, index_js_1.ApiParamsFromModel)(apiProperties),
|
|
623
|
+
(0, swagger_1.ApiOperation)({ summary: `Retrieve a ${modelClazzName} record by id.` }),
|
|
624
|
+
(0, swagger_1.ApiOkResponse)({
|
|
625
|
+
description: `${modelClazzName} retrieved successfully.`,
|
|
626
|
+
schema: { $ref: (0, swagger_1.getSchemaPath)(ModelConstr) },
|
|
627
|
+
}),
|
|
628
|
+
(0, swagger_1.ApiNotFoundResponse)({
|
|
629
|
+
description: `No ${modelClazzName} record matches the provided identifier.`,
|
|
630
|
+
}),
|
|
631
|
+
];
|
|
632
|
+
}
|
|
633
|
+
static updateDecorators(ModelConstr, modelClazzName, apiProperties, pkPath) {
|
|
634
|
+
return [
|
|
635
|
+
(0, index_js_1.ApiOperationFromModel)(ModelConstr, "PUT", pkPath),
|
|
870
636
|
(0, index_js_1.ApiParamsFromModel)(apiProperties),
|
|
871
637
|
(0, swagger_1.ApiOperation)({
|
|
872
638
|
summary: `Replace an existing ${modelClazzName} record with a new payload.`,
|
|
873
639
|
}),
|
|
874
640
|
(0, swagger_1.ApiBody)({
|
|
875
|
-
description: `Payload for
|
|
641
|
+
description: `Payload for replacing an existing record of ${modelClazzName}`,
|
|
876
642
|
type: (0, DtoBuilder_js_1.DtoFor)(db_decorators_1.OperationKeys.UPDATE, ModelConstr),
|
|
877
643
|
}),
|
|
878
644
|
(0, swagger_1.ApiOkResponse)({
|
|
879
645
|
description: `${modelClazzName} updated successfully.`,
|
|
880
|
-
schema: {
|
|
881
|
-
$ref: (0, swagger_1.getSchemaPath)(ModelConstr),
|
|
882
|
-
},
|
|
646
|
+
schema: { $ref: (0, swagger_1.getSchemaPath)(ModelConstr) },
|
|
883
647
|
}),
|
|
884
648
|
(0, swagger_1.ApiNotFoundResponse)({
|
|
885
649
|
description: `No ${modelClazzName} record matches the provided identifier.`,
|
|
886
650
|
}),
|
|
887
651
|
(0, swagger_1.ApiBadRequestResponse)({ description: "Payload validation failed." }),
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
__metadata("design:returntype", Promise)
|
|
894
|
-
], DynamicModelController.prototype, "update", null);
|
|
895
|
-
__decorate([
|
|
896
|
-
(0, index_js_1.ApiOperationFromModel)(ModelConstr, "DELETE", "bulk"),
|
|
652
|
+
];
|
|
653
|
+
}
|
|
654
|
+
static deleteDecorators(ModelConstr, modelClazzName, apiProperties, pkPath) {
|
|
655
|
+
return [
|
|
656
|
+
(0, index_js_1.ApiOperationFromModel)(ModelConstr, "DELETE", pkPath),
|
|
897
657
|
(0, index_js_1.ApiParamsFromModel)(apiProperties),
|
|
898
|
-
(0, swagger_1.ApiOperation)({ summary: `
|
|
899
|
-
(0, swagger_1.ApiQuery)({ name: "ids", required: true, type: "array" }),
|
|
658
|
+
(0, swagger_1.ApiOperation)({ summary: `Delete a ${modelClazzName} record by id.` }),
|
|
900
659
|
(0, swagger_1.ApiOkResponse)({
|
|
901
660
|
description: `${modelClazzName} deleted successfully.`,
|
|
902
|
-
schema: {
|
|
903
|
-
type: "array",
|
|
904
|
-
items: {
|
|
905
|
-
$ref: (0, swagger_1.getSchemaPath)(ModelConstr),
|
|
906
|
-
},
|
|
907
|
-
},
|
|
661
|
+
schema: { $ref: (0, swagger_1.getSchemaPath)(ModelConstr) },
|
|
908
662
|
}),
|
|
909
663
|
(0, swagger_1.ApiNotFoundResponse)({
|
|
910
664
|
description: `No ${modelClazzName} record matches the provided identifier.`,
|
|
911
665
|
}),
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
(0,
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
666
|
+
];
|
|
667
|
+
}
|
|
668
|
+
static statementDecorators(ModelConstr, modelClazzName) {
|
|
669
|
+
return [
|
|
670
|
+
(0, index_js_1.ApiOperationFromModel)(ModelConstr, "GET", "statement/:method/*args"),
|
|
671
|
+
(0, swagger_1.ApiOperation)({
|
|
672
|
+
summary: `Executes a prepared statement on ${modelClazzName}.`,
|
|
673
|
+
}),
|
|
674
|
+
(0, swagger_1.ApiParam)({
|
|
675
|
+
name: "method",
|
|
676
|
+
description: "the prepared statement to execute",
|
|
677
|
+
}),
|
|
678
|
+
(0, swagger_1.ApiParam)({
|
|
679
|
+
name: "args",
|
|
680
|
+
description: "concatenated list of arguments the prepared statement can accept",
|
|
681
|
+
}),
|
|
682
|
+
(0, swagger_1.ApiQuery)({
|
|
683
|
+
name: "direction",
|
|
684
|
+
required: true,
|
|
685
|
+
enum: core_1.OrderDirection,
|
|
686
|
+
description: "the sort order when applicable",
|
|
927
687
|
}),
|
|
688
|
+
(0, swagger_1.ApiQuery)({
|
|
689
|
+
name: "limit",
|
|
690
|
+
required: true,
|
|
691
|
+
description: "limit or page size when applicable",
|
|
692
|
+
}),
|
|
693
|
+
(0, swagger_1.ApiQuery)({
|
|
694
|
+
name: "offset",
|
|
695
|
+
required: true,
|
|
696
|
+
description: "offset or bookmark when applicable",
|
|
697
|
+
}),
|
|
698
|
+
(0, swagger_1.ApiOkResponse)({ description: `${modelClazzName} listed found.` }),
|
|
928
699
|
(0, swagger_1.ApiNotFoundResponse)({
|
|
929
700
|
description: `No ${modelClazzName} record matches the provided identifier.`,
|
|
930
701
|
}),
|
|
931
|
-
|
|
932
|
-
__param(1, (0, common_1.Response)({ passthrough: true })),
|
|
933
|
-
__metadata("design:type", Function),
|
|
934
|
-
__metadata("design:paramtypes", [Object, Object]),
|
|
935
|
-
__metadata("design:returntype", Promise)
|
|
936
|
-
], DynamicModelController.prototype, "delete", null);
|
|
937
|
-
DynamicModelController = __decorate([
|
|
938
|
-
(0, common_1.Controller)(routePath),
|
|
939
|
-
(0, swagger_1.ApiTags)(modelClazzName),
|
|
940
|
-
(0, swagger_1.ApiExtraModels)(ModelConstr),
|
|
941
|
-
(0, decorators_js_1.Auth)(ModelConstr),
|
|
942
|
-
__metadata("design:paramtypes", [index_js_2.DecafRequestContext])
|
|
943
|
-
], DynamicModelController);
|
|
944
|
-
return DynamicModelController;
|
|
702
|
+
];
|
|
945
703
|
}
|
|
946
|
-
static
|
|
947
|
-
const
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
const
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
name: key,
|
|
959
|
-
description: decoration_1.Metadata.description(ModelClazz, key),
|
|
960
|
-
required: true,
|
|
961
|
-
type: String,
|
|
962
|
-
};
|
|
704
|
+
static statementShortcutDecorators(ModelConstr, modelClazzName, path, statementKey) {
|
|
705
|
+
const base = [
|
|
706
|
+
(0, index_js_1.ApiOperationFromModel)(ModelConstr, "GET", path),
|
|
707
|
+
(0, swagger_1.ApiOperation)({ summary: `Retrieve ${modelClazzName} records.` }),
|
|
708
|
+
(0, swagger_1.ApiOkResponse)({
|
|
709
|
+
description: `${modelClazzName} retrieved successfully.`,
|
|
710
|
+
}),
|
|
711
|
+
];
|
|
712
|
+
const segments = path.split("/").filter((s) => s.startsWith(":"));
|
|
713
|
+
segments.forEach((seg) => {
|
|
714
|
+
const name = seg.replace(":", "");
|
|
715
|
+
base.push((0, swagger_1.ApiParam)({ name, description: `The ${name} parameter` }));
|
|
963
716
|
});
|
|
964
|
-
|
|
965
|
-
path
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
717
|
+
if (path.startsWith("listBy/") ||
|
|
718
|
+
path.startsWith("paginateBy/") ||
|
|
719
|
+
path.startsWith("find/") ||
|
|
720
|
+
path.startsWith("page/")) {
|
|
721
|
+
base.push((0, swagger_1.ApiQuery)({
|
|
722
|
+
name: "direction",
|
|
723
|
+
required: true,
|
|
724
|
+
enum: core_1.OrderDirection,
|
|
725
|
+
description: "the sort order",
|
|
726
|
+
}));
|
|
727
|
+
}
|
|
728
|
+
if (path.startsWith("paginateBy/") || path.startsWith("page/")) {
|
|
729
|
+
base.push((0, swagger_1.ApiQuery)({ name: "limit", required: false, description: "page size" }), (0, swagger_1.ApiQuery)({
|
|
730
|
+
name: "offset",
|
|
731
|
+
required: false,
|
|
732
|
+
description: "page number",
|
|
733
|
+
}), (0, swagger_1.ApiQuery)({
|
|
734
|
+
name: "bookmark",
|
|
735
|
+
required: false,
|
|
736
|
+
description: "bookmark for cursor pagination",
|
|
737
|
+
}));
|
|
738
|
+
}
|
|
739
|
+
if (path.startsWith("findOneBy/") || path.startsWith("findBy/")) {
|
|
740
|
+
base.push((0, swagger_1.ApiNotFoundResponse)({
|
|
741
|
+
description: `No ${modelClazzName} record matches the provided identifier.`,
|
|
742
|
+
}));
|
|
743
|
+
}
|
|
744
|
+
if (statementKey === core_1.PreparedStatementKeys.COUNT_OF ||
|
|
745
|
+
statementKey === core_1.PreparedStatementKeys.AVG_OF ||
|
|
746
|
+
statementKey === core_1.PreparedStatementKeys.SUM_OF) {
|
|
747
|
+
base.push((0, swagger_1.ApiOkResponse)({
|
|
748
|
+
description: `Result for ${modelClazzName}.`,
|
|
749
|
+
type: Number,
|
|
750
|
+
}));
|
|
751
|
+
}
|
|
752
|
+
if (statementKey === core_1.PreparedStatementKeys.DISTINCT_OF) {
|
|
753
|
+
base.push((0, swagger_1.ApiOkResponse)({
|
|
754
|
+
description: `Distinct values for ${modelClazzName}.`,
|
|
755
|
+
type: [String],
|
|
756
|
+
}));
|
|
757
|
+
}
|
|
758
|
+
return base;
|
|
759
|
+
}
|
|
760
|
+
static getQueryDecorators(methodName, routePath, httpVerb, includeQueryParams = false) {
|
|
761
|
+
const extractPathParams = (p) => p
|
|
762
|
+
.split("/")
|
|
763
|
+
.filter((s) => s.startsWith(":"))
|
|
764
|
+
.map((s) => s.slice(1));
|
|
765
|
+
const apiPathParams = extractPathParams(routePath).map((name) => ({
|
|
766
|
+
name,
|
|
767
|
+
description: `${name} parameter for the query`,
|
|
768
|
+
required: true,
|
|
769
|
+
type: String,
|
|
770
|
+
}));
|
|
771
|
+
const decorators = [
|
|
772
|
+
...apiPathParams.map((p) => (0, swagger_1.ApiParam)(p)),
|
|
773
|
+
(0, swagger_1.ApiOperation)({ summary: `Retrieve records using "${methodName}".` }),
|
|
774
|
+
(0, swagger_1.ApiOkResponse)({ description: "Result successfully retrieved." }),
|
|
775
|
+
(0, swagger_1.ApiNoContentResponse)({
|
|
776
|
+
description: "No content returned by the method.",
|
|
777
|
+
}),
|
|
778
|
+
];
|
|
779
|
+
if (httpVerb === "GET" && includeQueryParams) {
|
|
780
|
+
decorators.push((0, swagger_1.ApiQuery)({
|
|
781
|
+
name: "direction",
|
|
782
|
+
required: false,
|
|
783
|
+
enum: core_1.OrderDirection,
|
|
784
|
+
description: "the sort order when applicable",
|
|
785
|
+
}), (0, swagger_1.ApiQuery)({
|
|
786
|
+
name: "limit",
|
|
787
|
+
required: false,
|
|
788
|
+
description: "limit or page size",
|
|
789
|
+
}), (0, swagger_1.ApiQuery)({
|
|
790
|
+
name: "offset",
|
|
791
|
+
required: false,
|
|
792
|
+
description: "offset or bookmark",
|
|
793
|
+
}));
|
|
794
|
+
}
|
|
795
|
+
return decorators;
|
|
970
796
|
}
|
|
971
797
|
}
|
|
972
798
|
exports.FromModelController = FromModelController;
|
|
973
799
|
//# sourceMappingURL=FromModelController.js.map
|
|
974
|
-
//# sourceMappingURL=FromModelController.
|
|
800
|
+
//# sourceMappingURL=FromModelController.cjs.map
|