From d5e80b40b7180218828d5190cbfe29cc404dcd3e Mon Sep 17 00:00:00 2001 From: Minseo Park Date: Sun, 22 Sep 2024 17:16:48 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20users=20api=20=EB=AA=85=EC=84=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/users/dto/users.dto.ts | 42 ++++ backend/src/users/schema/users.schema.ts | 112 ++++++++++ backend/src/users/users.controller.spec.ts | 18 ++ backend/src/users/users.controller.ts | 225 +++++++++++++++++++++ backend/src/users/users.module.ts | 11 + backend/src/users/users.service.spec.ts | 18 ++ backend/src/users/users.service.ts | 4 + 7 files changed, 430 insertions(+) create mode 100644 backend/src/users/dto/users.dto.ts create mode 100644 backend/src/users/schema/users.schema.ts create mode 100644 backend/src/users/users.controller.spec.ts create mode 100644 backend/src/users/users.controller.ts create mode 100644 backend/src/users/users.module.ts create mode 100644 backend/src/users/users.service.spec.ts create mode 100644 backend/src/users/users.service.ts diff --git a/backend/src/users/dto/users.dto.ts b/backend/src/users/dto/users.dto.ts new file mode 100644 index 0000000..5e00ba9 --- /dev/null +++ b/backend/src/users/dto/users.dto.ts @@ -0,0 +1,42 @@ +import { createZodDto } from '@anatine/zod-nestjs'; +import { + createUsersRequestSchema, + getAPIVersionResponseSchema, + getMyUserInfoResponseSchema, + getUsersRequestSchema, + getUsersResponseSchema, + myUpdateUsersRequestSchema, + updateUsersParamSchema, + updateUsersRequestSchema, + updateUsersResponseSchema, +} from '../schema/users.schema'; + +export class GetUsersRequestDto extends createZodDto(getUsersRequestSchema) {} + +export class GetUsersResponseDto extends createZodDto(getUsersResponseSchema) {} + +export class CreateUsersRequestDto extends createZodDto( + createUsersRequestSchema, +) {} + +export class UpdateUsersParamDto extends createZodDto(updateUsersParamSchema) {} + +export class UpdateUsersRequestDto extends createZodDto( + updateUsersRequestSchema, +) {} + +export class UpdateUsersResponseDto extends createZodDto( + updateUsersResponseSchema, +) {} + +export class MyUpddateUsersRequestDto extends createZodDto( + myUpdateUsersRequestSchema, +) {} + +export class GetAPIVersionResponseDto extends createZodDto( + getAPIVersionResponseSchema, +) {} + +export class GetMyUserInfoResponseDto extends createZodDto( + getMyUserInfoResponseSchema, +) {} \ No newline at end of file diff --git a/backend/src/users/schema/users.schema.ts b/backend/src/users/schema/users.schema.ts new file mode 100644 index 0000000..a0d0466 --- /dev/null +++ b/backend/src/users/schema/users.schema.ts @@ -0,0 +1,112 @@ +import { extendApi } from '@anatine/zod-openapi'; +import { z } from 'zod'; + +export const getUsersRequestSchema = z.object({ + nicknameOrEmail: z.string().describe('검색할 유저의 nickname or email'), + page: z.coerce.number().int().describe('페이지').min(1), + limit: z.coerce + .number() + .int() + .describe(' 한 페이지에 들어올 검색결과 수 ') + .min(1), + id: z.coerce.number().int().describe('유저의 id').min(0), +}); + +const lending = z.object({ + userId: z.coerce.number(), + bookInfoId: z.coerce.number(), + lendDate: z.coerce.date(), + lendingCondition: z.string(), + image: z.string(), + author: z.string(), + title: z.string(), + duedate: z.coerce.date(), + overDueDay: z.coerce.number(), + reservedNum: z.coerce.number().min(0), +}); + +const VUserReservations = z.object({ + reservationId: z.coerce.number().min(0), + reservedBookInfoId: z.coerce.number().min(0), + reservationDate: z.coerce.date(), + endAt: z.coerce.date(), + ranking: z.coerce.number().min(0), + title: z.string(), + author: z.string(), + image: z.string(), + userId: z.coerce.number().min(0), +}); + +const getUsersResponseInnerSchema = z.object({ + id: z.coerce.number().int().describe('유저 번호').min(0), + email: z.string().describe('이메일'), + nickname: z.string().describe('닉네임'), + intraId: z.coerce.number().int().describe('인트라 고유 번호').min(0), + slack: z.string().describe('slack 멤버 Id'), + penaltyEndDate: z.string().describe('패널티 끝나는 날짜'), + overDueDay: z.coerce.number().describe('현재 연체된 날수').min(0), + role: z.coerce.number().int().describe('권한'), + reservations: z.array(VUserReservations).describe('해당 유저의 예약 정보'), + lendings: z.array(lending).describe('해당 유저의 대출 정보'), +}); + +const getUsersResponseMetaSchema = z.object({ + totalItems: z.coerce.number().int().describe('전체 검색 결과 수'), + itemCount: z.coerce.number().int().describe('현재 페이지 검색 결과 수'), + itemsPerPage: z.coerce.number().int().describe('페이지 당 검색 결과 수'), + totalPages: z.coerce.number().int().describe('전체 결과 페이지 수'), + currentPage: z.coerce.number().int().describe('현재 페이지'), +}); + +export const getUsersResponseSchema = z.object({ + items: z.array(getUsersResponseInnerSchema).describe('유저 정보 목록'), + meta: getUsersResponseMetaSchema.describe('유저 수와 관련된 정보'), +}); + +export const createUsersRequestSchema = z.object({ + email: z.string().describe('이메일'), + password: z.string().describe('비밀번호'), +}); + +export const updateUsersParamSchema = z.object({ + id: z.coerce.number().int().describe('변경할 유저의 id 값').min(0), +}); + +export const updateUsersRequestSchema = z.object({ + nickname: z.string(), + intraId: z.coerce.number().int().min(0).describe('인트라 ID'), + slack: z.string().describe('slack 멤버 변수'), + role: z.coerce.number().int().describe('유저의 권한'), + penaltyEndDate: z.date().describe('패널티 끝나는 날짜'), +}); + +export const updateUsersResponseSchema = updateUsersRequestSchema; + +export const myUpdateUsersRequestSchema = createUsersRequestSchema; + +export const getAPIVersionResponseSchema = z.object({ + version: extendApi(z.string().describe('API 버전'), { + example: 'gshim.v1', + }), +}); + +export const getMyUserInfoResponseSchema = z.object({ + nickname: extendApi(z.string().describe('닉네임'), { example: 'jimin' }), + intraId: extendApi(z.number().describe('인트라 ID'), { example: 10035 }), + slack: extendApi(z.string().describe('slack 멤버 변수'), { + example: 'U02LNNDRC9F', + }), + role: extendApi(z.coerce.number().describe('유저의 권한'), { example: 2 }), + penaltyEndDate: extendApi(z.date().describe('패널티 끝나는 날짜'), { + example: '2021-08-01', + }), + overDueDay: extendApi(z.coerce.number().describe('현재 연체된 날수'), { + example: 0, + }), + reservations: extendApi(z.array(VUserReservations).describe('해당 유저의 예약 정보'), { + example: [], + }), + lendings: extendApi(z.array(lending).describe('해당 유저의 대출 정보'), { + example: [], + }), +}); diff --git a/backend/src/users/users.controller.spec.ts b/backend/src/users/users.controller.spec.ts new file mode 100644 index 0000000..3e27c39 --- /dev/null +++ b/backend/src/users/users.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { UsersController } from './users.controller'; + +describe('UsersController', () => { + let controller: UsersController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [UsersController], + }).compile(); + + controller = module.get(UsersController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/backend/src/users/users.controller.ts b/backend/src/users/users.controller.ts new file mode 100644 index 0000000..7e5190a --- /dev/null +++ b/backend/src/users/users.controller.ts @@ -0,0 +1,225 @@ +import { + Body, + Controller, + Get, + Param, + Patch, + Post, + Query, +} from '@nestjs/common'; +import { ApiOperation, ApiResponse } from '@nestjs/swagger'; +import { + CreateUsersRequestDto, + GetAPIVersionResponseDto, + GetUsersRequestDto, + MyUpddateUsersRequestDto, + UpdateUsersRequestDto, + UpdateUsersResponseDto, +} from './dto/users.dto'; + +@Controller('users') +export class UsersController { + constructor() {} + + @Get('search') + @ApiOperation({ + summary: 'Search users', + description: '검색할 유저의 nickname or email', + tags: ['users'], + }) + @ApiResponse({ + status: 200, + description: '검색 결과를 반환한다.', + type: GetUsersRequestDto, + }) + @ApiResponse({ + status: 400, + description: '입력된 인자가 부적절합니다', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + errorCode: { + type: 'number', + description: '에러코드', + example: 200, + }, + }, + }, + }, + }, + }) + @ApiResponse({ + status: 500, + description: '', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + errorCode: { type: 'number', description: '에러코드', example: 1 }, + }, + }, + }, + }, + }) + async getUsers(@Query() getUsersRequestDto: GetUsersRequestDto) {} + + @Post('create') + @ApiOperation({ + summary: 'Create users', + description: '유저를 생성한다.', + tags: ['users'], + }) + @ApiResponse({ + status: 200, + description: '유저 생성 성공!', + }) + @ApiResponse({ + status: 400, + description: 'Client Error Bad Request', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + errorCode: { + type: 'number', + description: '에러코드', + example: 200, + }, + }, + }, + }, + }, + }) + async createUsers(@Body() createUsersRequestDto: CreateUsersRequestDto) {} + + @Post('update/:id') + @ApiOperation({ + description: '유저 정보를 변경한다.', + tags: ['users'], + }) + @ApiResponse({ + status: 200, + description: '유저 정보 수정 성공!', + type: UpdateUsersResponseDto, + }) + async updateUsers( + @Param('id') id: string, + @Body() updateUsersRequestDto: UpdateUsersRequestDto, + ) {} + + @Patch('myupdate') + @ApiOperation({ + description: '내 유저정보를 변경한다.', + tags: ['users'], + }) + @ApiResponse({ + status: 200, + description: '유저 정보 변경 성공!', + }) + @ApiResponse({ + status: 400, + description: '들어온 인자가 없습니다..', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + errorCode: { + type: 'number', + description: '에러코드', + example: 200, + }, + }, + }, + }, + }, + }) + @ApiResponse({ + status: 403, + description: '수정하려는 계정이 본인의 계정이 아닙니다', + content: { + 'application/json': { + schema: { + type: 'object', + description: 'error description', + properties: { + errorCode: { + type: 'number', + description: '에러코드', + example: 206, + }, + }, + }, + }, + }, + }) + @ApiResponse({ + status: 409, + description: '수정하려는 값이 중복됩니다', + content: { + 'application/json': { + schema: { + type: 'object', + description: '203, 204 에러', + properties: { + errorCode: { + type: 'number', + description: '에러코드', + example: 204, + }, + }, + }, + }, + }, + }) + @ApiResponse({ + status: 500, + description: 'Server Error', + content: { + 'application/json': { + schema: { + type: 'object', + description: 'error description', + properties: { + errorCode: { + type: 'number', + description: '에러코드', + example: 1, + }, + }, + }, + }, + }, + }) + async myUpdateUsers( + @Body() myUpddateUsersRequestDto: MyUpddateUsersRequestDto, + ) {} + + @Get('EasterEgg') + @ApiOperation({ + description: '집현전 개발 버전을 확인합니다.', + tags: ['users'], + }) + @ApiResponse({ + status: 200, + description: '집현전 개발 버전을 반환합니다.', + type: GetAPIVersionResponseDto, + }) + async getVersion() {} + + @Get('me') + @ApiOperation({ + description: '내 정보를 조회합니다.', + tags: ['users'], + }) + @ApiResponse({ + status: 200, + description: '내 정보를 반환합니다.', + type: UpdateUsersResponseDto, + }) + async getMyUserInfo() {} +} diff --git a/backend/src/users/users.module.ts b/backend/src/users/users.module.ts new file mode 100644 index 0000000..cd8e8ee --- /dev/null +++ b/backend/src/users/users.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { UsersController } from './users.controller'; +import { UsersService } from './users.service'; + +@Module({ + controllers: [UsersController], + providers: [UsersService] +}) +export class UsersModule { + +} diff --git a/backend/src/users/users.service.spec.ts b/backend/src/users/users.service.spec.ts new file mode 100644 index 0000000..62815ba --- /dev/null +++ b/backend/src/users/users.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { UsersService } from './users.service'; + +describe('UsersService', () => { + let service: UsersService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [UsersService], + }).compile(); + + service = module.get(UsersService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/backend/src/users/users.service.ts b/backend/src/users/users.service.ts new file mode 100644 index 0000000..ef0d82d --- /dev/null +++ b/backend/src/users/users.service.ts @@ -0,0 +1,4 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class UsersService {} From 4af1471514637795b52efbb202c8715276319cec Mon Sep 17 00:00:00 2001 From: Minseo Park Date: Sun, 22 Sep 2024 17:28:38 +0900 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20RESTful=20=ED=95=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EC=9D=80=20endpoint=20deprecated=20=EC=B2=98=EB=A6=AC?= =?UTF-8?q?=20=EB=B0=8F=20RESTful=20=ED=95=9C=20=EA=B2=BD=EB=A1=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/users/users.controller.ts | 184 +++++++++++++++++++++++++- 1 file changed, 183 insertions(+), 1 deletion(-) diff --git a/backend/src/users/users.controller.ts b/backend/src/users/users.controller.ts index 7e5190a..d8f581a 100644 --- a/backend/src/users/users.controller.ts +++ b/backend/src/users/users.controller.ts @@ -22,6 +22,52 @@ export class UsersController { constructor() {} @Get('search') + @ApiOperation({ + summary: 'Search users', + description: '검색할 유저의 nickname or email', + deprecated: true, + tags: ['users'], + }) + @ApiResponse({ + status: 200, + description: '검색 결과를 반환한다.', + type: GetUsersRequestDto, + }) + @ApiResponse({ + status: 400, + description: '입력된 인자가 부적절합니다', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + errorCode: { + type: 'number', + description: '에러코드', + example: 200, + }, + }, + }, + }, + }, + }) + @ApiResponse({ + status: 500, + description: '', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + errorCode: { type: 'number', description: '에러코드', example: 1 }, + }, + }, + }, + }, + }) + async getUsersDeprecated(@Query() getUsersRequestDto: GetUsersRequestDto) {} + + @Get() @ApiOperation({ summary: 'Search users', description: '검색할 유저의 nickname or email', @@ -67,6 +113,37 @@ export class UsersController { async getUsers(@Query() getUsersRequestDto: GetUsersRequestDto) {} @Post('create') + @ApiOperation({ + summary: 'Create users', + description: '유저를 생성한다.', + deprecated: true, + tags: ['users'], + }) + @ApiResponse({ + status: 200, + description: '유저 생성 성공!', + }) + @ApiResponse({ + status: 400, + description: 'Client Error Bad Request', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + errorCode: { + type: 'number', + description: '에러코드', + example: 200, + }, + }, + }, + }, + }, + }) + async createUsersDeprecated(@Body() createUsersRequestDto: CreateUsersRequestDto) {} + + @Post() @ApiOperation({ summary: 'Create users', description: '유저를 생성한다.', @@ -97,6 +174,22 @@ export class UsersController { async createUsers(@Body() createUsersRequestDto: CreateUsersRequestDto) {} @Post('update/:id') + @ApiOperation({ + description: '유저 정보를 변경한다.', + deprecated: true, + tags: ['users'], + }) + @ApiResponse({ + status: 200, + description: '유저 정보 수정 성공!', + type: UpdateUsersResponseDto, + }) + async updateUsersDeprecated( + @Param('id') id: string, + @Body() updateUsersRequestDto: UpdateUsersRequestDto, + ) {} + + @Post(':id') @ApiOperation({ description: '유저 정보를 변경한다.', tags: ['users'], @@ -112,6 +205,95 @@ export class UsersController { ) {} @Patch('myupdate') + @ApiOperation({ + description: '내 유저정보를 변경한다.', + deprecated: true, + tags: ['users'], + }) + @ApiResponse({ + status: 200, + description: '유저 정보 변경 성공!', + }) + @ApiResponse({ + status: 400, + description: '들어온 인자가 없습니다..', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + errorCode: { + type: 'number', + description: '에러코드', + example: 200, + }, + }, + }, + }, + }, + }) + @ApiResponse({ + status: 403, + description: '수정하려는 계정이 본인의 계정이 아닙니다', + content: { + 'application/json': { + schema: { + type: 'object', + description: 'error description', + properties: { + errorCode: { + type: 'number', + description: '에러코드', + example: 206, + }, + }, + }, + }, + }, + }) + @ApiResponse({ + status: 409, + description: '수정하려는 값이 중복됩니다', + content: { + 'application/json': { + schema: { + type: 'object', + description: '203, 204 에러', + properties: { + errorCode: { + type: 'number', + description: '에러코드', + example: 204, + }, + }, + }, + }, + }, + }) + @ApiResponse({ + status: 500, + description: 'Server Error', + content: { + 'application/json': { + schema: { + type: 'object', + description: 'error description', + properties: { + errorCode: { + type: 'number', + description: '에러코드', + example: 1, + }, + }, + }, + }, + }, + }) + async myUpdateUsersDeprecated( + @Body() myUpddateUsersRequestDto: MyUpddateUsersRequestDto, + ) {} + + @Patch('/me') @ApiOperation({ description: '내 유저정보를 변경한다.', tags: ['users'], @@ -199,7 +381,7 @@ export class UsersController { @Body() myUpddateUsersRequestDto: MyUpddateUsersRequestDto, ) {} - @Get('EasterEgg') + @Get('EasterEgg') // suggesting to change this to 'version' @ApiOperation({ description: '집현전 개발 버전을 확인합니다.', tags: ['users'], From 168fb44be75fc3674c13467d38698b4117178fbc Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sun, 22 Sep 2024 10:07:08 +0000 Subject: [PATCH 3/3] [autofix.ci] apply automated fixes --- backend/src/users/dto/users.dto.ts | 2 +- backend/src/users/schema/users.schema.ts | 9 ++++++--- backend/src/users/users.controller.ts | 12 +++++++----- backend/src/users/users.module.ts | 6 ++---- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/backend/src/users/dto/users.dto.ts b/backend/src/users/dto/users.dto.ts index 5e00ba9..8dc5f49 100644 --- a/backend/src/users/dto/users.dto.ts +++ b/backend/src/users/dto/users.dto.ts @@ -39,4 +39,4 @@ export class GetAPIVersionResponseDto extends createZodDto( export class GetMyUserInfoResponseDto extends createZodDto( getMyUserInfoResponseSchema, -) {} \ No newline at end of file +) {} diff --git a/backend/src/users/schema/users.schema.ts b/backend/src/users/schema/users.schema.ts index a0d0466..a7ccf8c 100644 --- a/backend/src/users/schema/users.schema.ts +++ b/backend/src/users/schema/users.schema.ts @@ -103,9 +103,12 @@ export const getMyUserInfoResponseSchema = z.object({ overDueDay: extendApi(z.coerce.number().describe('현재 연체된 날수'), { example: 0, }), - reservations: extendApi(z.array(VUserReservations).describe('해당 유저의 예약 정보'), { - example: [], - }), + reservations: extendApi( + z.array(VUserReservations).describe('해당 유저의 예약 정보'), + { + example: [], + }, + ), lendings: extendApi(z.array(lending).describe('해당 유저의 대출 정보'), { example: [], }), diff --git a/backend/src/users/users.controller.ts b/backend/src/users/users.controller.ts index d8f581a..294a3c3 100644 --- a/backend/src/users/users.controller.ts +++ b/backend/src/users/users.controller.ts @@ -66,7 +66,7 @@ export class UsersController { }, }) async getUsersDeprecated(@Query() getUsersRequestDto: GetUsersRequestDto) {} - + @Get() @ApiOperation({ summary: 'Search users', @@ -141,8 +141,10 @@ export class UsersController { }, }, }) - async createUsersDeprecated(@Body() createUsersRequestDto: CreateUsersRequestDto) {} - + async createUsersDeprecated( + @Body() createUsersRequestDto: CreateUsersRequestDto, + ) {} + @Post() @ApiOperation({ summary: 'Create users', @@ -188,7 +190,7 @@ export class UsersController { @Param('id') id: string, @Body() updateUsersRequestDto: UpdateUsersRequestDto, ) {} - + @Post(':id') @ApiOperation({ description: '유저 정보를 변경한다.', @@ -292,7 +294,7 @@ export class UsersController { async myUpdateUsersDeprecated( @Body() myUpddateUsersRequestDto: MyUpddateUsersRequestDto, ) {} - + @Patch('/me') @ApiOperation({ description: '내 유저정보를 변경한다.', diff --git a/backend/src/users/users.module.ts b/backend/src/users/users.module.ts index cd8e8ee..440ef36 100644 --- a/backend/src/users/users.module.ts +++ b/backend/src/users/users.module.ts @@ -4,8 +4,6 @@ import { UsersService } from './users.service'; @Module({ controllers: [UsersController], - providers: [UsersService] + providers: [UsersService], }) -export class UsersModule { - -} +export class UsersModule {}