diff --git a/server/build.gradle b/server/build.gradle index 4d89e69..b556195 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -125,6 +125,7 @@ tasks.register('copyDocument', Copy){ openapi3 { servers = [ { url = "http://localhost:8080" }, + { url = "http://127.0.0.1:8080" }, { url = "https://greenroom-server.site" } ] title = '그린룸 API DOCUMENT' diff --git a/server/src/docs/asciidoc/index.adoc b/server/src/docs/asciidoc/index.adoc index 895fa82..30989af 100644 --- a/server/src/docs/asciidoc/index.adoc +++ b/server/src/docs/asciidoc/index.adoc @@ -1034,3 +1034,26 @@ include::{snippets}/api/greenroom/activity/patch/1/response-fields.adoc[] 실패 1. include::{snippets}/api/greenroom/activity/patch/2/http-response.adoc[] + +=== **13. 식물 상세 정보를 조회** + +특정 식물의 상세 정보를 조회함. + +==== Request +include::{snippets}/api/plants/info/1/http-request.adoc[] + +==== Request Headers +include::{snippets}/api/plants/info/1/request-headers.adoc[] + +==== Request Path Parameter Fields +include::{snippets}/api/plants/info/1/path-parameters.adoc[] + +==== 성공 Response +include::{snippets}/api/plants/info/1/http-response.adoc[] + +==== Response Body Fields +include::{snippets}/api/plants/info/1/response-fields.adoc[] + +==== 실패 Response +실패 1. +include::{snippets}/api/plants/info/2/http-response.adoc[] diff --git a/server/src/main/java/com/greenroom/server/api/domain/greenroom/controller/PlantController.java b/server/src/main/java/com/greenroom/server/api/domain/greenroom/controller/PlantController.java index 4ba1468..d914618 100644 --- a/server/src/main/java/com/greenroom/server/api/domain/greenroom/controller/PlantController.java +++ b/server/src/main/java/com/greenroom/server/api/domain/greenroom/controller/PlantController.java @@ -35,4 +35,8 @@ public ResponseEntity getWateringInfo(@PathVariable(value = "plant_ return ResponseEntity.ok(ApiResponse.success(plantService.getWateringInfo(plantId))); } + @GetMapping("{plant_id}") + public ResponseEntity getPlantDetailInfo(@PathVariable(value = "plant_id")Long plantId){ + return ResponseEntity.ok(ApiResponse.success(plantService.getPlantDetailInfo(plantId))); + } } diff --git a/server/src/main/java/com/greenroom/server/api/domain/greenroom/dto/out/GreenroomDetailResponseDto.java b/server/src/main/java/com/greenroom/server/api/domain/greenroom/dto/out/GreenroomDetailResponseDto.java index 03b639d..93615c3 100644 --- a/server/src/main/java/com/greenroom/server/api/domain/greenroom/dto/out/GreenroomDetailResponseDto.java +++ b/server/src/main/java/com/greenroom/server/api/domain/greenroom/dto/out/GreenroomDetailResponseDto.java @@ -1,9 +1,8 @@ package com.greenroom.server.api.domain.greenroom.dto.out; import com.greenroom.server.api.domain.greenroom.entity.GreenRoom; -import com.greenroom.server.api.domain.greenroom.entity.Plant; import com.greenroom.server.api.domain.greenroom.entity.Todo; -import lombok.*; +import com.greenroom.server.api.global.config.PropertiesHolder; import java.time.LocalDate; import java.util.List; @@ -29,8 +28,8 @@ public static GreenroomDetailResponseDto of( } public record GreenroomBasicInfoDto (Long greenroomId, String nickName, String plantName, Integer duration, String memo, String imageUrl){ - public static GreenroomBasicInfoDto from(GreenRoom greenRoom,String cdnPath){ - String completeImageUrl = greenRoom.getPictureUrl()==null? null: cdnPath+"/"+greenRoom.getPictureUrl(); + public static GreenroomBasicInfoDto from(GreenRoom greenRoom){ + String completeImageUrl = greenRoom.getPictureUrl()==null? null: PropertiesHolder.CDN_PATH+"/"+greenRoom.getPictureUrl(); return new GreenroomBasicInfoDto(greenRoom.getGreenroomId(),greenRoom.getName(), greenRoom.getPlant()==null?null:greenRoom.getPlant().getCommonName(),LocalDate.now().getDayOfYear()- greenRoom.getCreateDate().getDayOfYear()+1,greenRoom.getMemo(), completeImageUrl); } } @@ -40,16 +39,4 @@ public static GreenroomManagementInfoDto from(Todo todo){ return new GreenroomManagementInfoDto(todo.getActivity().getActivityId(),todo.getActivity().getActivityName(),todo.getTerm(),todo.getNextTodoDate().getDayOfYear()-LocalDate.now().getDayOfYear());} } - public record PlantInfoDto (Long plantId,String name,String scientificName,String description){ - public static PlantInfoDto from(Plant plant){ - return new PlantInfoDto(plant.getPlantId(),plant.getCommonName(),plant.getScientificName(),plant.getOtherInformation()); - } - } - - public record PlantManagementInfoDto (String managementLevel,String temperature,String sunlight,String watering,String humidity,String fertilizer){ - public static PlantManagementInfoDto from(Plant plant){ - return new PlantManagementInfoDto(plant.getManageLevel(),plant.getGrowthTemperature(), plant.getLightDemand(), plant.getWaterCycle(), plant.getHumidity(),plant.getFertilizer()); - } - } - } diff --git a/server/src/main/java/com/greenroom/server/api/domain/greenroom/dto/out/GreenroomInfoResponseDto.java b/server/src/main/java/com/greenroom/server/api/domain/greenroom/dto/out/GreenroomInfoResponseDto.java index 1fb3bfc..fc185d4 100644 --- a/server/src/main/java/com/greenroom/server/api/domain/greenroom/dto/out/GreenroomInfoResponseDto.java +++ b/server/src/main/java/com/greenroom/server/api/domain/greenroom/dto/out/GreenroomInfoResponseDto.java @@ -3,6 +3,7 @@ import com.greenroom.server.api.domain.greenroom.entity.Activity; import com.greenroom.server.api.domain.greenroom.entity.GreenRoom; import com.greenroom.server.api.domain.greenroom.entity.Item; +import com.greenroom.server.api.global.config.PropertiesHolder; import lombok.AllArgsConstructor; import lombok.Data; import lombok.extern.slf4j.Slf4j; @@ -21,8 +22,8 @@ public static GreenroomInfoResponseDto of(GreenroomBasicInfoDto basicInfo, Green return new GreenroomInfoResponseDto(basicInfo,todo,customItems); } public record GreenroomBasicInfoDto(Long greenroomId,String plantNickname,String plantName, String imageUrl,String memo){ - public static GreenroomBasicInfoDto from(GreenRoom greenRoom,String cdnPath){ - return new GreenroomBasicInfoDto(greenRoom.getGreenroomId(), greenRoom.getName(), greenRoom.getPlant()==null?null:greenRoom.getPlant().getCommonName() , greenRoom.getPictureUrl()==null?null:cdnPath+"/"+greenRoom.getPictureUrl() ,greenRoom.getMemo()); + public static GreenroomBasicInfoDto from(GreenRoom greenRoom){ + return new GreenroomBasicInfoDto(greenRoom.getGreenroomId(), greenRoom.getName(), greenRoom.getPlant()==null?null:greenRoom.getPlant().getCommonName() , greenRoom.getPictureUrl()==null?null: PropertiesHolder.CDN_PATH+"/"+greenRoom.getPictureUrl() ,greenRoom.getMemo()); } } public record GreenroomTodoInfoDto(List todoList, Integer numberOfTodo){ diff --git a/server/src/main/java/com/greenroom/server/api/domain/greenroom/dto/out/PlantDetailInfoResponseDto.java b/server/src/main/java/com/greenroom/server/api/domain/greenroom/dto/out/PlantDetailInfoResponseDto.java new file mode 100644 index 0000000..297fcbd --- /dev/null +++ b/server/src/main/java/com/greenroom/server/api/domain/greenroom/dto/out/PlantDetailInfoResponseDto.java @@ -0,0 +1,10 @@ +package com.greenroom.server.api.domain.greenroom.dto.out; + + +import com.greenroom.server.api.domain.greenroom.entity.Plant; + +public record PlantDetailInfoResponseDto(PlantInfoDto plantInfo, PlantManagementInfoDto plantManagementInfo) { + public static PlantDetailInfoResponseDto from(Plant plant){ + return new PlantDetailInfoResponseDto(PlantInfoDto.from(plant),PlantManagementInfoDto.from(plant)); + } +} diff --git a/server/src/main/java/com/greenroom/server/api/domain/greenroom/dto/out/PlantInfoDto.java b/server/src/main/java/com/greenroom/server/api/domain/greenroom/dto/out/PlantInfoDto.java new file mode 100644 index 0000000..56cffb5 --- /dev/null +++ b/server/src/main/java/com/greenroom/server/api/domain/greenroom/dto/out/PlantInfoDto.java @@ -0,0 +1,12 @@ +package com.greenroom.server.api.domain.greenroom.dto.out; + +import com.greenroom.server.api.domain.greenroom.entity.Plant; +import com.greenroom.server.api.global.config.PropertiesHolder; +import org.springframework.beans.factory.annotation.Value; + +public record PlantInfoDto (Long plantId, String name, String scientificName, String description, String imageUrl){ + + public static PlantInfoDto from(Plant plant){ + return new PlantInfoDto(plant.getPlantId(),plant.getCommonName(),plant.getScientificName(),plant.getOtherInformation(), PropertiesHolder.CDN_PATH +"/"+ plant.getPlantPictureUrlS3()); + } +} \ No newline at end of file diff --git a/server/src/main/java/com/greenroom/server/api/domain/greenroom/dto/out/PlantManagementInfoDto.java b/server/src/main/java/com/greenroom/server/api/domain/greenroom/dto/out/PlantManagementInfoDto.java new file mode 100644 index 0000000..a355494 --- /dev/null +++ b/server/src/main/java/com/greenroom/server/api/domain/greenroom/dto/out/PlantManagementInfoDto.java @@ -0,0 +1,10 @@ +package com.greenroom.server.api.domain.greenroom.dto.out; + +import com.greenroom.server.api.domain.greenroom.entity.Plant; + + +public record PlantManagementInfoDto (String managementLevel,String temperature,String sunlight,String watering,String humidity,String fertilizer){ + public static PlantManagementInfoDto from(Plant plant){ + return new PlantManagementInfoDto(plant.getManageLevel(),plant.getGrowthTemperature(), plant.getLightDemand(), plant.getWaterCycle(), plant.getHumidity(),plant.getFertilizer()); + } +} \ No newline at end of file diff --git a/server/src/main/java/com/greenroom/server/api/domain/greenroom/service/GreenroomResponseAssembler.java b/server/src/main/java/com/greenroom/server/api/domain/greenroom/service/GreenroomResponseAssembler.java index 1cb96fd..280c07e 100644 --- a/server/src/main/java/com/greenroom/server/api/domain/greenroom/service/GreenroomResponseAssembler.java +++ b/server/src/main/java/com/greenroom/server/api/domain/greenroom/service/GreenroomResponseAssembler.java @@ -1,8 +1,6 @@ package com.greenroom.server.api.domain.greenroom.service; -import com.greenroom.server.api.domain.greenroom.dto.out.GreenroomDetailResponseDto; -import com.greenroom.server.api.domain.greenroom.dto.out.GreenroomInfoResponseDto; -import com.greenroom.server.api.domain.greenroom.dto.out.ItemSimpleDto; +import com.greenroom.server.api.domain.greenroom.dto.out.*; import com.greenroom.server.api.domain.greenroom.entity.GreenRoom; import com.greenroom.server.api.domain.greenroom.entity.Plant; import org.springframework.beans.factory.annotation.Value; @@ -13,10 +11,6 @@ @Component public class GreenroomResponseAssembler { - - @Value("${cloud.cdn.path.root}") - private String cdnPath; - private final AdornmentService adornmentService; private final TodoService todoService; @@ -27,7 +21,7 @@ public GreenroomResponseAssembler(AdornmentService adornmentService, TodoService public GreenroomDetailResponseDto toDetailResponse(GreenRoom greenRoom) { - GreenroomDetailResponseDto.GreenroomBasicInfoDto greenroomBasicInfoDto = GreenroomDetailResponseDto.GreenroomBasicInfoDto.from(greenRoom,cdnPath); + GreenroomDetailResponseDto.GreenroomBasicInfoDto greenroomBasicInfoDto = GreenroomDetailResponseDto.GreenroomBasicInfoDto.from(greenRoom); Map decoration = adornmentService.getGreenroomSimpleAdornmentInfo(greenRoom); @@ -35,9 +29,9 @@ public GreenroomDetailResponseDto toDetailResponse(GreenRoom greenRoom) { Plant plant = greenRoom.getPlant(); - GreenroomDetailResponseDto.PlantInfoDto plantInfo = plant==null? null: GreenroomDetailResponseDto.PlantInfoDto.from(plant); + PlantInfoDto plantInfo = plant==null? null: PlantInfoDto.from(plant); - GreenroomDetailResponseDto.PlantManagementInfoDto plantManagementInfo = plant==null? null: GreenroomDetailResponseDto.PlantManagementInfoDto.from(plant); + PlantManagementInfoDto plantManagementInfo = plant==null? null: PlantManagementInfoDto.from(plant); return GreenroomDetailResponseDto.of(greenroomBasicInfoDto,decoration,managementInfo,plantInfo,plantManagementInfo); } @@ -45,7 +39,7 @@ public GreenroomDetailResponseDto toDetailResponse(GreenRoom greenRoom) { public GreenroomInfoResponseDto toGreenroomInfo(GreenRoom greenRoom){ //user의 greenroom 기본 정보 조회 - GreenroomInfoResponseDto.GreenroomBasicInfoDto greenroomBasicInfo= GreenroomInfoResponseDto.GreenroomBasicInfoDto.from(greenRoom,cdnPath); + GreenroomInfoResponseDto.GreenroomBasicInfoDto greenroomBasicInfo= GreenroomInfoResponseDto.GreenroomBasicInfoDto.from(greenRoom); //user의 greenroom todo 조회 GreenroomInfoResponseDto.GreenroomTodoInfoDto greenroomTodoInfo = todoService.getGreenroomTodoInfo(greenRoom); diff --git a/server/src/main/java/com/greenroom/server/api/domain/greenroom/service/PlantService.java b/server/src/main/java/com/greenroom/server/api/domain/greenroom/service/PlantService.java index 7c8c8a3..6fc0d99 100644 --- a/server/src/main/java/com/greenroom/server/api/domain/greenroom/service/PlantService.java +++ b/server/src/main/java/com/greenroom/server/api/domain/greenroom/service/PlantService.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.greenroom.server.api.domain.greenroom.document.PlantDocument; +import com.greenroom.server.api.domain.greenroom.dto.out.PlantDetailInfoResponseDto; import com.greenroom.server.api.domain.greenroom.dto.out.PlantResponseDto; import com.greenroom.server.api.domain.greenroom.dto.out.PlantWateringInfoResponseDto; import com.greenroom.server.api.domain.greenroom.entity.Plant; @@ -123,5 +124,11 @@ public PlantWateringInfoResponseDto getWateringInfo(Long plantId){ return PlantWateringInfoResponseDto.of(plantId,plant.getCommonName(),plant.getWaterCycle()); } + public PlantDetailInfoResponseDto getPlantDetailInfo(Long plantId){ + Plant plant = findPlantById(plantId); // 없으면 not found + return PlantDetailInfoResponseDto.from(plant); + } + + } \ No newline at end of file diff --git a/server/src/main/java/com/greenroom/server/api/global/config/PropertiesHolder.java b/server/src/main/java/com/greenroom/server/api/global/config/PropertiesHolder.java new file mode 100644 index 0000000..21bb060 --- /dev/null +++ b/server/src/main/java/com/greenroom/server/api/global/config/PropertiesHolder.java @@ -0,0 +1,20 @@ +package com.greenroom.server.api.global.config; + +import jakarta.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class PropertiesHolder { + + @Value("${cloud.cdn.path.root}") + private String cdnPathValue; + + public static String CDN_PATH; + + @PostConstruct + public void init() { + CDN_PATH = cdnPathValue; + } + +} diff --git a/server/src/test/java/com/greenroom/server/api/GreenroomIntegrationTest.java b/server/src/test/java/com/greenroom/server/api/GreenroomIntegrationTest.java index d638c3f..8e2e51e 100644 --- a/server/src/test/java/com/greenroom/server/api/GreenroomIntegrationTest.java +++ b/server/src/test/java/com/greenroom/server/api/GreenroomIntegrationTest.java @@ -415,6 +415,7 @@ private MockMultipartFile getInvalidTestMultiPartFile (){ fieldWithPath("data.plantInfo.name").type(JsonFieldType.STRING).description("식물 이름").optional(), fieldWithPath("data.plantInfo.scientificName").type(JsonFieldType.STRING).description("식물 학명").optional(), fieldWithPath("data.plantInfo.description").type(JsonFieldType.STRING).description("식물에 대한 설명").optional(), + fieldWithPath("data.plantInfo.imageUrl").type(JsonFieldType.STRING).description("식물 사진").optional(), fieldWithPath("data.plantManagementInfo").type(JsonFieldType.OBJECT).description("식물 키우는 법").optional().attributes(new Attributes.Attribute("constraint","등록된 식물이 없으면 null")), fieldWithPath("data.plantManagementInfo.managementLevel").type(JsonFieldType.STRING).description("관리 레벨 정보").optional(), fieldWithPath("data.plantManagementInfo.temperature").type(JsonFieldType.STRING).description("온도 정보").optional(), @@ -1429,4 +1430,5 @@ private RestDocumentationResultHandler getDocumentForUpdateActivityInfo(Integer } + } diff --git a/server/src/test/java/com/greenroom/server/api/PlantIntegrationTest.java b/server/src/test/java/com/greenroom/server/api/PlantIntegrationTest.java index cd0a30e..046f5d7 100644 --- a/server/src/test/java/com/greenroom/server/api/PlantIntegrationTest.java +++ b/server/src/test/java/com/greenroom/server/api/PlantIntegrationTest.java @@ -8,6 +8,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.greenroom.server.api.config.TestExecutionListener; import com.greenroom.server.api.domain.greenroom.document.PlantDocument; +import com.greenroom.server.api.domain.greenroom.dto.in.ActivityInfoUpdateRequestDto; +import com.greenroom.server.api.domain.greenroom.dto.in.GreenroomPlantRequestDto; +import com.greenroom.server.api.domain.greenroom.entity.GreenRoom; import com.greenroom.server.api.domain.greenroom.repository.*; import com.greenroom.server.api.domain.greenroom.service.PlantService; import com.greenroom.server.api.domain.user.entity.User; @@ -29,9 +32,11 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; import org.springframework.restdocs.RestDocumentationContextProvider; import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler; import org.springframework.restdocs.operation.preprocess.HeadersModifyingOperationPreprocessor; import org.springframework.restdocs.payload.FieldDescriptor; import org.springframework.restdocs.payload.JsonFieldType; @@ -48,6 +53,7 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -189,6 +195,25 @@ private HeadersModifyingOperationPreprocessor getModifiedHeader() { fieldWithPath("data.wateringInfo").type(JsonFieldType.STRING).description("식물 물주기 정보") ); + List resultDescriptorsForPlantDetails= List.of( + fieldWithPath("status").type(JsonFieldType.STRING).description("응답 상태"), + fieldWithPath("code").type(JsonFieldType.STRING).description("상태 코드"), + fieldWithPath("data").type(JsonFieldType.OBJECT).optional().description("data").optional(), + fieldWithPath("data.plantInfo").type(JsonFieldType.OBJECT).description("식물 기본 정보"), + fieldWithPath("data.plantInfo.plantId").type(JsonFieldType.NUMBER).description("식물 id").optional(), + fieldWithPath("data.plantInfo.name").type(JsonFieldType.STRING).description("식물 이름").optional(), + fieldWithPath("data.plantInfo.scientificName").type(JsonFieldType.STRING).description("식물 학명").optional(), + fieldWithPath("data.plantInfo.description").type(JsonFieldType.STRING).description("식물에 대한 설명").optional(), + fieldWithPath("data.plantInfo.imageUrl").type(JsonFieldType.STRING).description("식물 사진").optional(), + fieldWithPath("data.plantManagementInfo").type(JsonFieldType.OBJECT).description("식물 키우는 법"), + fieldWithPath("data.plantManagementInfo.managementLevel").type(JsonFieldType.STRING).description("관리 레벨 정보").optional(), + fieldWithPath("data.plantManagementInfo.temperature").type(JsonFieldType.STRING).description("온도 정보").optional(), + fieldWithPath("data.plantManagementInfo.sunlight").type(JsonFieldType.STRING).description("햇빛 정보").optional(), + fieldWithPath("data.plantManagementInfo.watering").type(JsonFieldType.STRING).description("물주기 정보").optional(), + fieldWithPath("data.plantManagementInfo.humidity").type(JsonFieldType.STRING).description("습도 정보").optional(), + fieldWithPath("data.plantManagementInfo.fertilizer").type(JsonFieldType.STRING).description("비료 정보").optional() + ); + @Test void 인기_식물_조회_성공() throws Exception { @@ -356,4 +381,57 @@ private HeadersModifyingOperationPreprocessor getModifiedHeader() { .build())) ); } + + private ResultActions getResultActionsForPlantInfo(Long plantId) throws Exception { + + String token = getTokenForTest((long) (10*1000)); + return mockMvc.perform( // api 실행 + RestDocumentationRequestBuilders + .get("/api/plants/{plantId}",plantId) + .header(HttpHeaders.AUTHORIZATION, "Bearer "+token)); + } + + private RestDocumentationResultHandler getDocumentForPlantInfo(Integer identifier){ + return document("api/plants/info/"+identifier, + preprocessRequest(prettyPrint(),modifyUris().scheme("https").host("greenroom-server.site").removePort()), + preprocessResponse(prettyPrint(), getModifiedHeader()), + pathParameters(pathParametersForPlantId), + responseFields(resultDescriptorsForPlantDetails), // responseBody 설명 + requestHeaders(headerWithName("Authorization").description("Bearer : 사용자 access Token")), + resource(ResourceSnippetParameters.builder() + .tag("그린룸") // 문서에서 api들이 태그로 분류됨 + .summary("식물 상세 정보 조회 api") // api 이름 + .description("식물 사전에서 식물 상세 정보를 조회함.") // api 설명 + .build())); + } + + @Test + @Transactional + public void 식물정보_조회_성공() throws Exception { + //given + + //when + ResultActions resultActions = getResultActionsForPlantInfo(10L); + + //then + resultActions.andExpect(status().isOk()); + + //문서화 + resultActions.andDo(getDocumentForPlantInfo(1)); + } + + @Test + @Transactional + public void 식물정보_조회_실패() throws Exception { + //given + + //when + ResultActions resultActions = getResultActionsForPlantInfo(10000L); + + //then + resultActions.andExpect(status().is(ResponseCodeEnum.PLANT_NOT_FOUND.getStatus().value())).andExpect(jsonPath("code").value(ResponseCodeEnum.PLANT_NOT_FOUND.getCode())); + + //문서화 + resultActions.andDo(getDocumentForPlantInfo(2)); + } }