Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: 식물 카드 상세 조회 API #33

Merged
merged 11 commits into from
Nov 4, 2023
18 changes: 13 additions & 5 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
## 📚 PR 요약 / Linked Issue
해당 PR에서 작업한 내용을 한 줄로 요약해주세요.

해당 PR에서 작업한 내용을 한 줄로 요약해주세요.
close #{no}

## 💡 변경 사항

디테일한 작업 내역을 적어주세요.
주의할 사항이 있다면 적어주세요.
변경사항 (모듈 설치 등)이 있다면 적어주세요.

## ✅ PR check list
- [ ] 커밋 컨벤션, 제목 등을 확인했나요?
- [ ] 알맞은 라벨을 달았나요?
- [ ] 셀프 코드리뷰를 작성했나요?
## 📖 Swagger

API Swagger 를 추가했다면 캡쳐해주세요.

## ✅ PR check list

- [ ] 테스트 코드를 작성했나요? (unit/e2e)
- [ ] 커밋 컨벤션, 제목 등을 확인했나요?
- [ ] 알맞은 라벨을 달았나요?
- [ ] 셀프 코드리뷰를 작성했나요?
1 change: 1 addition & 0 deletions src/common/objects/response-message.object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const RESPONSE_MESSAGE: {
REISSUED_TOKEN_SUCCESS: '토큰 재발급 성공',

// plants
READ_PLANT_DETAIL_SUCCESS: '식물 상세 조회 성공',
READ_PLANT_INFORMATION_SUCCESS: '식물 단계 조회 성공',
READ_PLANT_WATER_LOG_SUCCESS: '식물 물주기 기록 조회 성공',
};
13 changes: 11 additions & 2 deletions src/constants/swagger/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import { SIGNIN_DESCRIPTION } from './auth';
import { PLANT_INFORMATION, PLANT_WATER_LOG } from './plants';
import {
READ_PLANT_DETAIL,
READ_PLANT_INFORMATION,
READ_PLANT_WATER_LOG,
} from './plants';

export const ERROR_DESCRIPTION = {
INTERNAL_SERVER_ERROR: 'Internal Server Error',
};

export { SIGNIN_DESCRIPTION, PLANT_INFORMATION, PLANT_WATER_LOG };
export {
SIGNIN_DESCRIPTION,
READ_PLANT_DETAIL,
READ_PLANT_INFORMATION,
READ_PLANT_WATER_LOG,
};
56 changes: 46 additions & 10 deletions src/constants/swagger/plants.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,45 @@
export const PLANT_INFORMATION = {
export const READ_PLANT_DETAIL = {
API_OPERATION: {
SUMMARY: '식물 정보 조회 API',
DESCRIPTION: '식물 단계별 정보를 조회합니다.',
summary: '식물 상세 조회 API',
description: '식물 카드 상세 정보를 조회합니다.',
},
API_PARAM: {
NAME: 'id',
DESCRIPTION: 'plant id',
type: Number,
name: 'id',
required: true,
description: 'userPlant id',
},
DTO_DESCRIPTION: {
RESPONSE: {
ID: 'userPlant id',
NICKNAME: '식물 닉네임',
DURATION: '함께한 날',
INSTAGRAM: '인스타그램 id',
D_DAY: '물 주기 D-Day',
PLANT_ID: 'plant id',
PLANT_IMAGE: '식물 이미지 url',
PLANT_NAME: '식물 이름',
LEVEL_NAME: '식물 레벨 이름',
STATUS_MESSAGE: '식물 상태 메시지',
STATUS_GAGUE: '식물 상태 게이지',
},
},
ERROR_DESCRIPTION: {
BAD_REQUEST: 'Bad Request - 요청 id 가 없거나, number가 아닌 경우 등',
NOT_FOUND: 'Not Found - 요청한 id에 해당하는 식물 자원이 없는 경우',
},
};

export const READ_PLANT_INFORMATION = {
API_OPERATION: {
summary: '식물 정보 조회 API',
description: '식물 단계별 정보를 조회합니다.',
},
API_PARAM: {
type: Number,
name: 'id',
required: true,
description: 'plant id',
},
DTO_DESCRIPTION: {
RESPONSE: {
Expand All @@ -27,14 +61,16 @@ export const PLANT_INFORMATION = {
},
};

export const PLANT_WATER_LOG = {
export const READ_PLANT_WATER_LOG = {
API_OPERATION: {
SUMMARY: '식물 물주기 조회 API',
DESCRIPTION: '식물별 물주기 기록을 조회합니다.',
summary: '식물 물주기 조회 API',
description: '식물별 물주기 기록을 조회합니다.',
},
API_PARAM: {
NAME: 'id',
DESCRIPTION: 'user_plant_id',
type: Number,
name: 'id',
required: true,
description: 'user_plant_id',
},
DTO_DESCRIPTION: {
RESPONSE: {
Expand Down
34 changes: 34 additions & 0 deletions src/plants/constants/plant-status.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
export const PLANT_STATUS: Record<
string,
{ statusMessage: string; statusGague: number }
> = {
healthy: {
statusMessage: '힘이 솟아요',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[] <- 요거 머죠!?!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거 왜 모바일에선 너가 말한것처럼 이상한 기호 보이고 pc 에선 안보임 ㄷ ㄷ
image

statusGague: 1,
},
waterDay: {
statusMessage: '물 주는 날이에요!',
statusGague: 1,
},
happy: {
statusMessage: '기분이 좋아요',
statusGague: 0.75,
},
thirsty: {
statusMessage: '갈증나요',
statusGague: 0.5,
},
veryThirsty: {
statusMessage: '물 주세요',
statusGague: 0.25,
},
};

export const PLANT_D_DAY: Record<number, number[]> = {
1: [0, 3, 7],
2: [0, 4, 13],
3: [0, 6, 13],
4: [0, 3, 7],
5: [0, 3, 7],
6: [0, 3, 7],
};
Comment on lines +27 to +34
Copy link
Member Author

@jokj624 jokj624 Aug 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

plant health status 가 바뀌는 기준 데이입니다.
key : plantId

48 changes: 48 additions & 0 deletions src/plants/dto/response-plant-detail.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { ApiProperty } from '@nestjs/swagger';
import { ResponseSuccessDto } from 'src/common/dto/response-success.dto';
import { READ_PLANT_DETAIL } from 'src/constants/swagger';

const DTO_RESPONSE_DESCRIPTION = READ_PLANT_DETAIL.DTO_DESCRIPTION.RESPONSE;

export class ResponsePlantDetailData {
@ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.ID })
id: number;

@ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.NICKNAME })
nickname: string;

@ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.DURATION })
duration: number;

@ApiProperty({
required: false,
description: DTO_RESPONSE_DESCRIPTION.INSTAGRAM,
})
instagram?: string;

@ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.D_DAY })
dDay: number;

@ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.PLANT_ID })
plantId: number;

@ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.PLANT_IMAGE })
plantImage: string;

@ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.PLANT_NAME })
plantName: string;

@ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.LEVEL_NAME })
levelName: string;

@ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.STATUS_MESSAGE })
statusMessage: string;

@ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.STATUS_GAGUE })
statusGague: number;
}

export class ResponsePlantDetailDto extends ResponseSuccessDto {
@ApiProperty()
data: ResponsePlantDetailData;
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { ApiProperty } from '@nestjs/swagger';
import { ResponseSuccessDto } from 'src/common/dto/response-success.dto';
import { PLANT_INFORMATION } from 'src/constants/swagger';
import { READ_PLANT_INFORMATION } from 'src/constants/swagger';

const DTO_RESPONSE_DESCRIPTION = PLANT_INFORMATION.DTO_DESCRIPTION.RESPONSE;
const DTO_RESPONSE_DESCRIPTION =
READ_PLANT_INFORMATION.DTO_DESCRIPTION.RESPONSE;

export class PlantLevelData {
@ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.PLANT_LEVEL.LEVEL_NAME })
Expand Down Expand Up @@ -30,7 +31,10 @@ export class ResponsePlantInformationData {
@ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.EXPLANATION })
explanation: string;

@ApiProperty()
@ApiProperty({
isArray: true,
type: PlantLevelData,
})
plantLevel: PlantLevelData[];

@ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.CIRCLE_IMAGE_URL })
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ApiProperty } from '@nestjs/swagger';
import { ResponseSuccessDto } from 'src/common/dto/response-success.dto';
import { PLANT_WATER_LOG } from 'src/constants/swagger';
import { READ_PLANT_WATER_LOG } from 'src/constants/swagger';

const DTO_RESPONSE_DESCRIPTION = PLANT_WATER_LOG.DTO_DESCRIPTION.RESPONSE;
const DTO_RESPONSE_DESCRIPTION = READ_PLANT_WATER_LOG.DTO_DESCRIPTION.RESPONSE;

