Skip to content

Commit

Permalink
Configure applications to generate NestJS DTOs from Zod schemas (#194)
Browse files Browse the repository at this point in the history
* Install nestjs-zod

* Configure Swagger on all applications to understand Zod DTOs

* Rename nestjs-shared decorators directory

* Fix DTO unit test

* Add utility to set up Swagger

* Refactor NestJS application bootstrap to use shared withSwagger
  • Loading branch information
wcalderipe authored Mar 27, 2024
1 parent b4703d6 commit af6bc5c
Show file tree
Hide file tree
Showing 22 changed files with 263 additions and 71 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
| ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| [@app/armory](./apps/armory/README.md) | <a href="https://github.com/narval-xyz/narval/actions/workflows/armory.yml" target="_blank"><img src="https://github.com/narval-xyz/narval/actions/workflows/armory.yml/badge.svg?branch=main" alt="@app/armory CI status" /></a> |
| [@app/policy-engine](./apps/policy-engine/README.md) | <a href="https://github.com/narval-xyz/narval/actions/workflows/policy-engine.yml" target="_blank"><img src="https://github.com/narval-xyz/narval/actions/workflows/policy-engine.yml/badge.svg?branch=main" alt="@app/policy-engine CI status" /></a> |
| [@app/vault](./apps/vault/README.md) | <a href="https://github.com/narval-xyz/armory/actions/workflows/vault.yml" target="_blank"><img src="https://github.com/narval-xyz/armory/actions/workflows/vault.yml/badge.svg" alt="@app/vault CI status" /></a> |
| [@narval/encryption](./packages/encryption/README.md) | <a href="https://github.com/narval-xyz/armory/actions/workflows/packages.yml" target="_blank"><img src="https://github.com/narval-xyz/armory/actions/workflows/packages.yml/badge.svg?branch=main" alt="Packages CI status" /></a> |
| [@narval/policy-engine-shared](./packages/policy-engine-shared/README.md) | <a href="https://github.com/narval-xyz/armory/actions/workflows/packages.yml" target="_blank"><img src="https://github.com/narval-xyz/armory/actions/workflows/packages.yml/badge.svg?branch=main" alt="Packages CI status" /></a> |
| [@narval/signature](./packages/signature/README.md) | <a href="https://github.com/narval-xyz/armory/actions/workflows/packages.yml" target="_blank"><img src="https://github.com/narval-xyz/armory/actions/workflows/packages.yml/badge.svg?branch=main" alt="Packages CI status" /></a> |
Expand Down
32 changes: 8 additions & 24 deletions apps/armory/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,14 @@
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'
import { ApplicationExceptionFilter } from './shared/filter/application-exception.filter'
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.
*
Expand Down Expand Up @@ -88,7 +66,13 @@ async function bootstrap(): Promise<void> {

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)),
Expand Down
6 changes: 6 additions & 0 deletions apps/armory/src/orchestration/orchestration.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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]
Expand Down
6 changes: 6 additions & 0 deletions apps/policy-engine/src/engine/engine.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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]
Expand Down
30 changes: 8 additions & 22 deletions apps/policy-engine/src/main.ts
Original file line number Diff line number Diff line change
@@ -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.
*
Expand Down Expand Up @@ -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))
Expand Down
6 changes: 6 additions & 0 deletions apps/policy-engine/src/policy-engine.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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
}
]
})
Expand Down
6 changes: 6 additions & 0 deletions apps/vault/src/main.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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
}
]
})
Expand Down
30 changes: 8 additions & 22 deletions apps/vault/src/main.ts
Original file line number Diff line number Diff line change
@@ -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.
*
Expand All @@ -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))
)
Expand Down
6 changes: 6 additions & 0 deletions apps/vault/src/tenant/tenant.module.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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]
Expand Down
Loading

0 comments on commit af6bc5c

Please sign in to comment.