From 2c4ff21eb279fea3d9c030fa74b1db81985295f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=83=81=ED=9B=88?= Date: Sun, 7 Jul 2024 07:22:41 +0900 Subject: [PATCH] =?UTF-8?q?[KAN-123]=20feat(functionaltest):=20=EC=83=81?= =?UTF-8?q?=ED=92=88=20=EC=A1=B0=ED=9A=8C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/build.gradle | 18 +- server/gradle.properties | 1 + server/src/docs/asciidoc/index.adoc | 23 +- .../docs/asciidoc/products/products-get.adoc | 17 + .../src/docs/asciidoc/products/products.adoc | 4 + .../test/controller/PostController.java | 43 -- .../controller/TProductsController.java | 53 ++ .../products/dto/TProductsResponseDto.java | 28 + .../domain/products/entity/TProducts.java | 17 + .../cmsplusmain/test/dto/PostRequestDto.java | 11 - .../cmsplusmain/test/dto/PostResponseDto.java | 17 - .../or/kosa/cmsplusmain/test/entity/Post.java | 12 - .../src/main/resources/static/docs/index.html | 553 ++++++++++++++++++ .../controller/ProductsTestController.java} | 58 +- 14 files changed, 711 insertions(+), 144 deletions(-) create mode 100644 server/gradle.properties create mode 100644 server/src/docs/asciidoc/products/products-get.adoc create mode 100644 server/src/docs/asciidoc/products/products.adoc delete mode 100644 server/src/main/java/kr/or/kosa/cmsplusmain/test/controller/PostController.java create mode 100644 server/src/main/java/kr/or/kosa/cmsplusmain/test/domain/products/controller/TProductsController.java create mode 100644 server/src/main/java/kr/or/kosa/cmsplusmain/test/domain/products/dto/TProductsResponseDto.java create mode 100644 server/src/main/java/kr/or/kosa/cmsplusmain/test/domain/products/entity/TProducts.java delete mode 100644 server/src/main/java/kr/or/kosa/cmsplusmain/test/dto/PostRequestDto.java delete mode 100644 server/src/main/java/kr/or/kosa/cmsplusmain/test/dto/PostResponseDto.java delete mode 100644 server/src/main/java/kr/or/kosa/cmsplusmain/test/entity/Post.java create mode 100644 server/src/main/resources/static/docs/index.html rename server/src/test/java/kr/or/kosa/cmsplusmain/{controller/PostControllerTest.java => domain/products/controller/ProductsTestController.java} (63%) diff --git a/server/build.gradle b/server/build.gradle index a283d33e..f25380f8 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -61,18 +61,22 @@ asciidoctor { inputs.dir snippetsDir configurations 'asciidoctorExtensions' dependsOn test + sources { + include("**/*.adoc") + } + baseDirFollowsSourceFile() } -bootJar { +task copyDocs(type: Copy) { dependsOn asciidoctor + from "${asciidoctor.outputDir}" + into "src/main/resources/static/docs" // src/main/resources/static/docs로 복사 +} - // build 경로 안에 있는 index.html을 밖으로 꺼내준다. - copy { - from "${asciidoctor.outputDir}" - into "src/main/resources/static/docs" // src/main/resources/static/docs로 복사 - } - +build { + dependsOn copyDocs } + /** * QueryDSL Build Options */ diff --git a/server/gradle.properties b/server/gradle.properties new file mode 100644 index 00000000..656f2e60 --- /dev/null +++ b/server/gradle.properties @@ -0,0 +1 @@ +org.gradle.jvmargs=-Dfile.encoding=UTF-8 \ No newline at end of file diff --git a/server/src/docs/asciidoc/index.adoc b/server/src/docs/asciidoc/index.adoc index d55b1e85..eacbba4f 100644 --- a/server/src/docs/asciidoc/index.adoc +++ b/server/src/docs/asciidoc/index.adoc @@ -1,21 +1,16 @@ -= REST DOC EXAMPLE -Rest Documentation 실습 +ifndef::snippets[] +:snippets:build/generated-snippets +endif::[] + += CMS+ Application API Document +made by Sanghun.Kim :doctype: book :icons: font :source-highlighter: highlightjs :toc: left :toclevels: 4 :sectlinks: -:operation-http-request-title: Example request -:operation-http-response-title: Example response - -[[POST-API]] -== POST - -[[GET-POST]] -=== GET POST -operation::getPost[snippets='http-request,http-response'] +:operation-http-request-title: Request +:operation-http-response-title: Response -[[POST-POST]] -=== CREATE POST -operation::create Post[snippets='http-request,http-response'] \ No newline at end of file +include::{docdir}/products/products.adoc[] \ No newline at end of file diff --git a/server/src/docs/asciidoc/products/products-get.adoc b/server/src/docs/asciidoc/products/products-get.adoc new file mode 100644 index 00000000..58528017 --- /dev/null +++ b/server/src/docs/asciidoc/products/products-get.adoc @@ -0,0 +1,17 @@ +[[GET-POST]] +=== 상품 조회 + +.Request Header +|=== +|파라미터 |타입 |필수여부 |설명 +|Authorization |String |필수 |인증 키 +|=== + +.Request Parameter +|=== +|파라미터 |타입 |필수여부 |설명 +|page |Integer|선택 |페이지 번호 +|size |Integer|선택 |한 페이지에 표시할 데이터 개수 +|=== + +operation::getProducts[snippets='http-request,http-response'] \ No newline at end of file diff --git a/server/src/docs/asciidoc/products/products.adoc b/server/src/docs/asciidoc/products/products.adoc new file mode 100644 index 00000000..4e7bfefb --- /dev/null +++ b/server/src/docs/asciidoc/products/products.adoc @@ -0,0 +1,4 @@ +[[POST-API]] +== 상품 + +include::{docdir}/products/products-get.adoc[] \ No newline at end of file diff --git a/server/src/main/java/kr/or/kosa/cmsplusmain/test/controller/PostController.java b/server/src/main/java/kr/or/kosa/cmsplusmain/test/controller/PostController.java deleted file mode 100644 index c5bc7bd3..00000000 --- a/server/src/main/java/kr/or/kosa/cmsplusmain/test/controller/PostController.java +++ /dev/null @@ -1,43 +0,0 @@ -package kr.or.kosa.cmsplusmain.test.controller; - -import kr.or.kosa.cmsplusmain.test.dto.PostRequestDto; -import kr.or.kosa.cmsplusmain.test.dto.PostResponseDto; -import kr.or.kosa.cmsplusmain.test.entity.Post; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; - -@RestController -public class PostController { - - @GetMapping("/post") - public ResponseEntity getPost() { - Post post = buildPost("rlatkdtest1", "rlatkdtest2"); - PostResponseDto postResponseDto = buildResponseDto(post); - return ResponseEntity.ok(postResponseDto); - } - - @PostMapping("/post") - public ResponseEntity createPost(@RequestBody PostRequestDto postRequestDto) { - Post post = buildPost(postRequestDto.getName(), postRequestDto.getContent()); - PostResponseDto postResponseDto = buildResponseDto(post); - return ResponseEntity.ok(postResponseDto); - } - - private static Post buildPost(String name, String content) { - return Post.builder() - .name(name) - .content(content) - .build(); - } - - private static PostResponseDto buildResponseDto(Post post) { - PostResponseDto postResponseDto = PostResponseDto.builder() - .name(post.getName()) - .content(post.getContent()) - .build(); - return postResponseDto; - } -} \ No newline at end of file diff --git a/server/src/main/java/kr/or/kosa/cmsplusmain/test/domain/products/controller/TProductsController.java b/server/src/main/java/kr/or/kosa/cmsplusmain/test/domain/products/controller/TProductsController.java new file mode 100644 index 00000000..cb011f55 --- /dev/null +++ b/server/src/main/java/kr/or/kosa/cmsplusmain/test/domain/products/controller/TProductsController.java @@ -0,0 +1,53 @@ +package kr.or.kosa.cmsplusmain.test.domain.products.controller; + +import kr.or.kosa.cmsplusmain.test.domain.products.dto.TProductsResponseDto; +import kr.or.kosa.cmsplusmain.test.domain.products.entity.TProducts; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.*; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/api/v1/vendor/products") +public class TProductsController { + + private List TProductsList = Arrays.asList( + TProducts.builder().name("제목1").price(1000.0).contractCount(5).createdAt("2024-07-07").notes("내용1").build(), + TProducts.builder().name("제목2").price(2000.0).contractCount(10).createdAt("2024-07-08").notes("내용2").build(), + TProducts.builder().name("제목3").price(3000.0).contractCount(15).createdAt("2024-07-09").notes("내용3").build() + ); + + @GetMapping + public ResponseEntity> getProducts(@RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "2") int size) { + int start = page * size; + int end = Math.min((page + 1) * size, TProductsList.size()); + + if (start > TProductsList.size()) { + return ResponseEntity.ok(Map.of( + "page", page, + "size", size, + "totalPage", (TProductsList.size() + size - 1) / size, + "totalCount", TProductsList.size(), + "data", Collections.emptyList() + )); + } + + List productsResponseList = this.TProductsList.subList(start, end).stream() + .map(TProductsResponseDto::fromEntity) + .collect(Collectors.toList()); + + Map response = new LinkedHashMap<>(); + response.put("page", page); + response.put("size", size); + response.put("totalPage", (this.TProductsList.size() + size - 1) / size); + response.put("totalCount", this.TProductsList.size()); + response.put("data", productsResponseList); + + return ResponseEntity.ok(response); + } +} \ No newline at end of file diff --git a/server/src/main/java/kr/or/kosa/cmsplusmain/test/domain/products/dto/TProductsResponseDto.java b/server/src/main/java/kr/or/kosa/cmsplusmain/test/domain/products/dto/TProductsResponseDto.java new file mode 100644 index 00000000..ff0b71c9 --- /dev/null +++ b/server/src/main/java/kr/or/kosa/cmsplusmain/test/domain/products/dto/TProductsResponseDto.java @@ -0,0 +1,28 @@ +package kr.or.kosa.cmsplusmain.test.domain.products.dto; + +import kr.or.kosa.cmsplusmain.test.domain.products.entity.TProducts; +import lombok.*; + +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PROTECTED) +public class TProductsResponseDto { + + private String name; + private double price; + private int contractCount; + private String createdAt; + private String notes; + + public static TProductsResponseDto fromEntity(TProducts tProducts) { + return TProductsResponseDto.builder() + .name(tProducts.getName()) + .contractCount(tProducts.getContractCount()) + .price(tProducts.getPrice()) + .createdAt(tProducts.getCreatedAt()) + .notes(tProducts.getNotes()) + .build(); + } + +} \ No newline at end of file diff --git a/server/src/main/java/kr/or/kosa/cmsplusmain/test/domain/products/entity/TProducts.java b/server/src/main/java/kr/or/kosa/cmsplusmain/test/domain/products/entity/TProducts.java new file mode 100644 index 00000000..70690969 --- /dev/null +++ b/server/src/main/java/kr/or/kosa/cmsplusmain/test/domain/products/entity/TProducts.java @@ -0,0 +1,17 @@ +package kr.or.kosa.cmsplusmain.test.domain.products.entity; + +import lombok.*; + +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PROTECTED) +public class TProducts { + + private String name; + private double price; + private int contractCount; + private String createdAt; + private String notes; + +} \ No newline at end of file diff --git a/server/src/main/java/kr/or/kosa/cmsplusmain/test/dto/PostRequestDto.java b/server/src/main/java/kr/or/kosa/cmsplusmain/test/dto/PostRequestDto.java deleted file mode 100644 index 6ea13668..00000000 --- a/server/src/main/java/kr/or/kosa/cmsplusmain/test/dto/PostRequestDto.java +++ /dev/null @@ -1,11 +0,0 @@ -package kr.or.kosa.cmsplusmain.test.dto; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -@Getter -@AllArgsConstructor -public class PostRequestDto { - private String name; - private String content; -} \ No newline at end of file diff --git a/server/src/main/java/kr/or/kosa/cmsplusmain/test/dto/PostResponseDto.java b/server/src/main/java/kr/or/kosa/cmsplusmain/test/dto/PostResponseDto.java deleted file mode 100644 index 467a1b7e..00000000 --- a/server/src/main/java/kr/or/kosa/cmsplusmain/test/dto/PostResponseDto.java +++ /dev/null @@ -1,17 +0,0 @@ -package kr.or.kosa.cmsplusmain.test.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; - -import static lombok.AccessLevel.PROTECTED; - -@Getter -@Builder -@NoArgsConstructor(access = PROTECTED) -@AllArgsConstructor(access = PROTECTED) -public class PostResponseDto { - private String name; - private String content; -} \ No newline at end of file diff --git a/server/src/main/java/kr/or/kosa/cmsplusmain/test/entity/Post.java b/server/src/main/java/kr/or/kosa/cmsplusmain/test/entity/Post.java deleted file mode 100644 index f234f32b..00000000 --- a/server/src/main/java/kr/or/kosa/cmsplusmain/test/entity/Post.java +++ /dev/null @@ -1,12 +0,0 @@ -package kr.or.kosa.cmsplusmain.test.entity; - -import lombok.*; - -@Getter -@Builder -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor(access = AccessLevel.PROTECTED) -public class Post { - private String name; - private String content; -} \ No newline at end of file diff --git a/server/src/main/resources/static/docs/index.html b/server/src/main/resources/static/docs/index.html new file mode 100644 index 00000000..2b3f3d31 --- /dev/null +++ b/server/src/main/resources/static/docs/index.html @@ -0,0 +1,553 @@ + + + + + + + + +CMS+ Application API Document + + + + + + + +
+
+

상품

+
+
+

상품 조회

+ + ++++++ + + + + + + + + + + + + + + +
Table 1. Request Header

파라미터

타입

필수여부

설명

Authorization

String

필수

인증 키

+ + ++++++ + + + + + + + + + + + + + + + + + + + + +
Table 2. Request Parameter

파라미터

타입

필수여부

설명

page

Integer

선택

페이지 번호

size

Integer

선택

한 페이지에 표시할 데이터 개수

+
+

Request

+
+

Snippet http-request not found for operation::getProducts

+
+
+
+

Response

+
+

Snippet http-response not found for operation::getProducts

+
+
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/server/src/test/java/kr/or/kosa/cmsplusmain/controller/PostControllerTest.java b/server/src/test/java/kr/or/kosa/cmsplusmain/domain/products/controller/ProductsTestController.java similarity index 63% rename from server/src/test/java/kr/or/kosa/cmsplusmain/controller/PostControllerTest.java rename to server/src/test/java/kr/or/kosa/cmsplusmain/domain/products/controller/ProductsTestController.java index 72b4831e..4ca48908 100644 --- a/server/src/test/java/kr/or/kosa/cmsplusmain/controller/PostControllerTest.java +++ b/server/src/test/java/kr/or/kosa/cmsplusmain/domain/products/controller/ProductsTestController.java @@ -1,7 +1,6 @@ -package kr.or.kosa.cmsplusmain.controller; +package kr.or.kosa.cmsplusmain.domain.products.controller; import com.fasterxml.jackson.databind.ObjectMapper; -import kr.or.kosa.cmsplusmain.test.dto.PostRequestDto; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -13,7 +12,6 @@ import org.springframework.restdocs.RestDocumentationContextProvider; import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.restdocs.headers.HeaderDocumentation; -import org.springframework.restdocs.payload.JsonFieldType; import org.springframework.restdocs.payload.PayloadDocumentation; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; @@ -31,15 +29,16 @@ @ExtendWith({RestDocumentationExtension.class}) @AutoConfigureMockMvc @SpringBootTest -class PostControllerTest { +class ProductsTestController { @Autowired MockMvc mockMvc; + @Autowired ObjectMapper objectMapper; @BeforeEach - void setup(WebApplicationContext webApplicationContext, + void setUp(WebApplicationContext webApplicationContext, RestDocumentationContextProvider restDocumentationContextProvider) { this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext) .addFilter(new CharacterEncodingFilter("UTF-8", true)) @@ -51,53 +50,32 @@ void setup(WebApplicationContext webApplicationContext, } @Test - void getPost() throws Exception { - this.mockMvc.perform(MockMvcRequestBuilders.get("/post").accept(MediaType.APPLICATION_JSON)) - .andExpect(MockMvcResultMatchers.status().isOk()) - .andDo(MockMvcResultHandlers.print()) - .andDo(document("getPost", - HeaderDocumentation.requestHeaders( - HeaderDocumentation.headerWithName(HttpHeaders.ACCEPT).description("accept header") - ), - HeaderDocumentation.responseHeaders( - HeaderDocumentation.headerWithName(HttpHeaders.CONTENT_TYPE).description("content type") - ), - PayloadDocumentation.responseFields( - PayloadDocumentation.fieldWithPath("name").type(JsonFieldType.STRING).description("name of post"), - PayloadDocumentation.fieldWithPath("content").type(JsonFieldType.STRING).description("content of post") - ) - )); - } - - @Test - void createPost() throws Exception{ - PostRequestDto requestDto = new PostRequestDto("rlatkdtest1", "rlatkdgnstestt1"); - - this.mockMvc.perform(MockMvcRequestBuilders.post("/post") - .content(objectMapper.writeValueAsString(requestDto)) - .contentType(MediaType.APPLICATION_JSON) + void getProducts() throws Exception { + this.mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/vendor/products") + .param("page", "1") + .param("size", "2") .accept(MediaType.APPLICATION_JSON)) .andExpect(MockMvcResultMatchers.status().isOk()) .andDo(MockMvcResultHandlers.print()) - .andDo(document("create Post", + .andDo(document("getProducts", HeaderDocumentation.requestHeaders( HeaderDocumentation.headerWithName(HttpHeaders.ACCEPT).description("accept header") ), - PayloadDocumentation.requestFields( - PayloadDocumentation.fieldWithPath("name").type(JsonFieldType.STRING).description("name of post"), - PayloadDocumentation.fieldWithPath("content").type(JsonFieldType.STRING).description("content of post") - - ), HeaderDocumentation.responseHeaders( HeaderDocumentation.headerWithName(HttpHeaders.CONTENT_TYPE).description("content type") ), PayloadDocumentation.responseFields( - PayloadDocumentation.fieldWithPath("name").type(JsonFieldType.STRING).description("name of post"), - PayloadDocumentation.fieldWithPath("content").type(JsonFieldType.STRING).description("content of post") + PayloadDocumentation.fieldWithPath("page").description("current page number"), + PayloadDocumentation.fieldWithPath("size").description("number of items per page"), + PayloadDocumentation.fieldWithPath("totalPage").description("total number of pages"), + PayloadDocumentation.fieldWithPath("totalCount").description("total number of products"), + PayloadDocumentation.fieldWithPath("data[].name").description("name of product"), + PayloadDocumentation.fieldWithPath("data[].price").description("price of product"), + PayloadDocumentation.fieldWithPath("data[].contractCount").description("contract count of product"), + PayloadDocumentation.fieldWithPath("data[].createdAt").description("creation date of product"), + PayloadDocumentation.fieldWithPath("data[].notes").description("notes of product") ) )); - - } } \ No newline at end of file