adorn-api 1.0.22 → 1.0.24
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/.eslintignore +3 -0
- package/.eslintrc.cjs +30 -0
- package/README.md +375 -531
- package/dist/core/express-adapter.d.ts +27 -0
- package/dist/core/express-adapter.d.ts.map +1 -0
- package/dist/core/express-adapter.js +146 -0
- package/dist/core/express-adapter.js.map +1 -0
- package/dist/core/http-error.d.ts +7 -0
- package/dist/core/http-error.d.ts.map +1 -0
- package/dist/core/http-error.js +14 -0
- package/dist/core/http-error.js.map +1 -0
- package/dist/decorators/controller.decorator.d.ts +2 -0
- package/dist/decorators/controller.decorator.d.ts.map +1 -0
- package/dist/decorators/controller.decorator.js +26 -0
- package/dist/decorators/controller.decorator.js.map +1 -0
- package/dist/decorators/create.decorator.d.ts +8 -0
- package/dist/decorators/create.decorator.d.ts.map +1 -0
- package/dist/decorators/create.decorator.js +67 -0
- package/dist/decorators/create.decorator.js.map +1 -0
- package/dist/decorators/http-method.decorator.d.ts +16 -0
- package/dist/decorators/http-method.decorator.d.ts.map +1 -0
- package/dist/decorators/http-method.decorator.js +117 -0
- package/dist/decorators/http-method.decorator.js.map +1 -0
- package/dist/decorators/http-params.d.ts +17 -0
- package/dist/decorators/http-params.d.ts.map +1 -0
- package/dist/decorators/http-params.js +26 -0
- package/dist/decorators/http-params.js.map +1 -0
- package/dist/decorators/index.d.ts +10 -5
- package/dist/decorators/index.d.ts.map +1 -1
- package/dist/decorators/index.js +14 -0
- package/dist/decorators/index.js.map +1 -0
- package/dist/decorators/list.decorator.d.ts +18 -0
- package/dist/decorators/list.decorator.d.ts.map +1 -0
- package/dist/decorators/list.decorator.js +99 -0
- package/dist/decorators/list.decorator.js.map +1 -0
- package/dist/decorators/middleware.decorator.d.ts +4 -0
- package/dist/decorators/middleware.decorator.d.ts.map +1 -0
- package/dist/decorators/middleware.decorator.js +34 -0
- package/dist/decorators/middleware.decorator.js.map +1 -0
- package/dist/decorators/response.decorator.d.ts +8 -0
- package/dist/decorators/response.decorator.d.ts.map +1 -0
- package/dist/decorators/response.decorator.js +44 -0
- package/dist/decorators/response.decorator.js.map +1 -0
- package/dist/decorators/route-options.d.ts +14 -0
- package/dist/decorators/route-options.d.ts.map +1 -0
- package/dist/decorators/route-options.js +22 -0
- package/dist/decorators/route-options.js.map +1 -0
- package/dist/decorators/schema.decorator.d.ts +82 -0
- package/dist/decorators/schema.decorator.d.ts.map +1 -0
- package/dist/decorators/schema.decorator.js +123 -0
- package/dist/decorators/schema.decorator.js.map +1 -0
- package/dist/decorators/update.decorator.d.ts +8 -0
- package/dist/decorators/update.decorator.d.ts.map +1 -0
- package/dist/decorators/update.decorator.js +63 -0
- package/dist/decorators/update.decorator.js.map +1 -0
- package/dist/index.d.ts +11 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -637
- package/dist/index.js.map +1 -1
- package/dist/metadata/metadata-storage.d.ts +38 -0
- package/dist/metadata/metadata-storage.d.ts.map +1 -0
- package/dist/metadata/metadata-storage.js +102 -0
- package/dist/metadata/metadata-storage.js.map +1 -0
- package/dist/metal-orm-integration/dto-helper.d.ts +5 -0
- package/dist/metal-orm-integration/dto-helper.d.ts.map +1 -0
- package/dist/metal-orm-integration/dto-helper.js +48 -0
- package/dist/metal-orm-integration/dto-helper.js.map +1 -0
- package/dist/metal-orm-integration/dto-response.decorator.d.ts +4 -0
- package/dist/metal-orm-integration/dto-response.decorator.d.ts.map +1 -0
- package/dist/metal-orm-integration/dto-response.decorator.js +69 -0
- package/dist/metal-orm-integration/dto-response.decorator.js.map +1 -0
- package/dist/metal-orm-integration/entity-schema-builder.d.ts +20 -0
- package/dist/metal-orm-integration/entity-schema-builder.d.ts.map +1 -0
- package/dist/metal-orm-integration/entity-schema-builder.js +356 -0
- package/dist/metal-orm-integration/entity-schema-builder.js.map +1 -0
- package/dist/metal-orm-integration/index.d.ts +5 -0
- package/dist/metal-orm-integration/index.d.ts.map +1 -0
- package/dist/metal-orm-integration/index.js +5 -0
- package/dist/metal-orm-integration/index.js.map +1 -0
- package/dist/metal-orm-integration/schema-modifier.d.ts +11 -0
- package/dist/metal-orm-integration/schema-modifier.d.ts.map +1 -0
- package/dist/metal-orm-integration/schema-modifier.js +62 -0
- package/dist/metal-orm-integration/schema-modifier.js.map +1 -0
- package/dist/openapi/index.d.ts +4 -0
- package/dist/openapi/index.d.ts.map +1 -0
- package/dist/openapi/index.js +4 -0
- package/dist/openapi/index.js.map +1 -0
- package/dist/openapi/openapi-generator.d.ts +22 -0
- package/dist/openapi/openapi-generator.d.ts.map +1 -0
- package/dist/openapi/openapi-generator.js +428 -0
- package/dist/openapi/openapi-generator.js.map +1 -0
- package/dist/openapi/swagger-ui.d.ts +11 -0
- package/dist/openapi/swagger-ui.d.ts.map +1 -0
- package/dist/openapi/swagger-ui.js +20 -0
- package/dist/openapi/swagger-ui.js.map +1 -0
- package/dist/openapi/zod-to-openapi.d.ts +4 -0
- package/dist/openapi/zod-to-openapi.d.ts.map +1 -0
- package/dist/openapi/zod-to-openapi.js +184 -0
- package/dist/openapi/zod-to-openapi.js.map +1 -0
- package/dist/types/common.d.ts +4 -0
- package/dist/types/common.d.ts.map +1 -0
- package/dist/types/common.js +2 -0
- package/dist/types/common.js.map +1 -0
- package/dist/types/controller.d.ts +14 -0
- package/dist/types/controller.d.ts.map +1 -0
- package/dist/types/controller.js +2 -0
- package/dist/types/controller.js.map +1 -0
- package/dist/types/metadata.d.ts +48 -0
- package/dist/types/metadata.d.ts.map +1 -0
- package/dist/types/metadata.js +2 -0
- package/dist/types/metadata.js.map +1 -0
- package/dist/types/openapi.d.ts +30 -0
- package/dist/types/openapi.d.ts.map +1 -0
- package/dist/types/openapi.js +2 -0
- package/dist/types/openapi.js.map +1 -0
- package/dist/validation/zod-adapter.d.ts +15 -0
- package/dist/validation/zod-adapter.d.ts.map +1 -0
- package/dist/validation/zod-adapter.js +61 -0
- package/dist/validation/zod-adapter.js.map +1 -0
- package/examples/basic/app.ts +15 -0
- package/examples/basic/index.ts +6 -0
- package/examples/basic/user.controller.ts +35 -0
- package/examples/basic/user.dtos.ts +23 -0
- package/examples/metal-orm-sqlite/app.ts +18 -0
- package/examples/metal-orm-sqlite/db.ts +90 -0
- package/examples/metal-orm-sqlite/index.ts +6 -0
- package/examples/metal-orm-sqlite/post.controller.ts +209 -0
- package/examples/metal-orm-sqlite/post.dtos.ts +78 -0
- package/examples/metal-orm-sqlite/post.entity.ts +24 -0
- package/examples/metal-orm-sqlite/user.controller.helpers.ts +305 -0
- package/examples/metal-orm-sqlite/user.controller.ts +231 -0
- package/examples/metal-orm-sqlite/user.dtos.ts +88 -0
- package/examples/metal-orm-sqlite/user.entity.ts +21 -0
- package/examples/metal-orm-sqlite-music/album.controller.ts +278 -0
- package/examples/metal-orm-sqlite-music/album.dtos.ts +85 -0
- package/examples/metal-orm-sqlite-music/album.entity.ts +28 -0
- package/examples/metal-orm-sqlite-music/app.ts +19 -0
- package/examples/metal-orm-sqlite-music/artist.controller.ts +272 -0
- package/examples/metal-orm-sqlite-music/artist.dtos.ts +68 -0
- package/examples/metal-orm-sqlite-music/artist.entity.ts +27 -0
- package/examples/metal-orm-sqlite-music/db.ts +148 -0
- package/examples/metal-orm-sqlite-music/index.ts +6 -0
- package/examples/metal-orm-sqlite-music/track.controller.ts +221 -0
- package/examples/metal-orm-sqlite-music/track.dtos.ts +82 -0
- package/examples/metal-orm-sqlite-music/track.entity.ts +27 -0
- package/examples/openapi/health.controller.ts +11 -0
- package/examples/openapi/health.dto.ts +7 -0
- package/examples/openapi/index.ts +12 -0
- package/examples/restful/app.ts +15 -0
- package/examples/restful/index.ts +9 -0
- package/examples/restful/task.controller.ts +118 -0
- package/examples/restful/task.dtos.ts +66 -0
- package/examples/restful/task.store.ts +95 -0
- package/examples/tsconfig.json +8 -0
- package/examples/utils/start-server.ts +56 -0
- package/package.json +33 -97
- package/scripts/run-example.js +29 -0
- package/src/adapter/express.ts +589 -0
- package/src/adapter/metal-orm/convention-overrides.ts +115 -0
- package/src/adapter/metal-orm/crud-dtos.ts +141 -0
- package/src/adapter/metal-orm/dto.ts +20 -0
- package/src/adapter/metal-orm/error-dtos.ts +51 -0
- package/src/adapter/metal-orm/field-builder.ts +185 -0
- package/src/adapter/metal-orm/filters.ts +52 -0
- package/src/adapter/metal-orm/index.ts +66 -0
- package/src/adapter/metal-orm/paged-dtos.ts +94 -0
- package/src/adapter/metal-orm/pagination.ts +28 -0
- package/src/adapter/metal-orm/types.ts +250 -0
- package/src/adapter/metal-orm/utils.ts +36 -0
- package/src/adapter/metal-orm.test.ts +439 -0
- package/src/core/__tests__/coerce.test.ts +39 -0
- package/src/core/__tests__/dto-compose.test.ts +68 -0
- package/src/core/__tests__/schema-builder.test.ts +82 -0
- package/src/core/coerce.ts +190 -0
- package/src/core/decorators.ts +645 -0
- package/src/core/errors.ts +55 -0
- package/src/core/metadata.ts +110 -0
- package/src/core/openapi.ts +282 -0
- package/src/core/schema-builder.ts +287 -0
- package/src/core/schema.ts +400 -0
- package/src/core/types.ts +14 -0
- package/src/e2e/http-error.e2e.test.ts +52 -0
- package/src/e2e/sqlite-metal-orm.e2e.test.ts +174 -0
- package/src/e2e/sqlite.e2e.test.ts +126 -0
- package/src/index.ts +8 -0
- package/tsconfig.eslint.json +7 -0
- package/tsconfig.json +18 -0
- package/vitest.config.ts +8 -0
- package/dist/adapter/express/auth.d.ts +0 -13
- package/dist/adapter/express/auth.d.ts.map +0 -1
- package/dist/adapter/express/bootstrap.d.ts +0 -40
- package/dist/adapter/express/bootstrap.d.ts.map +0 -1
- package/dist/adapter/express/coercion.d.ts +0 -102
- package/dist/adapter/express/coercion.d.ts.map +0 -1
- package/dist/adapter/express/index.d.ts +0 -6
- package/dist/adapter/express/index.d.ts.map +0 -1
- package/dist/adapter/express/merge.d.ts +0 -45
- package/dist/adapter/express/merge.d.ts.map +0 -1
- package/dist/adapter/express/openapi.d.ts +0 -66
- package/dist/adapter/express/openapi.d.ts.map +0 -1
- package/dist/adapter/express/router.d.ts +0 -10
- package/dist/adapter/express/router.d.ts.map +0 -1
- package/dist/adapter/express/swagger.d.ts +0 -18
- package/dist/adapter/express/swagger.d.ts.map +0 -1
- package/dist/adapter/express/types.d.ts +0 -110
- package/dist/adapter/express/types.d.ts.map +0 -1
- package/dist/adapter/express/validation.d.ts +0 -27
- package/dist/adapter/express/validation.d.ts.map +0 -1
- package/dist/cli/progress.d.ts +0 -122
- package/dist/cli/progress.d.ts.map +0 -1
- package/dist/cli.cjs +0 -4390
- package/dist/cli.cjs.map +0 -1
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -4371
- package/dist/cli.js.map +0 -1
- package/dist/compiler/analyze/index.d.ts +0 -5
- package/dist/compiler/analyze/index.d.ts.map +0 -1
- package/dist/compiler/analyze/scanControllers.d.ts +0 -88
- package/dist/compiler/analyze/scanControllers.d.ts.map +0 -1
- package/dist/compiler/cache/isStale.d.ts +0 -46
- package/dist/compiler/cache/isStale.d.ts.map +0 -1
- package/dist/compiler/cache/loadArtifacts.d.ts +0 -149
- package/dist/compiler/cache/loadArtifacts.d.ts.map +0 -1
- package/dist/compiler/cache/schema.d.ts +0 -32
- package/dist/compiler/cache/schema.d.ts.map +0 -1
- package/dist/compiler/cache/writeCache.d.ts +0 -14
- package/dist/compiler/cache/writeCache.d.ts.map +0 -1
- package/dist/compiler/gems.d.ts +0 -75
- package/dist/compiler/gems.d.ts.map +0 -1
- package/dist/compiler/generator/index.d.ts +0 -7
- package/dist/compiler/generator/index.d.ts.map +0 -1
- package/dist/compiler/generator/manifest.d.ts +0 -23
- package/dist/compiler/generator/manifest.d.ts.map +0 -1
- package/dist/compiler/generator/openapi.d.ts +0 -118
- package/dist/compiler/generator/openapi.d.ts.map +0 -1
- package/dist/compiler/graph/builder.d.ts +0 -24
- package/dist/compiler/graph/builder.d.ts.map +0 -1
- package/dist/compiler/graph/index.d.ts +0 -7
- package/dist/compiler/graph/index.d.ts.map +0 -1
- package/dist/compiler/graph/schemaGraph.d.ts +0 -67
- package/dist/compiler/graph/schemaGraph.d.ts.map +0 -1
- package/dist/compiler/graph/types.d.ts +0 -203
- package/dist/compiler/graph/types.d.ts.map +0 -1
- package/dist/compiler/index.d.ts +0 -12
- package/dist/compiler/index.d.ts.map +0 -1
- package/dist/compiler/ir/index.d.ts +0 -7
- package/dist/compiler/ir/index.d.ts.map +0 -1
- package/dist/compiler/ir/pipeline.d.ts +0 -82
- package/dist/compiler/ir/pipeline.d.ts.map +0 -1
- package/dist/compiler/ir/stages.d.ts +0 -40
- package/dist/compiler/ir/stages.d.ts.map +0 -1
- package/dist/compiler/ir/visitor.d.ts +0 -98
- package/dist/compiler/ir/visitor.d.ts.map +0 -1
- package/dist/compiler/manifest/emit.d.ts +0 -21
- package/dist/compiler/manifest/emit.d.ts.map +0 -1
- package/dist/compiler/manifest/format.d.ts +0 -119
- package/dist/compiler/manifest/format.d.ts.map +0 -1
- package/dist/compiler/manifest/index.d.ts +0 -6
- package/dist/compiler/manifest/index.d.ts.map +0 -1
- package/dist/compiler/runner/createProgram.d.ts +0 -24
- package/dist/compiler/runner/createProgram.d.ts.map +0 -1
- package/dist/compiler/runner/index.d.ts +0 -5
- package/dist/compiler/runner/index.d.ts.map +0 -1
- package/dist/compiler/schema/extractAnnotations.d.ts +0 -57
- package/dist/compiler/schema/extractAnnotations.d.ts.map +0 -1
- package/dist/compiler/schema/index.d.ts +0 -8
- package/dist/compiler/schema/index.d.ts.map +0 -1
- package/dist/compiler/schema/intersectionHandler.d.ts +0 -44
- package/dist/compiler/schema/intersectionHandler.d.ts.map +0 -1
- package/dist/compiler/schema/objectHandler.d.ts +0 -146
- package/dist/compiler/schema/objectHandler.d.ts.map +0 -1
- package/dist/compiler/schema/openapi.d.ts +0 -71
- package/dist/compiler/schema/openapi.d.ts.map +0 -1
- package/dist/compiler/schema/parameters.d.ts +0 -90
- package/dist/compiler/schema/parameters.d.ts.map +0 -1
- package/dist/compiler/schema/partitioner.d.ts +0 -85
- package/dist/compiler/schema/partitioner.d.ts.map +0 -1
- package/dist/compiler/schema/primitives.d.ts +0 -68
- package/dist/compiler/schema/primitives.d.ts.map +0 -1
- package/dist/compiler/schema/queryBuilderAnalyzer.d.ts +0 -76
- package/dist/compiler/schema/queryBuilderAnalyzer.d.ts.map +0 -1
- package/dist/compiler/schema/queryBuilderSchemaBuilder.d.ts +0 -13
- package/dist/compiler/schema/queryBuilderSchemaBuilder.d.ts.map +0 -1
- package/dist/compiler/schema/splitOpenapi.d.ts +0 -46
- package/dist/compiler/schema/splitOpenapi.d.ts.map +0 -1
- package/dist/compiler/schema/typeToJsonSchema.d.ts +0 -26
- package/dist/compiler/schema/typeToJsonSchema.d.ts.map +0 -1
- package/dist/compiler/schema/types.d.ts +0 -70
- package/dist/compiler/schema/types.d.ts.map +0 -1
- package/dist/compiler/schema/unionHandler.d.ts +0 -70
- package/dist/compiler/schema/unionHandler.d.ts.map +0 -1
- package/dist/compiler/transform/dedup.d.ts +0 -35
- package/dist/compiler/transform/dedup.d.ts.map +0 -1
- package/dist/compiler/transform/flatten.d.ts +0 -50
- package/dist/compiler/transform/flatten.d.ts.map +0 -1
- package/dist/compiler/transform/index.d.ts +0 -7
- package/dist/compiler/transform/index.d.ts.map +0 -1
- package/dist/compiler/transform/inline.d.ts +0 -46
- package/dist/compiler/transform/inline.d.ts.map +0 -1
- package/dist/compiler/validation/emitPrecompiledValidators.d.ts +0 -62
- package/dist/compiler/validation/emitPrecompiledValidators.d.ts.map +0 -1
- package/dist/compiler/validation/index.d.ts +0 -5
- package/dist/compiler/validation/index.d.ts.map +0 -1
- package/dist/decorators/Auth.d.ts +0 -22
- package/dist/decorators/Auth.d.ts.map +0 -1
- package/dist/decorators/Controller.d.ts +0 -17
- package/dist/decorators/Controller.d.ts.map +0 -1
- package/dist/decorators/Public.d.ts +0 -15
- package/dist/decorators/Public.d.ts.map +0 -1
- package/dist/decorators/Use.d.ts +0 -23
- package/dist/decorators/Use.d.ts.map +0 -1
- package/dist/decorators/methods.d.ts +0 -26
- package/dist/decorators/methods.d.ts.map +0 -1
- package/dist/express.cjs +0 -1186
- package/dist/express.cjs.map +0 -1
- package/dist/express.d.ts +0 -8
- package/dist/express.d.ts.map +0 -1
- package/dist/express.js +0 -1150
- package/dist/express.js.map +0 -1
- package/dist/http.d.ts +0 -33
- package/dist/http.d.ts.map +0 -1
- package/dist/index.cjs +0 -724
- package/dist/index.cjs.map +0 -1
- package/dist/metal/applyListQuery.d.ts +0 -100
- package/dist/metal/applyListQuery.d.ts.map +0 -1
- package/dist/metal/index.cjs +0 -278
- package/dist/metal/index.cjs.map +0 -1
- package/dist/metal/index.d.ts +0 -15
- package/dist/metal/index.d.ts.map +0 -1
- package/dist/metal/index.js +0 -243
- package/dist/metal/index.js.map +0 -1
- package/dist/metal/listQuery.d.ts +0 -26
- package/dist/metal/listQuery.d.ts.map +0 -1
- package/dist/metal/queryOptions.d.ts +0 -16
- package/dist/metal/queryOptions.d.ts.map +0 -1
- package/dist/metal/readMetalBag.d.ts +0 -69
- package/dist/metal/readMetalBag.d.ts.map +0 -1
- package/dist/metal/registerMetalEntities.d.ts +0 -26
- package/dist/metal/registerMetalEntities.d.ts.map +0 -1
- package/dist/metal/schemaFromEntity.d.ts +0 -41
- package/dist/metal/schemaFromEntity.d.ts.map +0 -1
- package/dist/metal/searchWhere.d.ts +0 -97
- package/dist/metal/searchWhere.d.ts.map +0 -1
- package/dist/metal/symbolMetadata.d.ts +0 -8
- package/dist/metal/symbolMetadata.d.ts.map +0 -1
- package/dist/runtime/auth/runtime.d.ts +0 -183
- package/dist/runtime/auth/runtime.d.ts.map +0 -1
- package/dist/runtime/metadata/bucket.d.ts +0 -2
- package/dist/runtime/metadata/bucket.d.ts.map +0 -1
- package/dist/runtime/metadata/key.d.ts +0 -2
- package/dist/runtime/metadata/key.d.ts.map +0 -1
- package/dist/runtime/metadata/read.d.ts +0 -2
- package/dist/runtime/metadata/read.d.ts.map +0 -1
- package/dist/runtime/metadata/types.d.ts +0 -95
- package/dist/runtime/metadata/types.d.ts.map +0 -1
- package/dist/runtime/polyfill.d.ts +0 -2
- package/dist/runtime/polyfill.d.ts.map +0 -1
- package/dist/runtime/upload.d.ts +0 -44
- package/dist/runtime/upload.d.ts.map +0 -1
- package/dist/runtime/validation/ajv.d.ts +0 -120
- package/dist/runtime/validation/ajv.d.ts.map +0 -1
- package/dist/runtime/validation/index.d.ts +0 -11
- package/dist/runtime/validation/index.d.ts.map +0 -1
- package/dist/schema/decorators.d.ts +0 -37
- package/dist/schema/decorators.d.ts.map +0 -1
- package/dist/schema/index.cjs +0 -214
- package/dist/schema/index.cjs.map +0 -1
- package/dist/schema/index.d.ts +0 -2
- package/dist/schema/index.d.ts.map +0 -1
- package/dist/schema/index.js +0 -163
- package/dist/schema/index.js.map +0 -1
- package/dist/scripts/adorn-example.cjs +0 -404
- package/dist/scripts/adorn-example.cjs.map +0 -1
- package/dist/utils/operationId.d.ts +0 -2
- package/dist/utils/operationId.d.ts.map +0 -1
- package/dist/utils/path.d.ts +0 -2
- package/dist/utils/path.d.ts.map +0 -1
- package/dist/utils/port.d.ts +0 -9
- package/dist/utils/port.d.ts.map +0 -1
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { Dto } from "../../core/decorators";
|
|
2
|
+
import type { DtoConstructor } from "../../core/types";
|
|
3
|
+
import { MetalDto } from "./dto";
|
|
4
|
+
import type {
|
|
5
|
+
MetalCrudDtoOptions,
|
|
6
|
+
MetalCrudDtoClassOptions,
|
|
7
|
+
MetalCrudDtoClasses,
|
|
8
|
+
MetalCrudDtoDecorators,
|
|
9
|
+
NestedCreateDtoOptions
|
|
10
|
+
} from "./types";
|
|
11
|
+
|
|
12
|
+
export function createMetalCrudDtos(
|
|
13
|
+
target: any,
|
|
14
|
+
options: MetalCrudDtoOptions = {}
|
|
15
|
+
): MetalCrudDtoDecorators {
|
|
16
|
+
const mutationExclude = options.mutationExclude;
|
|
17
|
+
const immutable = options.immutable;
|
|
18
|
+
|
|
19
|
+
const response = buildCrudOptions(options.response, options.overrides);
|
|
20
|
+
const create = buildCrudOptions(options.create, options.overrides, {
|
|
21
|
+
mode: "create",
|
|
22
|
+
exclude: mergeStringArrays(mutationExclude, options.create?.exclude)
|
|
23
|
+
});
|
|
24
|
+
const replace = buildCrudOptions(options.replace, options.overrides, {
|
|
25
|
+
mode: "create",
|
|
26
|
+
exclude: mergeStringArrays(mutationExclude, immutable, options.replace?.exclude)
|
|
27
|
+
});
|
|
28
|
+
const update = buildCrudOptions(options.update, options.overrides, {
|
|
29
|
+
mode: "update",
|
|
30
|
+
exclude: mergeStringArrays(mutationExclude, immutable, options.update?.exclude)
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const params = buildCrudOptions(options.params, options.overrides);
|
|
34
|
+
params.include = params.include ?? options.paramsInclude ?? ["id"];
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
response: MetalDto(target, response),
|
|
38
|
+
create: MetalDto(target, create),
|
|
39
|
+
replace: MetalDto(target, replace),
|
|
40
|
+
update: MetalDto(target, update),
|
|
41
|
+
params: MetalDto(target, params)
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function createMetalCrudDtoClasses(
|
|
46
|
+
target: any,
|
|
47
|
+
options: MetalCrudDtoClassOptions = {}
|
|
48
|
+
): MetalCrudDtoClasses {
|
|
49
|
+
const { baseName, names, ...crudOptions } = options;
|
|
50
|
+
const decorators = createMetalCrudDtos(target, crudOptions);
|
|
51
|
+
const entityName = baseName ?? getTargetName(target);
|
|
52
|
+
const defaultNames: Record<keyof MetalCrudDtoDecorators, string> = {
|
|
53
|
+
response: `${entityName}Dto`,
|
|
54
|
+
create: `Create${entityName}Dto`,
|
|
55
|
+
replace: `Replace${entityName}Dto`,
|
|
56
|
+
update: `Update${entityName}Dto`,
|
|
57
|
+
params: `${entityName}ParamsDto`
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const classes: Partial<MetalCrudDtoClasses> = {};
|
|
61
|
+
for (const key of Object.keys(decorators) as Array<keyof MetalCrudDtoDecorators>) {
|
|
62
|
+
const name = names?.[key] ?? defaultNames[key];
|
|
63
|
+
classes[key] = buildDtoClass(name, decorators[key]);
|
|
64
|
+
}
|
|
65
|
+
return classes as MetalCrudDtoClasses;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function createNestedCreateDtoClass(
|
|
69
|
+
target: any,
|
|
70
|
+
overrides: Record<string, any>,
|
|
71
|
+
options: NestedCreateDtoOptions
|
|
72
|
+
): DtoConstructor {
|
|
73
|
+
const { additionalExclude, name, parentEntity, ...metalDtoOptions } = options;
|
|
74
|
+
|
|
75
|
+
const allExcludes = mergeStringArrays(
|
|
76
|
+
["id", "createdAt"],
|
|
77
|
+
additionalExclude,
|
|
78
|
+
metalDtoOptions.exclude
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
@MetalDto(target, {
|
|
82
|
+
...metalDtoOptions,
|
|
83
|
+
mode: "create",
|
|
84
|
+
exclude: allExcludes,
|
|
85
|
+
overrides
|
|
86
|
+
})
|
|
87
|
+
class NestedCreateDto {}
|
|
88
|
+
|
|
89
|
+
Object.defineProperty(NestedCreateDto, "name", { value: name, configurable: true });
|
|
90
|
+
|
|
91
|
+
return NestedCreateDto;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function buildDtoClass(name: string, decorator: (target: DtoConstructor) => void): DtoConstructor {
|
|
95
|
+
const DtoClass = class {};
|
|
96
|
+
Object.defineProperty(DtoClass, "name", { value: name, configurable: true });
|
|
97
|
+
decorator(DtoClass);
|
|
98
|
+
return DtoClass;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function getTargetName(target: any): string {
|
|
102
|
+
if (typeof target === "function" && target.name) {
|
|
103
|
+
return target.name;
|
|
104
|
+
}
|
|
105
|
+
return "Entity";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function mergeOverrides(
|
|
109
|
+
base?: Record<string, any>,
|
|
110
|
+
override?: Record<string, any>
|
|
111
|
+
): Record<string, any> | undefined {
|
|
112
|
+
if (!base && !override) {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
return { ...(base ?? {}), ...(override ?? {}) };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function mergeStringArrays(
|
|
119
|
+
...entries: Array<string[] | undefined>
|
|
120
|
+
): string[] | undefined {
|
|
121
|
+
const merged = new Set<string>();
|
|
122
|
+
for (const entry of entries) {
|
|
123
|
+
for (const value of entry ?? []) {
|
|
124
|
+
merged.add(value);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return merged.size ? Array.from(merged) : undefined;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function buildCrudOptions(
|
|
131
|
+
base: any | undefined,
|
|
132
|
+
overrides: Record<string, any> | undefined,
|
|
133
|
+
extra: any = {}
|
|
134
|
+
): any {
|
|
135
|
+
const mergedOverrides = mergeOverrides(overrides, base?.overrides);
|
|
136
|
+
const output: any = { ...base, ...extra };
|
|
137
|
+
if (mergedOverrides) {
|
|
138
|
+
output.overrides = mergedOverrides;
|
|
139
|
+
}
|
|
140
|
+
return output;
|
|
141
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { getColumnMap } from "metal-orm";
|
|
2
|
+
import type { DtoConstructor } from "../../core/types";
|
|
3
|
+
import { registerDto } from "../../core/metadata";
|
|
4
|
+
import { buildFields, type MetalDtoOptions } from "./field-builder";
|
|
5
|
+
|
|
6
|
+
export type MetalDtoTarget = Parameters<typeof getColumnMap>[0];
|
|
7
|
+
|
|
8
|
+
export function MetalDto(target: MetalDtoTarget, options: MetalDtoOptions = {}) {
|
|
9
|
+
return (value: DtoConstructor): void => {
|
|
10
|
+
const fields = buildFields(target, options);
|
|
11
|
+
registerDto(value, {
|
|
12
|
+
name: options.name ?? value.name,
|
|
13
|
+
description: options.description,
|
|
14
|
+
fields,
|
|
15
|
+
additionalProperties: options.additionalProperties
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type { MetalDtoOptions } from "./field-builder";
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Dto, Field } from "../../core/decorators";
|
|
2
|
+
import type { DtoConstructor } from "../../core/types";
|
|
3
|
+
import type { ErrorDtoOptions } from "./types";
|
|
4
|
+
import { t } from "../../core/schema";
|
|
5
|
+
|
|
6
|
+
export function createErrorDtoClass(options: ErrorDtoOptions = {}): DtoConstructor {
|
|
7
|
+
const { withDetails = true, includeTraceId = true } = options;
|
|
8
|
+
|
|
9
|
+
if (withDetails) {
|
|
10
|
+
@Dto()
|
|
11
|
+
class ErrorDetailDto {
|
|
12
|
+
@Field(t.string())
|
|
13
|
+
field!: string;
|
|
14
|
+
|
|
15
|
+
@Field(t.string())
|
|
16
|
+
message!: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@Dto()
|
|
20
|
+
class ErrorDto {
|
|
21
|
+
@Field(t.string())
|
|
22
|
+
message!: string;
|
|
23
|
+
|
|
24
|
+
@Field(t.optional(t.string()))
|
|
25
|
+
code?: string;
|
|
26
|
+
|
|
27
|
+
@Field(t.optional(t.array(t.ref(ErrorDetailDto))))
|
|
28
|
+
errors?: ErrorDetailDto[];
|
|
29
|
+
|
|
30
|
+
@Field(t.optional(t.string()))
|
|
31
|
+
traceId?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return ErrorDto;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@Dto()
|
|
38
|
+
class SimpleErrorDto {
|
|
39
|
+
@Field(t.string())
|
|
40
|
+
message!: string;
|
|
41
|
+
|
|
42
|
+
@Field(t.optional(t.string()))
|
|
43
|
+
traceId?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return SimpleErrorDto;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const StandardErrorDto = createErrorDtoClass({ withDetails: true, includeTraceId: true });
|
|
50
|
+
export const SimpleErrorDto = createErrorDtoClass({ withDetails: false, includeTraceId: true });
|
|
51
|
+
export const BasicErrorDto = createErrorDtoClass({ withDetails: false, includeTraceId: false });
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import type { ColumnDef } from "metal-orm";
|
|
2
|
+
import {
|
|
3
|
+
columnTypeToOpenApiFormat,
|
|
4
|
+
columnTypeToOpenApiType,
|
|
5
|
+
getColumnMap
|
|
6
|
+
} from "metal-orm";
|
|
7
|
+
import type { SchemaNode } from "../../core/schema";
|
|
8
|
+
import { t } from "../../core/schema";
|
|
9
|
+
import type { FieldMeta } from "../../core/metadata";
|
|
10
|
+
import type { FieldOverride } from "../../core/decorators";
|
|
11
|
+
|
|
12
|
+
export type MetalDtoMode = "response" | "create" | "update";
|
|
13
|
+
|
|
14
|
+
export interface MetalDtoOptions {
|
|
15
|
+
mode?: MetalDtoMode;
|
|
16
|
+
include?: string[];
|
|
17
|
+
exclude?: string[];
|
|
18
|
+
overrides?: Record<string, FieldOverride>;
|
|
19
|
+
description?: string;
|
|
20
|
+
name?: string;
|
|
21
|
+
additionalProperties?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function buildFields(
|
|
25
|
+
target: any,
|
|
26
|
+
options: MetalDtoOptions
|
|
27
|
+
): Record<string, FieldMeta> {
|
|
28
|
+
const columns = getColumnMap(target);
|
|
29
|
+
const include = options.include ? new Set(options.include) : undefined;
|
|
30
|
+
const exclude = options.exclude ? new Set(options.exclude) : undefined;
|
|
31
|
+
const mode = options.mode ?? "response";
|
|
32
|
+
const fields: Record<string, FieldMeta> = {};
|
|
33
|
+
|
|
34
|
+
for (const [name, col] of Object.entries(columns)) {
|
|
35
|
+
if (include && !include.has(name)) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (exclude && exclude.has(name)) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if ((mode === "create" || mode === "update") && isAutoGenerated(col)) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
fields[name] = buildFieldMeta(col, mode);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return applyOverrides(fields, options.overrides, columns);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function buildFieldMeta(col: any, mode: MetalDtoMode): FieldMeta {
|
|
51
|
+
let schema = columnToSchemaNode(col);
|
|
52
|
+
if (!col.notNull) {
|
|
53
|
+
schema = t.nullable(schema);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const optional = isOptional(col, mode);
|
|
57
|
+
const field: FieldMeta = { schema };
|
|
58
|
+
if (optional) {
|
|
59
|
+
field.optional = true;
|
|
60
|
+
}
|
|
61
|
+
if (col.comment) {
|
|
62
|
+
field.description = col.comment;
|
|
63
|
+
}
|
|
64
|
+
return field;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function isOptional(col: ColumnDef, mode: MetalDtoMode): boolean {
|
|
68
|
+
if (mode === "update") {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
if (mode === "create") {
|
|
72
|
+
if (isAutoGenerated(col)) {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
if (!col.notNull) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
if (col.default !== undefined) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
return !(col.notNull || col.primary);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function isAutoGenerated(col: ColumnDef): boolean {
|
|
87
|
+
return col.autoIncrement === true ||
|
|
88
|
+
col.generated === "always" ||
|
|
89
|
+
col.generated === "byDefault";
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function columnToSchemaNode(col: ColumnDef): SchemaNode {
|
|
93
|
+
const enumValues = getEnumValues(col);
|
|
94
|
+
if (enumValues) {
|
|
95
|
+
return t.enum(enumValues);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const openApiType = columnTypeToOpenApiType(col);
|
|
99
|
+
switch (openApiType) {
|
|
100
|
+
case "integer":
|
|
101
|
+
return t.integer();
|
|
102
|
+
case "number":
|
|
103
|
+
return t.number();
|
|
104
|
+
case "boolean":
|
|
105
|
+
return t.boolean();
|
|
106
|
+
case "string":
|
|
107
|
+
return buildStringSchema(col);
|
|
108
|
+
case "array":
|
|
109
|
+
return t.array(t.any());
|
|
110
|
+
case "object":
|
|
111
|
+
return t.object({}, { additionalProperties: true });
|
|
112
|
+
case "null":
|
|
113
|
+
return t.null();
|
|
114
|
+
default:
|
|
115
|
+
return t.any();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function buildStringSchema(col: ColumnDef): SchemaNode {
|
|
120
|
+
const format = columnTypeToOpenApiFormat(col);
|
|
121
|
+
const type = col.type.toUpperCase();
|
|
122
|
+
const length = col.args?.[0];
|
|
123
|
+
if ((type === "VARCHAR" || type === "CHAR") && typeof length === "number") {
|
|
124
|
+
return t.string({
|
|
125
|
+
format,
|
|
126
|
+
maxLength: length,
|
|
127
|
+
minLength: type === "CHAR" ? length : undefined
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return t.string({ format });
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function getEnumValues(col: ColumnDef): string[] | undefined {
|
|
134
|
+
if (col.type.toUpperCase() !== "ENUM") {
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
if (!Array.isArray(col.args) || !col.args.every((value) => typeof value === "string")) {
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
return col.args as string[];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function applyOverrides(
|
|
144
|
+
fields: Record<string, FieldMeta>,
|
|
145
|
+
overrides: Record<string, FieldOverride> | undefined,
|
|
146
|
+
columns: Record<string, ColumnDef>
|
|
147
|
+
): Record<string, FieldMeta> {
|
|
148
|
+
if (!overrides) {
|
|
149
|
+
return fields;
|
|
150
|
+
}
|
|
151
|
+
const output: Record<string, FieldMeta> = { ...fields };
|
|
152
|
+
const availableColumns = new Set(Object.keys(columns));
|
|
153
|
+
for (const [name, override] of Object.entries(overrides)) {
|
|
154
|
+
const field = output[name];
|
|
155
|
+
if (!field) {
|
|
156
|
+
if (availableColumns.has(name)) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
throw new Error(`DTO field "${name}" does not exist.`);
|
|
160
|
+
}
|
|
161
|
+
output[name] = normalizeOverride(field, override);
|
|
162
|
+
}
|
|
163
|
+
return output;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function normalizeOverride(field: FieldMeta, override: FieldOverride): FieldMeta {
|
|
167
|
+
if (isSchemaNode(override)) {
|
|
168
|
+
return {
|
|
169
|
+
schema: override,
|
|
170
|
+
optional: (override as any).optional ?? field.optional ?? (field.schema as any).optional,
|
|
171
|
+
description: field.description
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
const schema = override.schema ?? field.schema;
|
|
175
|
+
const optional = override.optional ?? (schema as any).optional ?? field.optional ?? (field.schema as any).optional;
|
|
176
|
+
return {
|
|
177
|
+
schema,
|
|
178
|
+
optional,
|
|
179
|
+
description: override.description ?? field.description
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function isSchemaNode(value: unknown): value is SchemaNode {
|
|
184
|
+
return !!value && typeof value === "object" && "kind" in (value as SchemaNode);
|
|
185
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Filter, FilterMapping, ParseFilterOptions } from "./types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Parses filter parameters from query parameters.
|
|
5
|
+
* @param query - Query parameters
|
|
6
|
+
* @param mappings - Filter field mappings
|
|
7
|
+
* @returns Parsed filter or undefined
|
|
8
|
+
*/
|
|
9
|
+
export function parseFilter<T, K extends keyof T>(
|
|
10
|
+
query: Record<string, unknown> | undefined,
|
|
11
|
+
mappings: Record<string, { field: K; operator: "equals" | "contains" | "startsWith" | "endsWith" | "gt" | "gte" | "lt" | "lte" }>
|
|
12
|
+
): Filter<T, K> | undefined {
|
|
13
|
+
if (!query) {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const filter: Filter<T, K> = {};
|
|
18
|
+
|
|
19
|
+
for (const [queryKey, value] of Object.entries(query)) {
|
|
20
|
+
const mapping = mappings[queryKey];
|
|
21
|
+
if (!mapping || value === undefined || value === null || value === "") {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const { field, operator } = mapping;
|
|
26
|
+
if (!filter[field]) {
|
|
27
|
+
filter[field] = {};
|
|
28
|
+
}
|
|
29
|
+
filter[field]![operator] = value as T[K];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return Object.keys(filter).length ? filter : undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Creates filter mappings for an entity.
|
|
37
|
+
* @param entity - Entity type
|
|
38
|
+
* @param fields - Array of field definitions
|
|
39
|
+
* @returns Filter mappings
|
|
40
|
+
*/
|
|
41
|
+
export function createFilterMappings<T extends Record<string, unknown>>(
|
|
42
|
+
entity: T,
|
|
43
|
+
fields: Array<{ queryKey: string; field: keyof T; operator?: "equals" | "contains" | "startsWith" | "endsWith" }>
|
|
44
|
+
): Record<string, { field: keyof T; operator: "equals" | "contains" | "startsWith" | "endsWith" }> {
|
|
45
|
+
const mappings: Record<string, { field: keyof T; operator: "equals" | "contains" | "startsWith" | "endsWith" }> = {};
|
|
46
|
+
|
|
47
|
+
for (const { queryKey, field, operator = "equals" } of fields) {
|
|
48
|
+
mappings[queryKey] = { field, operator };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return mappings;
|
|
52
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export {
|
|
2
|
+
MetalDto
|
|
3
|
+
} from "./dto";
|
|
4
|
+
|
|
5
|
+
export {
|
|
6
|
+
parsePagination
|
|
7
|
+
} from "./pagination";
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
parseFilter,
|
|
11
|
+
createFilterMappings
|
|
12
|
+
} from "./filters";
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
createPagedQueryDtoClass,
|
|
16
|
+
createPagedResponseDtoClass,
|
|
17
|
+
createPagedFilterQueryDtoClass
|
|
18
|
+
} from "./paged-dtos";
|
|
19
|
+
|
|
20
|
+
export {
|
|
21
|
+
createMetalCrudDtos,
|
|
22
|
+
createMetalCrudDtoClasses,
|
|
23
|
+
createNestedCreateDtoClass
|
|
24
|
+
} from "./crud-dtos";
|
|
25
|
+
|
|
26
|
+
export {
|
|
27
|
+
createMetalDtoOverrides,
|
|
28
|
+
type CreateMetalDtoOverridesOptions
|
|
29
|
+
} from "./convention-overrides";
|
|
30
|
+
|
|
31
|
+
export {
|
|
32
|
+
createErrorDtoClass,
|
|
33
|
+
StandardErrorDto,
|
|
34
|
+
SimpleErrorDto,
|
|
35
|
+
BasicErrorDto
|
|
36
|
+
} from "./error-dtos";
|
|
37
|
+
|
|
38
|
+
export {
|
|
39
|
+
withSession,
|
|
40
|
+
parseIdOrThrow
|
|
41
|
+
} from "./utils";
|
|
42
|
+
|
|
43
|
+
export type {
|
|
44
|
+
MetalDtoMode,
|
|
45
|
+
MetalDtoOptions,
|
|
46
|
+
MetalDtoTarget,
|
|
47
|
+
PaginationConfig,
|
|
48
|
+
PaginationOptions,
|
|
49
|
+
ParsedPagination,
|
|
50
|
+
Filter,
|
|
51
|
+
FilterMapping,
|
|
52
|
+
FilterFieldMapping,
|
|
53
|
+
ParseFilterOptions,
|
|
54
|
+
PagedQueryDtoOptions,
|
|
55
|
+
PagedResponseDtoOptions,
|
|
56
|
+
PagedFilterQueryDtoOptions,
|
|
57
|
+
FilterFieldDef,
|
|
58
|
+
MetalCrudDtoOptions,
|
|
59
|
+
MetalCrudDtoClassOptions,
|
|
60
|
+
MetalCrudDtoDecorators,
|
|
61
|
+
MetalCrudDtoClasses,
|
|
62
|
+
MetalCrudDtoClassNames,
|
|
63
|
+
NestedCreateDtoOptions,
|
|
64
|
+
ErrorDtoOptions,
|
|
65
|
+
CreateSessionFn
|
|
66
|
+
} from "./types";
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { Dto, Field } from "../../core/decorators";
|
|
2
|
+
import type { DtoConstructor } from "../../core/types";
|
|
3
|
+
import type { SchemaNode } from "../../core/schema";
|
|
4
|
+
import { t } from "../../core/schema";
|
|
5
|
+
import type { FieldMeta } from "../../core/metadata";
|
|
6
|
+
import { registerDto } from "../../core/metadata";
|
|
7
|
+
import type {
|
|
8
|
+
PagedQueryDtoOptions,
|
|
9
|
+
PagedResponseDtoOptions,
|
|
10
|
+
PagedFilterQueryDtoOptions
|
|
11
|
+
} from "./types";
|
|
12
|
+
|
|
13
|
+
export function createPagedQueryDtoClass(
|
|
14
|
+
options: PagedQueryDtoOptions = {}
|
|
15
|
+
): DtoConstructor {
|
|
16
|
+
const { defaultPageSize = 25, maxPageSize = 100 } = options;
|
|
17
|
+
|
|
18
|
+
@Dto({ name: options.name })
|
|
19
|
+
class PagedQueryDto {
|
|
20
|
+
@Field(t.optional(t.integer({ minimum: 1, default: 1 })))
|
|
21
|
+
page?: number;
|
|
22
|
+
|
|
23
|
+
@Field(
|
|
24
|
+
t.optional(
|
|
25
|
+
t.integer({ minimum: 1, maximum: maxPageSize, default: defaultPageSize })
|
|
26
|
+
)
|
|
27
|
+
)
|
|
28
|
+
pageSize?: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return PagedQueryDto;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function createPagedFilterQueryDtoClass(
|
|
35
|
+
options: PagedFilterQueryDtoOptions
|
|
36
|
+
): DtoConstructor {
|
|
37
|
+
const { filters, defaultPageSize = 25, maxPageSize = 100, name } = options;
|
|
38
|
+
|
|
39
|
+
const PagedFilterQueryDto = class {
|
|
40
|
+
page?: number;
|
|
41
|
+
pageSize?: number;
|
|
42
|
+
[key: string]: any;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const fields: Record<string, FieldMeta> = {
|
|
46
|
+
page: { schema: t.optional(t.integer({ minimum: 1, default: 1 })), optional: true },
|
|
47
|
+
pageSize: { schema: t.optional(t.integer({ minimum: 1, maximum: maxPageSize, default: defaultPageSize })), optional: true }
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
for (const [fieldName, def] of Object.entries(filters)) {
|
|
51
|
+
const schema = def.schema ?? t.string({ minLength: 1 });
|
|
52
|
+
fields[fieldName] = { schema: t.optional(schema), optional: true };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
registerDto(PagedFilterQueryDto, {
|
|
56
|
+
name: name ?? "PagedFilterQueryDto",
|
|
57
|
+
fields
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
return PagedFilterQueryDto as DtoConstructor;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function createPagedResponseDtoClass(
|
|
64
|
+
options: PagedResponseDtoOptions
|
|
65
|
+
): DtoConstructor {
|
|
66
|
+
const { itemDto, description, name } = options;
|
|
67
|
+
const responseDescription = description ?? "Paged response.";
|
|
68
|
+
|
|
69
|
+
@Dto({ name, description: responseDescription })
|
|
70
|
+
class PagedResponseDto {
|
|
71
|
+
@Field(t.array(t.ref(itemDto)))
|
|
72
|
+
items!: unknown[];
|
|
73
|
+
|
|
74
|
+
@Field(t.integer({ minimum: 0 }))
|
|
75
|
+
totalItems!: number;
|
|
76
|
+
|
|
77
|
+
@Field(t.integer({ minimum: 1 }))
|
|
78
|
+
page!: number;
|
|
79
|
+
|
|
80
|
+
@Field(t.integer({ minimum: 1 }))
|
|
81
|
+
pageSize!: number;
|
|
82
|
+
|
|
83
|
+
@Field(t.integer({ minimum: 1 }))
|
|
84
|
+
totalPages!: number;
|
|
85
|
+
|
|
86
|
+
@Field(t.boolean())
|
|
87
|
+
hasNextPage!: boolean;
|
|
88
|
+
|
|
89
|
+
@Field(t.boolean())
|
|
90
|
+
hasPrevPage!: boolean;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return PagedResponseDto;
|
|
94
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { PaginationConfig, PaginationOptions, ParsedPagination } from "./types";
|
|
2
|
+
import { coerce } from "../../core/coerce";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Parses pagination parameters from query parameters.
|
|
6
|
+
* @param query - Query parameters
|
|
7
|
+
* @param config - Pagination configuration
|
|
8
|
+
* @returns Parsed pagination result
|
|
9
|
+
*/
|
|
10
|
+
export function parsePagination(
|
|
11
|
+
query: Record<string, unknown>,
|
|
12
|
+
config: PaginationConfig = {}
|
|
13
|
+
): ParsedPagination {
|
|
14
|
+
const { defaultPageSize = 25, maxPageSize = 100 } = config;
|
|
15
|
+
|
|
16
|
+
const page = coerce.integer(query.page as string | number, {
|
|
17
|
+
min: 1,
|
|
18
|
+
clamp: true
|
|
19
|
+
}) ?? 1;
|
|
20
|
+
|
|
21
|
+
const pageSize = coerce.integer(query.pageSize as string | number, {
|
|
22
|
+
min: 1,
|
|
23
|
+
max: maxPageSize,
|
|
24
|
+
clamp: true
|
|
25
|
+
}) ?? defaultPageSize;
|
|
26
|
+
|
|
27
|
+
return { page, pageSize };
|
|
28
|
+
}
|