diff --git a/README.md b/README.md index c14d22687..549e01bf9 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | [@app/armory](./apps/armory/README.md) | @app/armory CI status | | [@app/policy-engine](./apps/policy-engine/README.md) | @app/policy-engine CI status | +| [@app/vault](./apps/vault/README.md) | @app/vault CI status | | [@narval/encryption](./packages/encryption/README.md) | Packages CI status | | [@narval/policy-engine-shared](./packages/policy-engine-shared/README.md) | Packages CI status | | [@narval/signature](./packages/signature/README.md) | Packages CI status | diff --git a/apps/armory/src/main.ts b/apps/armory/src/main.ts index f8bf31edb..20c966ed8 100644 --- a/apps/armory/src/main.ts +++ b/apps/armory/src/main.ts @@ -1,7 +1,7 @@ +import { withSwagger } from '@narval/nestjs-shared' import { ClassSerializerInterceptor, INestApplication, Logger, ValidationPipe } from '@nestjs/common' import { ConfigService } from '@nestjs/config' import { NestFactory, Reflector } from '@nestjs/core' -import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger' import { lastValueFrom, map, of, switchMap } from 'rxjs' import { Config } from './armory.config' import { ArmoryModule } from './armory.module' @@ -9,28 +9,6 @@ import { ApplicationExceptionFilter } from './shared/filter/application-exceptio import { HttpExceptionFilter } from './shared/filter/http-exception.filter' import { ZodExceptionFilter } from './shared/filter/zod-exception.filter' -/** - * Adds Swagger documentation to the application. - * - * @param app - The INestApplication instance. - * @returns The modified INestApplication instance. - */ -const withSwagger = (app: INestApplication): INestApplication => { - const document = SwaggerModule.createDocument( - app, - new DocumentBuilder() - .setTitle('Armory') - .setDescription('Armory is the most secure access management for web3') - .setVersion('1.0') - .build() - ) - SwaggerModule.setup('docs', app, document, { - customSiteTitle: 'Armory API' - }) - - return app -} - /** * Adds global pipes to the application. * @@ -88,7 +66,13 @@ async function bootstrap(): Promise { await lastValueFrom( of(application).pipe( - map(withSwagger), + map( + withSwagger({ + title: 'Armory', + description: 'Armory is the most secure access management for web3', + version: '1.0' + }) + ), map(withGlobalPipes), map(withGlobalInterceptors), map(withGlobalFilters(configService)), diff --git a/apps/armory/src/orchestration/orchestration.module.ts b/apps/armory/src/orchestration/orchestration.module.ts index 4e6888759..0e2703f69 100644 --- a/apps/armory/src/orchestration/orchestration.module.ts +++ b/apps/armory/src/orchestration/orchestration.module.ts @@ -5,6 +5,7 @@ import { BullModule } from '@nestjs/bull' import { ClassSerializerInterceptor, Module, ValidationPipe } from '@nestjs/common' import { ConfigModule } from '@nestjs/config' import { APP_FILTER, APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core' +import { ZodValidationPipe } from 'nestjs-zod' import { AUTHORIZATION_REQUEST_PROCESSING_QUEUE } from '../armory.constant' import { DataFeedModule } from '../data-feed/data-feed.module' import { PriceModule } from '../price/price.module' @@ -63,8 +64,13 @@ import { AuthorizationRequestProcessingProducer } from './queue/producer/authori useClass: ClassSerializerInterceptor }, { + // DEPRECATE: Use Zod generated DTOs to validate request and responses. provide: APP_PIPE, useClass: ValidationPipe + }, + { + provide: APP_PIPE, + useClass: ZodValidationPipe } ], exports: [AuthorizationRequestGateway] diff --git a/apps/policy-engine/src/engine/engine.module.ts b/apps/policy-engine/src/engine/engine.module.ts index 463ceed7c..bd411aa0c 100644 --- a/apps/policy-engine/src/engine/engine.module.ts +++ b/apps/policy-engine/src/engine/engine.module.ts @@ -3,6 +3,7 @@ import { EncryptionModule } from '@narval/encryption-module' import { HttpModule } from '@nestjs/axios' import { Module, ValidationPipe } from '@nestjs/common' import { APP_PIPE } from '@nestjs/core' +import { ZodValidationPipe } from 'nestjs-zod' import { load } from '../policy-engine.config' import { EncryptionModuleOptionFactory } from '../shared/factory/encryption-module-option.factory' import { AdminApiKeyGuard } from '../shared/guard/admin-api-key.guard' @@ -52,8 +53,13 @@ import { TenantRepository } from './persistence/repository/tenant.repository' TenantService, EvaluationService, { + // DEPRECATE: Use Zod generated DTOs to validate request and responses. provide: APP_PIPE, useClass: ValidationPipe + }, + { + provide: APP_PIPE, + useClass: ZodValidationPipe } ], exports: [EngineService, ProvisionService, BootstrapService] diff --git a/apps/policy-engine/src/main.ts b/apps/policy-engine/src/main.ts index 0b2e8d420..233194674 100644 --- a/apps/policy-engine/src/main.ts +++ b/apps/policy-engine/src/main.ts @@ -1,33 +1,13 @@ import { ConfigService } from '@narval/config-module' +import { withSwagger } from '@narval/nestjs-shared' import { INestApplication, Logger, ValidationPipe } from '@nestjs/common' import { NestFactory } from '@nestjs/core' -import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger' import { lastValueFrom, map, of, switchMap } from 'rxjs' import { Config } from './policy-engine.config' import { PolicyEngineModule } from './policy-engine.module' import { ApplicationExceptionFilter } from './shared/filter/application-exception.filter' import { HttpExceptionFilter } from './shared/filter/http-exception.filter' -/** - * Adds Swagger documentation to the application. - * - * @param app - The INestApplication instance. - * @returns The modified INestApplication instance. - */ -const withSwagger = (app: INestApplication): INestApplication => { - const document = SwaggerModule.createDocument( - app, - new DocumentBuilder() - .setTitle('Policy Engine') - .setDescription('The next generation of authorization for web3') - .setVersion('1.0') - .build() - ) - SwaggerModule.setup('docs', app, document) - - return app -} - /** * Adds global pipes to the application. * @@ -67,7 +47,13 @@ async function bootstrap() { await lastValueFrom( of(application).pipe( - map(withSwagger), + map( + withSwagger({ + title: 'Policy Engine', + description: 'The next generation of authorization for web3', + version: '1.0' + }) + ), map(withGlobalPipes), map(withGlobalFilters(configService)), switchMap((app) => app.listen(port)) diff --git a/apps/policy-engine/src/policy-engine.module.ts b/apps/policy-engine/src/policy-engine.module.ts index e2f624fed..75cf2a95d 100644 --- a/apps/policy-engine/src/policy-engine.module.ts +++ b/apps/policy-engine/src/policy-engine.module.ts @@ -2,6 +2,7 @@ import { ConfigModule, ConfigService } from '@narval/config-module' import { EncryptionModule } from '@narval/encryption-module' import { Module, OnApplicationBootstrap, ValidationPipe } from '@nestjs/common' import { APP_PIPE } from '@nestjs/core' +import { ZodValidationPipe } from 'nestjs-zod' import { BootstrapService } from './engine/core/service/bootstrap.service' import { EngineService } from './engine/core/service/engine.service' import { ProvisionService } from './engine/core/service/provision.service' @@ -27,8 +28,13 @@ import { EncryptionModuleOptionFactory } from './shared/factory/encryption-modul ], providers: [ { + // DEPRECATE: Use Zod generated DTOs to validate request and responses. provide: APP_PIPE, useClass: ValidationPipe + }, + { + provide: APP_PIPE, + useClass: ZodValidationPipe } ] }) diff --git a/apps/vault/src/main.module.ts b/apps/vault/src/main.module.ts index 002e0ca6e..db88dc197 100644 --- a/apps/vault/src/main.module.ts +++ b/apps/vault/src/main.module.ts @@ -2,6 +2,7 @@ import { EncryptionModule } from '@narval/encryption-module' import { Module, ValidationPipe, forwardRef } from '@nestjs/common' import { ConfigModule, ConfigService } from '@nestjs/config' import { APP_PIPE } from '@nestjs/core' +import { ZodValidationPipe } from 'nestjs-zod' import { load } from './main.config' import { EncryptionModuleOptionFactory } from './shared/factory/encryption-module-option.factory' import { TenantModule } from './tenant/tenant.module' @@ -27,8 +28,13 @@ import { VaultModule } from './vault/vault.module' ], providers: [ { + // DEPRECATE: Use Zod generated DTOs to validate request and responses. provide: APP_PIPE, useClass: ValidationPipe + }, + { + provide: APP_PIPE, + useClass: ZodValidationPipe } ] }) diff --git a/apps/vault/src/main.ts b/apps/vault/src/main.ts index 1e86aa6b6..9464b685c 100644 --- a/apps/vault/src/main.ts +++ b/apps/vault/src/main.ts @@ -1,30 +1,10 @@ +import { withSwagger } from '@narval/nestjs-shared' import { INestApplication, Logger, ValidationPipe } from '@nestjs/common' import { ConfigService } from '@nestjs/config' import { NestFactory } from '@nestjs/core' -import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger' import { lastValueFrom, map, of, switchMap } from 'rxjs' import { MainModule } from './main.module' -/** - * Adds Swagger documentation to the application. - * - * @param app - The INestApplication instance. - * @returns The modified INestApplication instance. - */ -const withSwagger = (app: INestApplication): INestApplication => { - const document = SwaggerModule.createDocument( - app, - new DocumentBuilder() - .setTitle('Vault') - .setDescription('The next generation of authorization for web3') - .setVersion('1.0') - .build() - ) - SwaggerModule.setup('docs', app, document) - - return app -} - /** * Adds global pipes to the application. * @@ -49,7 +29,13 @@ async function bootstrap() { await lastValueFrom( of(application).pipe( - map(withSwagger), + map( + withSwagger({ + title: 'Vault', + description: 'The next generation of authorization for web3', + version: '1.0' + }) + ), map(withGlobalPipes), switchMap((app) => app.listen(port)) ) diff --git a/apps/vault/src/tenant/tenant.module.ts b/apps/vault/src/tenant/tenant.module.ts index b532b6372..225873045 100644 --- a/apps/vault/src/tenant/tenant.module.ts +++ b/apps/vault/src/tenant/tenant.module.ts @@ -1,6 +1,7 @@ import { HttpModule } from '@nestjs/axios' import { Module, OnApplicationBootstrap, ValidationPipe, forwardRef } from '@nestjs/common' import { APP_PIPE } from '@nestjs/core' +import { ZodValidationPipe } from 'nestjs-zod' import { AdminApiKeyGuard } from '../shared/guard/admin-api-key.guard' import { KeyValueModule } from '../shared/module/key-value/key-value.module' import { VaultModule } from '../vault/vault.module' @@ -19,8 +20,13 @@ import { TenantRepository } from './persistence/repository/tenant.repository' TenantRepository, TenantService, { + // DEPRECATE: Use Zod generated DTOs to validate request and responses. provide: APP_PIPE, useClass: ValidationPipe + }, + { + provide: APP_PIPE, + useClass: ZodValidationPipe } ], exports: [TenantService, TenantRepository] diff --git a/package-lock.json b/package-lock.json index f53cc3cc0..261c7def0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,6 +43,7 @@ "lodash": "^4.17.21", "lowdb": "^7.0.1", "nest-commander": "^3.12.5", + "nestjs-zod": "^3.0.0", "next": "14.0.4", "prism-react-renderer": "^2.3.1", "react": "18.2.0", @@ -17882,6 +17883,14 @@ "deep-equal": "^2.0.5" } }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/array-buffer-byte-length": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", @@ -24299,6 +24308,25 @@ "is-callable": "^1.1.3" } }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha512-SKmowqGTJoPzLO1T0BBJpkfp3EMacCMOuH40hOUbrbzElVktk4DioXVM99QkLCyKoiuOmyjgcWMpVz2xjE7LZw==", + "dependencies": { + "for-in": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/foreground-child": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", @@ -26202,6 +26230,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -28055,6 +28088,14 @@ "shell-quote": "^1.8.1" } }, + "node_modules/lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/less": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", @@ -29541,6 +29582,89 @@ "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==", "peer": true }, + "node_modules/merge-deep": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/merge-deep/-/merge-deep-3.0.3.tgz", + "integrity": "sha512-qtmzAS6t6grwEkNrunqTBdn0qKwFgNWvlxUbAV8es9M7Ot1EbyApytCnvE0jALPa46ZpKDUo527kKiaWplmlFA==", + "dependencies": { + "arr-union": "^3.1.0", + "clone-deep": "^0.2.4", + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/merge-deep/node_modules/clone-deep": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-0.2.4.tgz", + "integrity": "sha512-we+NuQo2DHhSl+DP6jlUiAhyAjBQrYnpOk15rN6c6JSPScjiCLh8IbSU+VTcph6YS3o7mASE8a0+gbZ7ChLpgg==", + "dependencies": { + "for-own": "^0.1.3", + "is-plain-object": "^2.0.1", + "kind-of": "^3.0.2", + "lazy-cache": "^1.0.3", + "shallow-clone": "^0.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/merge-deep/node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/merge-deep/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/merge-deep/node_modules/shallow-clone": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz", + "integrity": "sha512-J1zdXCky5GmNnuauESROVu31MQSnLoYvlyEn6j2Ztk6Q5EHFIhxkMhYcv6vuDzl2XEzoRr856QwzMgWM/TmZgw==", + "dependencies": { + "is-extendable": "^0.1.1", + "kind-of": "^2.0.1", + "lazy-cache": "^0.2.3", + "mixin-object": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/merge-deep/node_modules/shallow-clone/node_modules/kind-of": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz", + "integrity": "sha512-0u8i1NZ/mg0b+W3MGGw5I7+6Eib2nx72S/QvXa0hYjEkjTknYmEYQJwGu3mLC0BrhtJjtQafTkyRUQ75Kx0LVg==", + "dependencies": { + "is-buffer": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/merge-deep/node_modules/shallow-clone/node_modules/lazy-cache": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz", + "integrity": "sha512-gkX52wvU/R8DVMMt78ATVPFMJqfW8FPz1GZ1sVHBVQHmu/WvhIWE4cE1GBzhJNFicDeYhnwp6Rl35BcAIM3YOQ==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -31945,6 +32069,26 @@ } } }, + "node_modules/mixin-object": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz", + "integrity": "sha512-ALGF1Jt9ouehcaXaHhn6t1yGWRqGaHkPFndtFVHfZXOvkIZ/yoGaSi0AHVTafb3ZBGg4dr/bDwnaEKqCXzchMA==", + "dependencies": { + "for-in": "^0.1.3", + "is-extendable": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mixin-object/node_modules/for-in": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz", + "integrity": "sha512-F0to7vbBSHP8E3l6dCjxNOLuSFAACIxFy3UehTUlG7svlXi37HHsDkyVcHo0Pq8QwrE+pXvWSVX3ZT1T9wAZ9g==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -32237,6 +32381,34 @@ } } }, + "node_modules/nestjs-zod": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/nestjs-zod/-/nestjs-zod-3.0.0.tgz", + "integrity": "sha512-vL9CHShCVj6TmjCVPOd4my46D8d7FdoB4nQvvh+lmVTuzvnwuD+slSxjT4EDdPDWDFtjhfpvQnnkr55/80KHEQ==", + "dependencies": { + "merge-deep": "^3.0.3" + }, + "peerDependencies": { + "@nestjs/common": ">= 8.0.0", + "@nestjs/core": ">= 8.0.0", + "@nestjs/swagger": ">= 5.0.0", + "zod": ">= 3.14.3" + }, + "peerDependenciesMeta": { + "@nestjs/common": { + "optional": true + }, + "@nestjs/core": { + "optional": true + }, + "@nestjs/swagger": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, "node_modules/next": { "version": "14.0.4", "resolved": "https://registry.npmjs.org/next/-/next-14.0.4.tgz", diff --git a/package.json b/package.json index bc98f450e..56489f987 100644 --- a/package.json +++ b/package.json @@ -105,6 +105,7 @@ "lodash": "^4.17.21", "lowdb": "^7.0.1", "nest-commander": "^3.12.5", + "nestjs-zod": "^3.0.0", "next": "14.0.4", "prism-react-renderer": "^2.3.1", "react": "18.2.0", diff --git a/packages/nestjs-shared/README.md b/packages/nestjs-shared/README.md index 4c0f7aef0..f66678199 100644 --- a/packages/nestjs-shared/README.md +++ b/packages/nestjs-shared/README.md @@ -1,6 +1,7 @@ # NestJS Shared -This library contains the shared nestjs utilities such as validators, DTOs and decorators. +This library contains the shared NestJS utilities such as validators, DTOs and +decorators. ## Testing diff --git a/packages/nestjs-shared/src/index.ts b/packages/nestjs-shared/src/index.ts index 33d9aa5d1..5e63f9cad 100644 --- a/packages/nestjs-shared/src/index.ts +++ b/packages/nestjs-shared/src/index.ts @@ -1,2 +1,3 @@ -export * from './lib/decorators' +export * from './lib/decorator' export * from './lib/dto' +export * from './lib/util' diff --git a/packages/nestjs-shared/src/lib/decorators/index.ts b/packages/nestjs-shared/src/lib/decorator/index.ts similarity index 100% rename from packages/nestjs-shared/src/lib/decorators/index.ts rename to packages/nestjs-shared/src/lib/decorator/index.ts diff --git a/packages/nestjs-shared/src/lib/decorators/is-account-id.decorator.ts b/packages/nestjs-shared/src/lib/decorator/is-account-id.decorator.ts similarity index 100% rename from packages/nestjs-shared/src/lib/decorators/is-account-id.decorator.ts rename to packages/nestjs-shared/src/lib/decorator/is-account-id.decorator.ts diff --git a/packages/nestjs-shared/src/lib/decorators/is-asset-id.decorator.ts b/packages/nestjs-shared/src/lib/decorator/is-asset-id.decorator.ts similarity index 100% rename from packages/nestjs-shared/src/lib/decorators/is-asset-id.decorator.ts rename to packages/nestjs-shared/src/lib/decorator/is-asset-id.decorator.ts diff --git a/packages/nestjs-shared/src/lib/decorators/is-hex-string.decorator.ts b/packages/nestjs-shared/src/lib/decorator/is-hex-string.decorator.ts similarity index 100% rename from packages/nestjs-shared/src/lib/decorators/is-hex-string.decorator.ts rename to packages/nestjs-shared/src/lib/decorator/is-hex-string.decorator.ts diff --git a/packages/nestjs-shared/src/lib/decorators/is-not-empty-array-enum.decorator.ts b/packages/nestjs-shared/src/lib/decorator/is-not-empty-array-enum.decorator.ts similarity index 100% rename from packages/nestjs-shared/src/lib/decorators/is-not-empty-array-enum.decorator.ts rename to packages/nestjs-shared/src/lib/decorator/is-not-empty-array-enum.decorator.ts diff --git a/packages/nestjs-shared/src/lib/decorators/is-not-empty-array-string.decorator.ts b/packages/nestjs-shared/src/lib/decorator/is-not-empty-array-string.decorator.ts similarity index 100% rename from packages/nestjs-shared/src/lib/decorators/is-not-empty-array-string.decorator.ts rename to packages/nestjs-shared/src/lib/decorator/is-not-empty-array-string.decorator.ts diff --git a/packages/nestjs-shared/src/lib/dto/sign-transaction-request-data.dto.ts b/packages/nestjs-shared/src/lib/dto/sign-transaction-request-data.dto.ts index 430a3bad1..300b9a1e1 100644 --- a/packages/nestjs-shared/src/lib/dto/sign-transaction-request-data.dto.ts +++ b/packages/nestjs-shared/src/lib/dto/sign-transaction-request-data.dto.ts @@ -2,7 +2,7 @@ import { Action, Address, Hex } from '@narval/policy-engine-shared' import { ApiProperty } from '@nestjs/swagger' import { Transform, Type } from 'class-transformer' import { IsDefined, IsEthereumAddress, IsIn, IsInt, IsOptional, IsString, Min, ValidateNested } from 'class-validator' -import { IsHexString } from '../decorators/is-hex-string.decorator' +import { IsHexString } from '../decorator/is-hex-string.decorator' import { BaseActionDto } from './' class AccessListDto { diff --git a/packages/nestjs-shared/src/lib/util/index.ts b/packages/nestjs-shared/src/lib/util/index.ts new file mode 100644 index 000000000..648a9a9e3 --- /dev/null +++ b/packages/nestjs-shared/src/lib/util/index.ts @@ -0,0 +1 @@ +export * from './with-swagger.util' diff --git a/packages/nestjs-shared/src/lib/util/with-swagger.util.ts b/packages/nestjs-shared/src/lib/util/with-swagger.util.ts new file mode 100644 index 000000000..2973f7e5a --- /dev/null +++ b/packages/nestjs-shared/src/lib/util/with-swagger.util.ts @@ -0,0 +1,29 @@ +import { INestApplication } from '@nestjs/common' +import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger' +import { patchNestJsSwagger } from 'nestjs-zod' + +/** + * Adds Swagger documentation to the application. + * + * @param app - The INestApplication instance. + * @returns The modified INestApplication instance. + */ +export const withSwagger = + (params: { title: string; description: string; version: string }) => + (app: INestApplication): INestApplication => { + // IMPORTANT: This modifies the Nest Swagger module to be compatible with + // DTOs created by Zod schemas. The patch MUST be done before the + // configuration process. + patchNestJsSwagger() + + const document = SwaggerModule.createDocument( + app, + new DocumentBuilder().setTitle(params.title).setDescription(params.description).setVersion(params.version).build() + ) + + SwaggerModule.setup('docs', app, document, { + customSiteTitle: `${params.title} API` + }) + + return app + }