export class PlantWaterReviewData {
@ApiProperty({ description: DTO_RESPONSE_DESCRIPTION.REVIEWS.ID })
Expand Down
21 changes: 20 additions & 1 deletion src/plants/plants.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import { Test, TestingModule } from '@nestjs/testing';
import { PlantsController } from './plants.controller';
import { PlantsService } from './plants.service';

import { mockPlant } from '../../test/mock/plants.mock';
import {
mockPlant,
mockUserPlantDetailData,
mockUserPlantDetailSuccessResponse,
} from '../../test/mock/plants.mock';

describe('PlantsController', () => {
let controller: PlantsController;
Expand All @@ -16,6 +20,7 @@ describe('PlantsController', () => {
{
provide: PlantsService,
useValue: {
getUserPlantDetail: jest.fn(),
getPlantInformation: jest.fn(),
getPlantWaterLog: jest.fn(),
},
Expand All @@ -31,6 +36,20 @@ describe('PlantsController', () => {
expect(controller).toBeDefined();
});

describe('get plant detail by userPlantId', () => {
const mockUserPlantId: number = 1;

it('존재하는 식물 id가 주어지면 성공 response 반환', async () => {
jest
.spyOn(service, 'getUserPlantDetail')
.mockResolvedValueOnce(mockUserPlantDetailData);

const result = await controller.getPlantDetail({ id: mockUserPlantId });

expect(result).toEqual(mockUserPlantDetailSuccessResponse);
});
});

describe('get plant information by plantId', () => {
const mockPlantId: number = 1;
const mockResult = {
Expand Down
61 changes: 34 additions & 27 deletions src/plants/plants.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,18 @@ import {
} from '@nestjs/swagger';

import { PlantsService } from './plants.service';
import { ResponsePlantInformationDto } from './dto/response-plantInformation.dto';
import { ResponsePlantInformationDto } from './dto/response-plant-information.dto';
import { CommonParamsDto } from 'src/common/dto/common-params.dto';
import {
ERROR_DESCRIPTION,
PLANT_INFORMATION,
PLANT_WATER_LOG,
READ_PLANT_DETAIL,
READ_PLANT_INFORMATION,
READ_PLANT_WATER_LOG,
} from 'src/constants/swagger';
import { wrapSuccess } from 'src/utils/success';
import { RESPONSE_MESSAGE } from 'src/common/objects';
import { ResponsePlantWaterLogDto } from './dto/response-plantwaterlog.dto';

import { ResponsePlantWaterLogDto } from './dto/response-plant-water-log.dto';
import { ResponsePlantDetailDto } from './dto/response-plant-detail.dto';
@Controller('plants')
@ApiTags('Plants')
@ApiInternalServerErrorResponse({
Expand All @@ -29,23 +30,37 @@ import { ResponsePlantWaterLogDto } from './dto/response-plantwaterlog.dto';
export class PlantsController {
constructor(private readonly plantsService: PlantsService) {}

@Get(':id/information')
@ApiOperation({
summary: PLANT_INFORMATION.API_OPERATION.SUMMARY,
description: PLANT_INFORMATION.API_OPERATION.DESCRIPTION,
@Get(':id')
@ApiOperation(READ_PLANT_DETAIL.API_OPERATION)
@ApiParam(READ_PLANT_DETAIL.API_PARAM)
Comment on lines +34 to +35
Copy link
Member Author

@jokj624 jokj624 Aug 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ApiOperation, ApiParam 을 불필요하게 속성 분리해서 넣고 있어서 그냥 한번에 객체 자체로 집어 넣을 수 있게 바꿨습니다.

@ApiOkResponse({ type: ResponsePlantDetailDto })
@ApiBadRequestResponse({
description: READ_PLANT_DETAIL.ERROR_DESCRIPTION.BAD_REQUEST,
})
@ApiParam({
type: Number,
name: PLANT_INFORMATION.API_PARAM.NAME,
required: true,
description: PLANT_INFORMATION.API_PARAM.DESCRIPTION,
@ApiNotFoundResponse({
description: READ_PLANT_DETAIL.ERROR_DESCRIPTION.NOT_FOUND,
})
async getPlantDetail(
@Param() { id }: CommonParamsDto,
): Promise<ResponsePlantDetailDto> {
const data = await this.plantsService.getUserPlantDetail(id);

return wrapSuccess(
HttpStatus.OK,
RESPONSE_MESSAGE.READ_PLANT_DETAIL_SUCCESS,
data,
);
}

@Get(':id/information')
@ApiOperation(READ_PLANT_INFORMATION.API_OPERATION)
@ApiParam(READ_PLANT_INFORMATION.API_PARAM)
@ApiOkResponse({ type: ResponsePlantInformationDto })
@ApiBadRequestResponse({
description: PLANT_INFORMATION.ERROR_DESCRIPTION.BAD_REQUEST,
description: READ_PLANT_INFORMATION.ERROR_DESCRIPTION.BAD_REQUEST,
})
@ApiNotFoundResponse({
description: PLANT_INFORMATION.ERROR_DESCRIPTION.NOT_FOUND,
description: READ_PLANT_INFORMATION.ERROR_DESCRIPTION.NOT_FOUND,
})
async getPlantInformation(
@Param() { id }: CommonParamsDto,
Expand All @@ -60,19 +75,11 @@ export class PlantsController {
}

@Get(':id/water')
@ApiOperation({
summary: PLANT_WATER_LOG.API_OPERATION.SUMMARY,
description: PLANT_WATER_LOG.API_OPERATION.DESCRIPTION,
})
@ApiParam({
type: Number,
name: PLANT_WATER_LOG.API_PARAM.NAME,
required: true,
description: PLANT_WATER_LOG.API_PARAM.DESCRIPTION,
})
@ApiOperation(READ_PLANT_WATER_LOG.API_OPERATION)
@ApiParam(READ_PLANT_WATER_LOG.API_PARAM)
@ApiOkResponse({ type: ResponsePlantWaterLogDto })
@ApiBadRequestResponse({
description: PLANT_WATER_LOG.ERROR_DESCRIPTION.BAD_REQUEST,
description: READ_PLANT_WATER_LOG.ERROR_DESCRIPTION.BAD_REQUEST,
})
async getPlantWaterLog(
@Param() { id }: CommonParamsDto,
Expand Down
Loading