From 92019f048931869478aa158d40fdacc457fe15b4 Mon Sep 17 00:00:00 2001 From: hseong3243 Date: Thu, 28 Mar 2024 22:24:26 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EB=8F=84=EB=A9=94=EC=9D=B8=20=EB=A7=81?= =?UTF-8?q?=ED=81=AC=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20api=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/docs/asciidoc/index.adoc | 10 ++ .../domain/controller/DomainController.java | 12 ++ .../request/FindDomainLinksRequest.java | 17 ++ src/main/resources/static/docs/index.html | 158 ++++++++++++++++-- .../controller/DomainControllerTest.java | 39 +++++ 5 files changed, 226 insertions(+), 10 deletions(-) create mode 100644 src/main/java/com/seong/shoutlink/domain/domain/controller/request/FindDomainLinksRequest.java diff --git a/src/docs/asciidoc/index.adoc b/src/docs/asciidoc/index.adoc index 3ab2000..c544eb9 100644 --- a/src/docs/asciidoc/index.adoc +++ b/src/docs/asciidoc/index.adoc @@ -172,3 +172,13 @@ operation::domain-controller-test/find-domain[snippets='http-request,path-parame ==== response operation::domain-controller-test/find-domain[snippets='http-response,response-fields'] + +=== 도메인 링크 목록 조회 + +==== request + +operation::domain-controller-test/find-domain-links[snippets='http-request,path-parameters,query-parameters'] + +==== response + +operation::domain-controller-test/find-domain-links[snippets='http-response,response-fields'] diff --git a/src/main/java/com/seong/shoutlink/domain/domain/controller/DomainController.java b/src/main/java/com/seong/shoutlink/domain/domain/controller/DomainController.java index 17f16e6..8fcceea 100644 --- a/src/main/java/com/seong/shoutlink/domain/domain/controller/DomainController.java +++ b/src/main/java/com/seong/shoutlink/domain/domain/controller/DomainController.java @@ -1,12 +1,15 @@ package com.seong.shoutlink.domain.domain.controller; +import com.seong.shoutlink.domain.domain.controller.request.FindDomainLinksRequest; import com.seong.shoutlink.domain.domain.controller.request.FindDomainsRequest; import com.seong.shoutlink.domain.domain.controller.request.FindRootDomainsRequest; import com.seong.shoutlink.domain.domain.service.DomainService; import com.seong.shoutlink.domain.domain.service.request.FindDomainCommand; +import com.seong.shoutlink.domain.domain.service.request.FindDomainLinksCommand; import com.seong.shoutlink.domain.domain.service.request.FindDomainsCommand; import com.seong.shoutlink.domain.domain.service.request.FindRootDomainsCommand; import com.seong.shoutlink.domain.domain.service.response.FindDomainDetailResponse; +import com.seong.shoutlink.domain.domain.service.response.FindDomainLinksResponse; import com.seong.shoutlink.domain.domain.service.response.FindDomainsResponse; import com.seong.shoutlink.domain.domain.service.response.FindRootDomainsResponse; import jakarta.validation.Valid; @@ -48,4 +51,13 @@ public ResponseEntity findDomain( = domainService.findDomain(new FindDomainCommand(domainId)); return ResponseEntity.ok(response); } + + @GetMapping("/{domainId}/links") + public ResponseEntity findDomainLinks( + @PathVariable("domainId") Long domainId, + @ModelAttribute @Valid FindDomainLinksRequest request) { + FindDomainLinksResponse response = domainService.findDomainLinks( + new FindDomainLinksCommand(domainId, request.page(), request.size())); + return ResponseEntity.ok(response); + } } diff --git a/src/main/java/com/seong/shoutlink/domain/domain/controller/request/FindDomainLinksRequest.java b/src/main/java/com/seong/shoutlink/domain/domain/controller/request/FindDomainLinksRequest.java new file mode 100644 index 0000000..41f3473 --- /dev/null +++ b/src/main/java/com/seong/shoutlink/domain/domain/controller/request/FindDomainLinksRequest.java @@ -0,0 +1,17 @@ +package com.seong.shoutlink.domain.domain.controller.request; + +import jakarta.validation.constraints.Min; +import java.util.Objects; +import org.hibernate.validator.constraints.Range; + +public record FindDomainLinksRequest( + @Min(value = 0, message = "페이지는 음수일 수 없습니다.") + Integer page, + @Range(min = 1, max = 100, message = "사이즈는 1 이상 100 이하여야 합니다.") + Integer size) { + + public FindDomainLinksRequest(Integer page, Integer size) { + this.page = Objects.isNull(page) ? 0 : page; + this.size = Objects.isNull(size) ? 10 : size; + } +} diff --git a/src/main/resources/static/docs/index.html b/src/main/resources/static/docs/index.html index e9d92d0..09360f8 100644 --- a/src/main/resources/static/docs/index.html +++ b/src/main/resources/static/docs/index.html @@ -484,6 +484,7 @@

API 문서

  • 루트 도메인 목록 조회
  • 도메인 목록 조회
  • 도메인 단건 조회
  • +
  • 도메인 링크 목록 조회
  • @@ -716,7 +717,7 @@
    POST /api/link-bundles HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzExNjI2NDM5LCJzdWIiOiIxIiwiZXhwIjoxNzExNjMwMDM5LCJyb2xlIjoiUk9MRV9VU0VSIn0.RiQMc9pgMkFKxG0oZO6q6R_HjPrHZeRMjOTegxbDwAc
    +Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzExNjMxOTM1LCJzdWIiOiIxIiwiZXhwIjoxNzExNjM1NTM1LCJyb2xlIjoiUk9MRV9VU0VSIn0.0V1ZcvM6a1HPSMSbXJ2jVNijQKWJnCNVJrWnQNXG788
     Content-Length: 57
     Host: localhost:8080
     
    @@ -832,7 +833,7 @@ 
    GET /api/link-bundles HTTP/1.1
    -Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzExNjI2NDM5LCJzdWIiOiIxIiwiZXhwIjoxNzExNjMwMDM5LCJyb2xlIjoiUk9MRV9VU0VSIn0.RiQMc9pgMkFKxG0oZO6q6R_HjPrHZeRMjOTegxbDwAc
    +Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzExNjMxOTM1LCJzdWIiOiIxIiwiZXhwIjoxNzExNjM1NTM1LCJyb2xlIjoiUk9MRV9VU0VSIn0.0V1ZcvM6a1HPSMSbXJ2jVNijQKWJnCNVJrWnQNXG788
     Host: localhost:8080
    @@ -937,7 +938,7 @@
    POST /api/hubs/1/link-bundles HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzExNjI2NDM5LCJzdWIiOiIxIiwiZXhwIjoxNzExNjMwMDM5LCJyb2xlIjoiUk9MRV9VU0VSIn0.RiQMc9pgMkFKxG0oZO6q6R_HjPrHZeRMjOTegxbDwAc
    +Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzExNjMxOTM1LCJzdWIiOiIxIiwiZXhwIjoxNzExNjM1NTM1LCJyb2xlIjoiUk9MRV9VU0VSIn0.0V1ZcvM6a1HPSMSbXJ2jVNijQKWJnCNVJrWnQNXG788
     Content-Length: 53
     Host: localhost:8080
     
    @@ -1075,7 +1076,7 @@ 
    GET /api/hubs/1/link-bundles HTTP/1.1
    -Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzExNjI2NDM5LCJzdWIiOiIxIiwiZXhwIjoxNzExNjMwMDM5LCJyb2xlIjoiUk9MRV9VU0VSIn0.RiQMc9pgMkFKxG0oZO6q6R_HjPrHZeRMjOTegxbDwAc
    +Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzExNjMxOTM1LCJzdWIiOiIxIiwiZXhwIjoxNzExNjM1NTM1LCJyb2xlIjoiUk9MRV9VU0VSIn0.0V1ZcvM6a1HPSMSbXJ2jVNijQKWJnCNVJrWnQNXG788
     Host: localhost:8080
    @@ -1203,7 +1204,7 @@
    POST /api/links HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzExNjI2NDM5LCJzdWIiOiIxIiwiZXhwIjoxNzExNjMwMDM5LCJyb2xlIjoiUk9MRV9VU0VSIn0.RiQMc9pgMkFKxG0oZO6q6R_HjPrHZeRMjOTegxbDwAc
    +Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzExNjMxOTM1LCJzdWIiOiIxIiwiZXhwIjoxNzExNjM1NTM1LCJyb2xlIjoiUk9MRV9VU0VSIn0.0V1ZcvM6a1HPSMSbXJ2jVNijQKWJnCNVJrWnQNXG788
     Content-Length: 100
     Host: localhost:8080
     
    @@ -1325,7 +1326,7 @@ 
    GET /api/links?linkBundleId=1&page=0&size=10 HTTP/1.1
    -Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzExNjI2NDM5LCJzdWIiOiIxIiwiZXhwIjoxNzExNjMwMDM5LCJyb2xlIjoiUk9MRV9VU0VSIn0.RiQMc9pgMkFKxG0oZO6q6R_HjPrHZeRMjOTegxbDwAc
    +Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzExNjMxOTM1LCJzdWIiOiIxIiwiZXhwIjoxNzExNjM1NTM1LCJyb2xlIjoiUk9MRV9VU0VSIn0.0V1ZcvM6a1HPSMSbXJ2jVNijQKWJnCNVJrWnQNXG788
     Host: localhost:8080
    @@ -1392,7 +1393,7 @@
    POST /api/hubs/1/links HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzExNjI2NDM5LCJzdWIiOiIxIiwiZXhwIjoxNzExNjMwMDM5LCJyb2xlIjoiUk9MRV9VU0VSIn0.RiQMc9pgMkFKxG0oZO6q6R_HjPrHZeRMjOTegxbDwAc
    +Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzExNjMxOTM1LCJzdWIiOiIxIiwiZXhwIjoxNzExNjM1NTM1LCJyb2xlIjoiUk9MRV9VU0VSIn0.0V1ZcvM6a1HPSMSbXJ2jVNijQKWJnCNVJrWnQNXG788
     Content-Length: 69
     Host: localhost:8080
     
    @@ -1536,7 +1537,7 @@ 
    GET /api/hubs/1/links?linkBundleId=1&page=0&size=20 HTTP/1.1
    -Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzExNjI2NDM5LCJzdWIiOiIxIiwiZXhwIjoxNzExNjMwMDM5LCJyb2xlIjoiUk9MRV9VU0VSIn0.RiQMc9pgMkFKxG0oZO6q6R_HjPrHZeRMjOTegxbDwAc
    +Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzExNjMxOTM1LCJzdWIiOiIxIiwiZXhwIjoxNzExNjM1NTM1LCJyb2xlIjoiUk9MRV9VU0VSIn0.0V1ZcvM6a1HPSMSbXJ2jVNijQKWJnCNVJrWnQNXG788
     Host: localhost:8080
    @@ -1705,7 +1706,7 @@
    POST /api/hubs HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzExNjI2NDM5LCJzdWIiOiIxIiwiZXhwIjoxNzExNjMwMDM5LCJyb2xlIjoiUk9MRV9VU0VSIn0.RiQMc9pgMkFKxG0oZO6q6R_HjPrHZeRMjOTegxbDwAc
    +Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzExNjMxOTM1LCJzdWIiOiIxIiwiZXhwIjoxNzExNjM1NTM1LCJyb2xlIjoiUk9MRV9VU0VSIn0.0V1ZcvM6a1HPSMSbXJ2jVNijQKWJnCNVJrWnQNXG788
     Content-Length: 88
     Host: localhost:8080
     
    @@ -2297,13 +2298,150 @@ 
    +

    도메인 링크 목록 조회

    +
    +

    request

    +
    +
    HTTP request
    +
    +
    +
    GET /api/domains/1/links?page=0&size=10 HTTP/1.1
    +Host: localhost:8080
    +
    +
    +
    +
    +
    Path parameters
    + + ++++ + + + + + + + + + + + + +
    Table 1. /api/domains/{domainId}/links
    ParameterDescription

    domainId

    도메인 ID

    +
    +
    +
    Query parameters
    + ++++ + + + + + + + + + + + + + + + + +
    ParameterDescription

    page

    페이지

    size

    사이즈

    +
    +
    +
    +

    response

    +
    +
    HTTP response
    +
    +
    +
    HTTP/1.1 200 OK
    +Vary: Origin
    +Vary: Access-Control-Request-Method
    +Vary: Access-Control-Request-Headers
    +Content-Type: application/json;charset=UTF-8
    +Content-Length: 151
    +
    +{
    +  "links" : [ {
    +    "linkId" : 1,
    +    "url" : "github.com/hseong3243",
    +    "aggregationCount" : 1
    +  } ],
    +  "totalElements" : 1,
    +  "hasNext" : false
    +}
    +
    +
    +
    +
    +
    Response fields
    + +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    PathTypeDescription

    links

    Array

    링크 목록

    links[].linkId

    Number

    링크 ID

    links[].url

    String

    링크 url

    links[].aggregationCount

    Number

    링크 집계 카운트

    totalElements

    Number

    총 요소 개수

    hasNext

    Boolean

    다음 페이지 여부

    +
    +
    + diff --git a/src/test/java/com/seong/shoutlink/domain/domain/controller/DomainControllerTest.java b/src/test/java/com/seong/shoutlink/domain/domain/controller/DomainControllerTest.java index ebd27f0..42a258e 100644 --- a/src/test/java/com/seong/shoutlink/domain/domain/controller/DomainControllerTest.java +++ b/src/test/java/com/seong/shoutlink/domain/domain/controller/DomainControllerTest.java @@ -16,6 +16,8 @@ import com.seong.shoutlink.base.BaseControllerTest; import com.seong.shoutlink.domain.domain.service.response.FindDomainDetailResponse; +import com.seong.shoutlink.domain.domain.service.response.FindDomainLinkResponse; +import com.seong.shoutlink.domain.domain.service.response.FindDomainLinksResponse; import com.seong.shoutlink.domain.domain.service.response.FindDomainResponse; import com.seong.shoutlink.domain.domain.service.response.FindDomainsResponse; import com.seong.shoutlink.domain.domain.service.response.FindRootDomainsResponse; @@ -113,4 +115,41 @@ void findDomain() throws Exception { ) )); } + + @Test + @DisplayName("성공: 도메인 링크 목록 조회 api 호출 시") + void findDomainLinks() throws Exception { + //given + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("page", "0"); + params.add("size", "10"); + + FindDomainLinkResponse content = new FindDomainLinkResponse(1L, "github.com/hseong3243", 1); + FindDomainLinksResponse response = new FindDomainLinksResponse(List.of(content), 1L, false); + given(domainService.findDomainLinks(any())).willReturn(response); + + //when + ResultActions resultActions = mockMvc.perform(get("/api/domains/{domainId}/links", 1L) + .params(params)); + + //then + resultActions.andExpect(status().isOk()) + .andDo(restDocs.document( + pathParameters( + parameterWithName("domainId").description("도메인 ID") + ), + queryParameters( + parameterWithName("page").description("페이지"), + parameterWithName("size").description("사이즈") + ), + responseFields( + fieldWithPath("links").type(ARRAY).description("링크 목록"), + fieldWithPath("links[].linkId").type(NUMBER).description("링크 ID"), + fieldWithPath("links[].url").type(STRING).description("링크 url"), + fieldWithPath("links[].aggregationCount").type(NUMBER).description("링크 집계 카운트"), + fieldWithPath("totalElements").type(NUMBER).description("총 요소 개수"), + fieldWithPath("hasNext").type(BOOLEAN).description("다음 페이지 여부") + ) + )); + } } \ No newline at end of file