Skip to content

Commit 5c871ff

Browse files
committed
WIP api decorators system
1 parent f9f8480 commit 5c871ff

23 files changed

+392
-53
lines changed

service/src/_common/abstracts/abstract.controller.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { ModuleRef } from '@nestjs/core'
22
import { ApiExtraModels } from '@nestjs/swagger'
3-
import { PaginatedDto } from '~/_common/dto/paginated.dto'
3+
import { PaginatedResponseDto } from '~/_common/dto/paginated-response.dto'
44

55
export interface AbstractControllerContext {
66
[key: string | number]: any
77

88
moduleRef?: ModuleRef
99
}
1010

11-
@ApiExtraModels(PaginatedDto)
11+
@ApiExtraModels(PaginatedResponseDto)
1212
export abstract class AbstractController {
1313
protected moduleRef?: ModuleRef
1414

service/src/_common/abstracts/abstract.service.schema.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,9 @@ export abstract class AbstractServiceSchema extends AbstractService implements S
6868
const document: Document<T, any, T> = new this._model({
6969
...data,
7070
metadata: {
71-
createdBy: this.request.user || 'anonymous',
71+
createdBy: this.request?.user || 'anonymous',
7272
createdAt: new Date(),
73-
lastUpdatedBy: this.request.user || 'anonymous',
73+
lastUpdatedBy: this.request?.user || 'anonymous',
7474
lastUpdatedAt: new Date(),
7575
}
7676
})
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { IsObject, IsOptional, ValidateNested } from 'class-validator'
2+
3+
export abstract class AbstractCustomFieldsDto {
4+
@IsObject()
5+
@IsOptional()
6+
@ValidateNested()
7+
public customFields?: { [key: string]: any }[]
8+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { applyDecorators, Type } from '@nestjs/common'
2+
import { ApiBody, ApiBodyOptions, ApiExtraModels, getSchemaPath } from '@nestjs/swagger'
3+
4+
export const ApiBodyDecorator = <TModel extends Type<any>>(
5+
model: TModel,
6+
options?: ApiBodyOptions | null | undefined,
7+
) => {
8+
return applyDecorators(
9+
ApiExtraModels(model),
10+
ApiBody({
11+
schema: {
12+
$ref: getSchemaPath(model),
13+
},
14+
...options,
15+
}),
16+
)
17+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { applyDecorators, Type } from '@nestjs/common'
2+
import { ApiBodyOptions } from '@nestjs/swagger'
3+
import { ApiBodyDecorator } from '~/_common/decorators/api-body.decorator'
4+
import { ApiResponseOptions } from '@nestjs/swagger/dist/decorators/api-response.decorator'
5+
import { ApiCreatedResponseDecorator } from '~/_common/decorators/api-created-response.decorator'
6+
7+
export const ApiCreateDecorator = <TModel extends Type<any>>(
8+
bodyModel: TModel,
9+
responseModel: TModel,
10+
bodyOptions?: ApiBodyOptions | null | undefined,
11+
responseOptions?: ApiResponseOptions | null | undefined,
12+
) => {
13+
return applyDecorators(
14+
ApiBodyDecorator(bodyModel, bodyOptions),
15+
ApiCreatedResponseDecorator(responseModel, responseOptions),
16+
)
17+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { applyDecorators, HttpStatus, Type } from '@nestjs/common'
2+
import { ApiBadRequestResponse, ApiCreatedResponse, ApiExtraModels, getSchemaPath } from '@nestjs/swagger'
3+
import { ApiResponseOptions } from '@nestjs/swagger/dist/decorators/api-response.decorator'
4+
import { ErrorSchemaDto } from '~/_common/dto/error-schema.dto'
5+
6+
export const ApiCreatedResponseDecorator = <TModel extends Type<any>>(
7+
model: TModel,
8+
options?: ApiResponseOptions | null | undefined,
9+
) => {
10+
return applyDecorators(
11+
ApiExtraModels(model),
12+
ApiExtraModels(ErrorSchemaDto),
13+
ApiCreatedResponse({
14+
schema: {
15+
properties: {
16+
statusCode: {
17+
type: 'number',
18+
enum: [HttpStatus.CREATED],
19+
},
20+
data: {
21+
$ref: getSchemaPath(model),
22+
}
23+
}
24+
},
25+
...options,
26+
}),
27+
ApiBadRequestResponse({
28+
description: 'Schema validation failed',
29+
schema: {
30+
$ref: getSchemaPath(ErrorSchemaDto),
31+
},
32+
}),
33+
)
34+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { applyDecorators, HttpStatus, Type } from '@nestjs/common'
2+
import { ApiExtraModels, ApiNotFoundResponse, getSchemaPath } from '@nestjs/swagger'
3+
import { ApiOkResponse, ApiResponseOptions } from '@nestjs/swagger/dist/decorators/api-response.decorator'
4+
import { NotFoundDto } from '~/_common/dto/not-found.dto'
5+
6+
export const ApiDeletedResponseDecorator = <TModel extends Type<any>>(
7+
model: TModel,
8+
responseOptions?: ApiResponseOptions | null | undefined,
9+
notFoundOptions?: ApiResponseOptions | null | undefined,
10+
) => {
11+
return applyDecorators(
12+
ApiExtraModels(model),
13+
ApiExtraModels(NotFoundDto),
14+
ApiOkResponse({
15+
schema: {
16+
properties: {
17+
statusCode: {
18+
type: 'number',
19+
enum: [HttpStatus.OK],
20+
},
21+
data: {
22+
$ref: getSchemaPath(model),
23+
}
24+
}
25+
},
26+
...responseOptions,
27+
}),
28+
ApiNotFoundResponse({
29+
description: 'Item not found',
30+
schema: {
31+
$ref: getSchemaPath(NotFoundDto),
32+
},
33+
...notFoundOptions,
34+
})
35+
)
36+
}
Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,18 @@
11
import { applyDecorators, Type } from '@nestjs/common'
2-
import { ApiExtraModels, ApiQuery, getSchemaPath } from '@nestjs/swagger'
3-
import { ApiOkResponse } from '@nestjs/swagger/dist/decorators/api-response.decorator'
4-
import { PaginatedDto } from '~/_common/dto/paginated.dto'
2+
import { ApiExtraModels, getSchemaPath } from '@nestjs/swagger'
3+
import { ApiOkResponse, ApiResponseOptions } from '@nestjs/swagger/dist/decorators/api-response.decorator'
4+
import { PaginatedResponseDto } from '~/_common/dto/paginated-response.dto'
55

6-
const DEFAULT_OPTIONS = {
7-
queryPagination: true,
8-
}
9-
10-
export interface ApiPaginatedResponseOptions {
11-
queryPagination?: boolean
12-
}
13-
14-
export const ApiPaginatedResponse = <TModel extends Type<any>>(
6+
export const ApiPaginatedResponseDecorator = <TModel extends Type<any>>(
157
model: TModel,
16-
options?: ApiPaginatedResponseOptions | null | undefined,
8+
options?: ApiResponseOptions | null | undefined,
179
) => {
18-
options = { ...DEFAULT_OPTIONS, ...options }
19-
const extraDecorators = []
20-
if (options?.queryPagination) {
21-
extraDecorators.push(
22-
ApiQuery({ name: 'limit', type: Number, required: false }),
23-
ApiQuery({ name: 'skip', type: Number, required: false }),
24-
)
25-
}
2610
return applyDecorators(
2711
ApiExtraModels(model),
2812
ApiOkResponse({
2913
schema: {
3014
allOf: [
31-
{ $ref: getSchemaPath(PaginatedDto) },
15+
{ $ref: getSchemaPath(PaginatedResponseDto) },
3216
{
3317
properties: {
3418
data: {
@@ -39,7 +23,7 @@ export const ApiPaginatedResponse = <TModel extends Type<any>>(
3923
},
4024
],
4125
},
26+
...options,
4227
}),
43-
...extraDecorators,
4428
)
4529
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { applyDecorators, Type } from '@nestjs/common'
2+
import { ApiExtraModels, ApiQuery, getSchemaPath } from '@nestjs/swagger'
3+
import { ApiResponseOptions } from '@nestjs/swagger/dist/decorators/api-response.decorator'
4+
import { ApiPaginatedResponseDecorator } from '~/_common/decorators/api-paginated-response.decorator'
5+
import { PaginatedFilterDto } from '~/_common/dto/paginated-filter.dto'
6+
7+
export const ApiPaginatedDecorator = <TModel extends Type<any>>(
8+
model: TModel,
9+
options?: ApiResponseOptions | null | undefined,
10+
) => {
11+
return applyDecorators(
12+
ApiQuery({ name: 'limit', type: Number, required: false }),
13+
ApiQuery({ name: 'skip', type: Number, required: false }),
14+
ApiExtraModels(PaginatedFilterDto),
15+
ApiQuery({
16+
required: false,
17+
name: 'filters',
18+
style: 'deepObject',
19+
explode: true,
20+
type: 'object',
21+
schema: {
22+
$ref: getSchemaPath(PaginatedFilterDto),
23+
}
24+
}),
25+
ApiPaginatedResponseDecorator(model, options),
26+
)
27+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { applyDecorators, HttpStatus, Type } from '@nestjs/common'
2+
import { ApiExtraModels, ApiNotFoundResponse, getSchemaPath } from '@nestjs/swagger'
3+
import { ApiOkResponse, ApiResponseOptions } from '@nestjs/swagger/dist/decorators/api-response.decorator'
4+
import { NotFoundDto } from '~/_common/dto/not-found.dto'
5+
6+
export const ApiReadResponseDecorator = <TModel extends Type<any>>(
7+
model: TModel,
8+
responseOptions?: ApiResponseOptions | null | undefined,
9+
notFoundOptions?: ApiResponseOptions | null | undefined,
10+
) => {
11+
return applyDecorators(
12+
ApiExtraModels(model),
13+
ApiExtraModels(NotFoundDto),
14+
ApiOkResponse({
15+
schema: {
16+
properties: {
17+
statusCode: {
18+
type: 'number',
19+
enum: [HttpStatus.OK],
20+
},
21+
data: {
22+
$ref: getSchemaPath(model),
23+
}
24+
}
25+
},
26+
...responseOptions,
27+
}),
28+
ApiNotFoundResponse({
29+
description: 'Item not found',
30+
schema: {
31+
$ref: getSchemaPath(NotFoundDto),
32+
},
33+
...notFoundOptions,
34+
})
35+
)
36+
}

0 commit comments

Comments
 (0)