feat: add swagger
This commit is contained in:
parent
b07b6082e4
commit
f3c5673c75
@ -21,6 +21,7 @@
|
|||||||
"@nestjs/mapped-types": "^2.0.6",
|
"@nestjs/mapped-types": "^2.0.6",
|
||||||
"@nestjs/passport": "^10.0.3",
|
"@nestjs/passport": "^10.0.3",
|
||||||
"@nestjs/platform-express": "^10.4.11",
|
"@nestjs/platform-express": "^10.4.11",
|
||||||
|
"@nestjs/swagger": "^8.0.7",
|
||||||
"@prisma/client": "5.22.0",
|
"@prisma/client": "5.22.0",
|
||||||
"axios": "^1.7.7",
|
"axios": "^1.7.7",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
|
25
src/main.ts
25
src/main.ts
@ -1,6 +1,7 @@
|
|||||||
import { NestFactory, Reflector } from "@nestjs/core";
|
|
||||||
import { AppModule } from "./app.module";
|
|
||||||
import { ClassSerializerInterceptor, ValidationPipe } from "@nestjs/common";
|
import { ClassSerializerInterceptor, ValidationPipe } from "@nestjs/common";
|
||||||
|
import { NestFactory, Reflector } from "@nestjs/core";
|
||||||
|
import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger";
|
||||||
|
import { AppModule } from "./app.module";
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = await NestFactory.create(AppModule);
|
const app = await NestFactory.create(AppModule);
|
||||||
@ -11,9 +12,23 @@ async function bootstrap() {
|
|||||||
|
|
||||||
app.useGlobalPipes(new ValidationPipe());
|
app.useGlobalPipes(new ValidationPipe());
|
||||||
|
|
||||||
app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector), {
|
app.useGlobalInterceptors(
|
||||||
excludeExtraneousValues: true,
|
new ClassSerializerInterceptor(app.get(Reflector), {
|
||||||
}));
|
excludeExtraneousValues: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const config = new DocumentBuilder()
|
||||||
|
.setTitle("Toogether API")
|
||||||
|
.addBearerAuth()
|
||||||
|
.build();
|
||||||
|
const documentFactory = () => SwaggerModule.createDocument(app, config);
|
||||||
|
SwaggerModule.setup("documentation", app, documentFactory, {
|
||||||
|
swaggerOptions: {
|
||||||
|
tryItOutEnabled: true,
|
||||||
|
persistAuthorization: true,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
await app.listen(process.env.PORT ?? 3000, "0.0.0.0");
|
await app.listen(process.env.PORT ?? 3000, "0.0.0.0");
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
import { IsString } from "class-validator";
|
import { IsString } from "class-validator";
|
||||||
|
|
||||||
export class CreateUserDTO {
|
export class CreateUserDTO {
|
||||||
@IsString()
|
@IsString()
|
||||||
|
@ApiProperty()
|
||||||
username: string;
|
username: string;
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,13 @@ import {
|
|||||||
Query,
|
Query,
|
||||||
UseGuards,
|
UseGuards,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
|
import {
|
||||||
|
ApiBearerAuth,
|
||||||
|
ApiBody,
|
||||||
|
ApiOkResponse,
|
||||||
|
ApiQuery,
|
||||||
|
ApiUnauthorizedResponse,
|
||||||
|
} from "@nestjs/swagger";
|
||||||
|
|
||||||
import { Role } from "@Modules/auth/Decorators/roles.decorator";
|
import { Role } from "@Modules/auth/Decorators/roles.decorator";
|
||||||
import { JwtAuthGuard } from "@Modules/auth/Guards/jwt.guard";
|
import { JwtAuthGuard } from "@Modules/auth/Guards/jwt.guard";
|
||||||
@ -21,32 +28,118 @@ import { UserService } from "./user.service";
|
|||||||
@Controller("user")
|
@Controller("user")
|
||||||
@UseGuards(RolesGuard)
|
@UseGuards(RolesGuard)
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@ApiUnauthorizedResponse({
|
||||||
|
description: "Unauthorized",
|
||||||
|
example: {
|
||||||
|
message: "Unauthorized",
|
||||||
|
statusCode: 401,
|
||||||
|
},
|
||||||
|
})
|
||||||
export class UserController {
|
export class UserController {
|
||||||
constructor(private readonly userService: UserService) {}
|
constructor(private readonly userService: UserService) {}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@Role("ADMIN")
|
@Role("ADMIN")
|
||||||
|
@ApiOkResponse({
|
||||||
|
type: UserEntity,
|
||||||
|
isArray: true,
|
||||||
|
examples: {
|
||||||
|
example: {
|
||||||
|
summary: "A list of users",
|
||||||
|
value: [
|
||||||
|
{ id: "1", role: "ADMIN", username: "admin" },
|
||||||
|
{ id: "2", role: "STUDENT", username: "student" },
|
||||||
|
] as UserEntity[],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
async findAll(): Promise<UserEntity[]> {
|
async findAll(): Promise<UserEntity[]> {
|
||||||
const users = await this.userService.findAll();
|
return await this.userService
|
||||||
|
.findAll()
|
||||||
return users.map((user) => new UserEntity(user));
|
.then((users) => users.map((user) => new UserEntity(user)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
@Role("ADMIN")
|
@Role("ADMIN")
|
||||||
create(@Body() createUserInput: CreateUserDTO) {
|
@ApiOkResponse({
|
||||||
return this.userService.create(createUserInput);
|
type: UserEntity,
|
||||||
|
description: "The user has been successfully created.",
|
||||||
|
examples: {
|
||||||
|
example: {
|
||||||
|
summary: "A user example",
|
||||||
|
value: {
|
||||||
|
id: "1",
|
||||||
|
role: "ADMIN",
|
||||||
|
username: "admin",
|
||||||
|
} as UserEntity,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: CreateUserDTO,
|
||||||
|
examples: {
|
||||||
|
example: {
|
||||||
|
summary: "A user example",
|
||||||
|
value: { username: "admin" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
async create(@Body() createUserInput: CreateUserDTO): Promise<UserEntity> {
|
||||||
|
return this.userService
|
||||||
|
.create(createUserInput)
|
||||||
|
.then((user) => new UserEntity(user));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete()
|
@Delete()
|
||||||
@Role("ADMIN")
|
@Role("ADMIN")
|
||||||
delete(@Query("id") id: string) {
|
@ApiOkResponse({
|
||||||
return this.userService.delete(id);
|
type: UserEntity,
|
||||||
|
description: "The user has been successfully deleted.",
|
||||||
|
examples: {
|
||||||
|
example: {
|
||||||
|
summary: "A user example",
|
||||||
|
value: {
|
||||||
|
id: "1",
|
||||||
|
role: "ADMIN",
|
||||||
|
username: "admin",
|
||||||
|
} as UserEntity,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
@ApiQuery({
|
||||||
|
name: "id",
|
||||||
|
type: String,
|
||||||
|
description: "The user id",
|
||||||
|
example: "1",
|
||||||
|
})
|
||||||
|
async delete(@Query("id") id: string): Promise<UserEntity> {
|
||||||
|
return this.userService.delete(id).then((user) => new UserEntity(user));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete("/bulk")
|
@Delete("/bulk")
|
||||||
@Role("ADMIN")
|
@Role("ADMIN")
|
||||||
bulkDelete(@Body() { ids }: BulkDeleteUserDTO) {
|
@ApiOkResponse({
|
||||||
|
description: "The users have been successfully deleted.",
|
||||||
|
examples: {
|
||||||
|
example: {
|
||||||
|
summary: "A count of deleted users",
|
||||||
|
value: { count: 2 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: BulkDeleteUserDTO,
|
||||||
|
examples: {
|
||||||
|
example: {
|
||||||
|
summary: "A list of user ids",
|
||||||
|
value: { ids: ["1", "2"] },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
bulkDelete(@Body() { ids }: BulkDeleteUserDTO): Promise<{
|
||||||
|
count: number;
|
||||||
|
}> {
|
||||||
return this.userService.bulkDelete(ids);
|
return this.userService.bulkDelete(ids);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
|
import { ApiProperty, ApiSchema } from "@nestjs/swagger";
|
||||||
import { $Enums } from "@prisma/client";
|
import { $Enums } from "@prisma/client";
|
||||||
import { Expose } from "class-transformer";
|
import { Expose } from "class-transformer";
|
||||||
|
|
||||||
|
@ApiSchema({ name: "User" })
|
||||||
export class UserEntity {
|
export class UserEntity {
|
||||||
@Expose()
|
@Expose()
|
||||||
|
@ApiProperty()
|
||||||
id: string;
|
id: string;
|
||||||
|
|
||||||
@Expose()
|
@Expose()
|
||||||
|
@ApiProperty()
|
||||||
username: string;
|
username: string;
|
||||||
|
|
||||||
@Expose()
|
@Expose()
|
||||||
|
@ApiProperty()
|
||||||
role: $Enums.Role
|
role: $Enums.Role
|
||||||
|
|
||||||
constructor(partial: Partial<UserEntity>) {
|
constructor(partial: Partial<UserEntity>) {
|
||||||
|
@ -3,13 +3,26 @@ import { PrismaService } from "@Modules/prisma/prisma.service";
|
|||||||
|
|
||||||
import { CreateUserDTO } from "./dto/create-user.dto";
|
import { CreateUserDTO } from "./dto/create-user.dto";
|
||||||
import { UpdateUserDTO } from "./dto/update-user.dto";
|
import { UpdateUserDTO } from "./dto/update-user.dto";
|
||||||
|
import { Prisma } from "@prisma/client";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UserService {
|
export class UserService {
|
||||||
constructor(private readonly prisma: PrismaService) {}
|
constructor(private readonly prisma: PrismaService) {}
|
||||||
|
|
||||||
async findAll() {
|
async findAll({
|
||||||
return await this.prisma.user.findMany();
|
include,
|
||||||
|
cursor,
|
||||||
|
take,
|
||||||
|
}: {
|
||||||
|
include?: Prisma.UserInclude;
|
||||||
|
cursor?: string;
|
||||||
|
take?: number;
|
||||||
|
} = {}) {
|
||||||
|
return await this.prisma.user.findMany({
|
||||||
|
include,
|
||||||
|
cursor: cursor ? { id: cursor } : undefined,
|
||||||
|
take: take || 10,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async findById(id: string) {
|
async findById(id: string) {
|
||||||
@ -51,6 +64,6 @@ export class UserService {
|
|||||||
in: ids,
|
in: ids,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
33
yarn.lock
33
yarn.lock
@ -233,6 +233,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@lukeed/csprng/-/csprng-1.1.0.tgz#1e3e4bd05c1cc7a0b2ddbd8a03f39f6e4b5e6cfe"
|
resolved "https://registry.yarnpkg.com/@lukeed/csprng/-/csprng-1.1.0.tgz#1e3e4bd05c1cc7a0b2ddbd8a03f39f6e4b5e6cfe"
|
||||||
integrity sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==
|
integrity sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==
|
||||||
|
|
||||||
|
"@microsoft/tsdoc@^0.15.0":
|
||||||
|
version "0.15.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.15.1.tgz#d4f6937353bc4568292654efb0a0e0532adbcba2"
|
||||||
|
integrity sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==
|
||||||
|
|
||||||
"@nestjs/cli@^10.0.0":
|
"@nestjs/cli@^10.0.0":
|
||||||
version "10.4.8"
|
version "10.4.8"
|
||||||
resolved "https://registry.yarnpkg.com/@nestjs/cli/-/cli-10.4.8.tgz#e6dec4eeda8a125918cbf1c0d10773ac6bb6d40e"
|
resolved "https://registry.yarnpkg.com/@nestjs/cli/-/cli-10.4.8.tgz#e6dec4eeda8a125918cbf1c0d10773ac6bb6d40e"
|
||||||
@ -296,7 +301,7 @@
|
|||||||
"@types/jsonwebtoken" "9.0.5"
|
"@types/jsonwebtoken" "9.0.5"
|
||||||
jsonwebtoken "9.0.2"
|
jsonwebtoken "9.0.2"
|
||||||
|
|
||||||
"@nestjs/mapped-types@^2.0.6":
|
"@nestjs/mapped-types@2.0.6", "@nestjs/mapped-types@^2.0.6":
|
||||||
version "2.0.6"
|
version "2.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/@nestjs/mapped-types/-/mapped-types-2.0.6.tgz#d2d8523709fd5d872a9b9e0c38162746e2a7f44e"
|
resolved "https://registry.yarnpkg.com/@nestjs/mapped-types/-/mapped-types-2.0.6.tgz#d2d8523709fd5d872a9b9e0c38162746e2a7f44e"
|
||||||
integrity sha512-84ze+CPfp1OWdpRi1/lOu59hOhTz38eVzJvRKrg9ykRFwDz+XleKfMsG0gUqNZYFa6v53XYzeD+xItt8uDW7NQ==
|
integrity sha512-84ze+CPfp1OWdpRi1/lOu59hOhTz38eVzJvRKrg9ykRFwDz+XleKfMsG0gUqNZYFa6v53XYzeD+xItt8uDW7NQ==
|
||||||
@ -328,6 +333,18 @@
|
|||||||
jsonc-parser "3.3.1"
|
jsonc-parser "3.3.1"
|
||||||
pluralize "8.0.0"
|
pluralize "8.0.0"
|
||||||
|
|
||||||
|
"@nestjs/swagger@^8.0.7":
|
||||||
|
version "8.0.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@nestjs/swagger/-/swagger-8.0.7.tgz#7347fcd919a413a001838732f1b1966ad19df094"
|
||||||
|
integrity sha512-zaTMCEZ/CxX7QYF110nTqJsn7eCXp4VI9kv7+AdUcIlBmhhgJpggBw2Mx2p6xVjyz1EoWXGfxxWKnxEyaQwFlg==
|
||||||
|
dependencies:
|
||||||
|
"@microsoft/tsdoc" "^0.15.0"
|
||||||
|
"@nestjs/mapped-types" "2.0.6"
|
||||||
|
js-yaml "4.1.0"
|
||||||
|
lodash "4.17.21"
|
||||||
|
path-to-regexp "3.3.0"
|
||||||
|
swagger-ui-dist "5.18.2"
|
||||||
|
|
||||||
"@nodelib/fs.scandir@2.1.5":
|
"@nodelib/fs.scandir@2.1.5":
|
||||||
version "2.1.5"
|
version "2.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
|
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
|
||||||
@ -409,6 +426,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@prisma/debug" "5.22.0"
|
"@prisma/debug" "5.22.0"
|
||||||
|
|
||||||
|
"@scarf/scarf@=1.4.0":
|
||||||
|
version "1.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@scarf/scarf/-/scarf-1.4.0.tgz#3bbb984085dbd6d982494538b523be1ce6562972"
|
||||||
|
integrity sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==
|
||||||
|
|
||||||
"@sideway/address@^4.1.5":
|
"@sideway/address@^4.1.5":
|
||||||
version "4.1.5"
|
version "4.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5"
|
resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5"
|
||||||
@ -2125,7 +2147,7 @@ js-tokens@^4.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||||
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
||||||
|
|
||||||
js-yaml@^4.1.0:
|
js-yaml@4.1.0, js-yaml@^4.1.0:
|
||||||
version "4.1.0"
|
version "4.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
|
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
|
||||||
integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
|
integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
|
||||||
@ -3072,6 +3094,13 @@ supports-color@^8.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
has-flag "^4.0.0"
|
has-flag "^4.0.0"
|
||||||
|
|
||||||
|
swagger-ui-dist@5.18.2:
|
||||||
|
version "5.18.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-5.18.2.tgz#62013074374d272c04ed3030704b88db5aa8c0b7"
|
||||||
|
integrity sha512-J+y4mCw/zXh1FOj5wGJvnAajq6XgHOyywsa9yITmwxIlJbMqITq3gYRZHaeqLVH/eV/HOPphE6NjF+nbSNC5Zw==
|
||||||
|
dependencies:
|
||||||
|
"@scarf/scarf" "=1.4.0"
|
||||||
|
|
||||||
symbol-observable@4.0.0:
|
symbol-observable@4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205"
|
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205"
|
||||||
|
Loading…
Reference in New Issue
Block a user