From 69c7b430db99d3ba39bbb02ecd427482d43a9f83 Mon Sep 17 00:00:00 2001 From: hseong3243 Date: Sun, 21 Apr 2024 21:07:39 +0900 Subject: [PATCH 1/4] =?UTF-8?q?refactor:=20=ED=97=88=EB=B8=8C=20=EB=A7=88?= =?UTF-8?q?=EC=8A=A4=ED=84=B0=20=EA=B6=8C=ED=95=9C=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/seong/shoutlink/domain/hub/Hub.java | 4 ++-- .../domain/link/service/LinkService.java | 3 ++- .../linkbundle/service/LinkBundleService.java | 3 ++- .../domain/link/service/LinkServiceTest.java | 12 ++++++---- .../service/LinkBundleServiceTest.java | 10 ++++---- .../repository/StubMemberRepository.java | 23 +++++++++++-------- 6 files changed, 33 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/seong/shoutlink/domain/hub/Hub.java b/src/main/java/com/seong/shoutlink/domain/hub/Hub.java index 41ae850..a66ead2 100644 --- a/src/main/java/com/seong/shoutlink/domain/hub/Hub.java +++ b/src/main/java/com/seong/shoutlink/domain/hub/Hub.java @@ -56,8 +56,8 @@ private String validateDescription(String description) { return description; } - public void checkMasterAuthority(Long memberId) { - if(Objects.equals(masterId, memberId)) { + public void checkMasterAuthority(Member member) { + if(Objects.equals(masterId, member.getMemberId())) { return; } throw new ShoutLinkException("권한이 없습니다.", ErrorCode.UNAUTHORIZED); diff --git a/src/main/java/com/seong/shoutlink/domain/link/service/LinkService.java b/src/main/java/com/seong/shoutlink/domain/link/service/LinkService.java index 3a4eba3..60273c8 100644 --- a/src/main/java/com/seong/shoutlink/domain/link/service/LinkService.java +++ b/src/main/java/com/seong/shoutlink/domain/link/service/LinkService.java @@ -80,7 +80,8 @@ private Member getMember(Long memberId) { @Transactional public CreateHubLinkResponse createHubLink(CreateHubLinkCommand command) { Hub hub = getHub(command.hubId()); - hub.checkMasterAuthority(command.memberId()); + Member member = getMember(command.memberId()); + hub.checkMasterAuthority(member); LinkBundle hubLinkBundle = getHubLinkBundle(command.linkBundleId(), hub); Link link = new Link(command.url(), command.description()); diff --git a/src/main/java/com/seong/shoutlink/domain/linkbundle/service/LinkBundleService.java b/src/main/java/com/seong/shoutlink/domain/linkbundle/service/LinkBundleService.java index df92eca..fdd88f7 100644 --- a/src/main/java/com/seong/shoutlink/domain/linkbundle/service/LinkBundleService.java +++ b/src/main/java/com/seong/shoutlink/domain/linkbundle/service/LinkBundleService.java @@ -63,7 +63,8 @@ private Member getMember(Long memberId) { @Transactional public CreateLinkBundleResponse createHubLinkBundle(CreateHubLinkBundleCommand command) { Hub hub = getHub(command.hubId()); - hub.checkMasterAuthority(command.memberId()); + Member member = getMember(command.memberId()); + hub.checkMasterAuthority(member); LinkBundle linkBundle = new LinkBundle( command.description(), command.isDefault()); diff --git a/src/test/java/com/seong/shoutlink/domain/link/service/LinkServiceTest.java b/src/test/java/com/seong/shoutlink/domain/link/service/LinkServiceTest.java index 6b24cd1..d78a955 100644 --- a/src/test/java/com/seong/shoutlink/domain/link/service/LinkServiceTest.java +++ b/src/test/java/com/seong/shoutlink/domain/link/service/LinkServiceTest.java @@ -231,13 +231,15 @@ void notFound_WhenHubLinkBundleNotFound() { @DisplayName("예외(Unahthorized): 사용자가 허브 마스터가 아님") void unauthorized_WhenMemberIsNotMaster() { //given - Member member = MemberFixture.member(); - Hub hub = HubFixture.hub(member); + Member master = MemberFixture.member(); + Member member = new Member(2L, "asdf1234@gmail.com", "asdf1234!", "asdf", + MemberRole.ROLE_USER); + Hub hub = HubFixture.hub(master); + memberRepository.stub(master); memberRepository.stub(member); hubRepository.stub(hub); - Long notMasterId = hub.getMasterId() + 1; - CreateHubLinkCommand command = new CreateHubLinkCommand(hub.getHubId(), notMasterId, 1L, - "url", "설명"); + CreateHubLinkCommand command = new CreateHubLinkCommand(hub.getHubId(), + member.getMemberId(), 1L, "url", "설명"); //when Exception exception = catchException(() -> linkService.createHubLink(command)); diff --git a/src/test/java/com/seong/shoutlink/domain/linkbundle/service/LinkBundleServiceTest.java b/src/test/java/com/seong/shoutlink/domain/linkbundle/service/LinkBundleServiceTest.java index 2729c00..c7f7f9c 100644 --- a/src/test/java/com/seong/shoutlink/domain/linkbundle/service/LinkBundleServiceTest.java +++ b/src/test/java/com/seong/shoutlink/domain/linkbundle/service/LinkBundleServiceTest.java @@ -189,14 +189,16 @@ void notFound_WhenHubNotFound() { @DisplayName("예외(Unauthorized): 링크 묶음 생성 권한 없음") void unauthorized_WhenMemberIsNotHubMaster() { //given - Member member = MemberFixture.member(); - Hub hub = HubFixture.hub(member); + Member master = MemberFixture.member(); + Hub hub = HubFixture.hub(master); + Member member = new Member(2L, "asdf1234@gmail.com", "asdf1234!", "asdf", + MemberRole.ROLE_USER); + memberRepository.stub(master); memberRepository.stub(member); hubRepository.stub(hub); - long unknownMemberId = member.getMemberId() + 1; CreateHubLinkBundleCommand command = new CreateHubLinkBundleCommand( hub.getHubId(), - unknownMemberId, + member.getMemberId(), "허브", false); diff --git a/src/test/java/com/seong/shoutlink/domain/member/repository/StubMemberRepository.java b/src/test/java/com/seong/shoutlink/domain/member/repository/StubMemberRepository.java index c0a7d4e..109d0bf 100644 --- a/src/test/java/com/seong/shoutlink/domain/member/repository/StubMemberRepository.java +++ b/src/test/java/com/seong/shoutlink/domain/member/repository/StubMemberRepository.java @@ -2,31 +2,36 @@ import com.seong.shoutlink.domain.member.Member; import com.seong.shoutlink.domain.member.service.MemberRepository; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; +import java.util.HashMap; +import java.util.Map; import java.util.Optional; public final class StubMemberRepository implements MemberRepository { - private List memory = new ArrayList<>(); + private Map memory = new HashMap<>(); public StubMemberRepository(Member... members) { - memory.addAll(Arrays.stream(members).toList()); + stub(members); } public void stub(Member... members) { - memory.addAll(Arrays.stream(members).toList()); + for (Member member : members) { + memory.put(getNextId(), member); + } + } + + public Long getNextId() { + return (long) (memory.size() + 1); } @Override public Optional findByEmail(String email) { - return memory.stream().filter(member -> member.getEmail().equals(email)).findFirst(); + return memory.values().stream().filter(member -> member.getEmail().equals(email)).findFirst(); } @Override public Optional findByNickname(String nickname) { - return memory.stream().filter(member -> member.getNickname().equals(nickname)).findFirst(); + return memory.values().stream().filter(member -> member.getNickname().equals(nickname)).findFirst(); } @Override @@ -36,6 +41,6 @@ public Long save(Member member) { @Override public Optional findById(Long memberId) { - return memory.stream().filter(member -> member.getMemberId().equals(memberId)).findFirst(); + return Optional.ofNullable(memory.get(memberId)); } } From db2db65e0acf15b6d7e0affa15d9ae53ba6fca07 Mon Sep 17 00:00:00 2001 From: hseong3243 Date: Sun, 21 Apr 2024 21:07:55 +0900 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20=ED=97=88=EB=B8=8C=20=EB=A7=81?= =?UTF-8?q?=ED=81=AC=20=EC=82=AD=EC=A0=9C=20=EC=84=9C=EB=B9=84=EC=8A=A4=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../link/repository/LinkRepositoryImpl.java | 6 + .../domain/link/service/LinkRepository.java | 3 + .../domain/link/service/LinkService.java | 15 +++ .../domain/link/service/LinkUseCase.java | 3 + .../service/request/DeleteHubLinkCommand.java | 5 + .../link/repository/StubLinkRepository.java | 6 + .../domain/link/service/LinkServiceTest.java | 112 ++++++++++++++++++ 7 files changed, 150 insertions(+) create mode 100644 src/main/java/com/seong/shoutlink/domain/link/service/request/DeleteHubLinkCommand.java diff --git a/src/main/java/com/seong/shoutlink/domain/link/repository/LinkRepositoryImpl.java b/src/main/java/com/seong/shoutlink/domain/link/repository/LinkRepositoryImpl.java index 383b0ee..63df7a9 100644 --- a/src/main/java/com/seong/shoutlink/domain/link/repository/LinkRepositoryImpl.java +++ b/src/main/java/com/seong/shoutlink/domain/link/repository/LinkRepositoryImpl.java @@ -1,6 +1,7 @@ package com.seong.shoutlink.domain.link.repository; import com.seong.shoutlink.domain.domain.Domain; +import com.seong.shoutlink.domain.hub.Hub; import com.seong.shoutlink.domain.link.Link; import com.seong.shoutlink.domain.link.LinkWithLinkBundle; import com.seong.shoutlink.domain.link.service.LinkRepository; @@ -76,6 +77,11 @@ public Optional findMemberLink(Long linkId, Member member) { .map(LinkEntity::toDomain); } + @Override + public Optional findHubLink(Long linkId, Hub hub) { + return Optional.empty(); + } + @Override public void delete(Link link) { linkJpaRepository.deleteById(link.getLinkId()); diff --git a/src/main/java/com/seong/shoutlink/domain/link/service/LinkRepository.java b/src/main/java/com/seong/shoutlink/domain/link/service/LinkRepository.java index 7e092a3..7af98d1 100644 --- a/src/main/java/com/seong/shoutlink/domain/link/service/LinkRepository.java +++ b/src/main/java/com/seong/shoutlink/domain/link/service/LinkRepository.java @@ -1,6 +1,7 @@ package com.seong.shoutlink.domain.link.service; import com.seong.shoutlink.domain.domain.Domain; +import com.seong.shoutlink.domain.hub.Hub; import com.seong.shoutlink.domain.link.Link; import com.seong.shoutlink.domain.link.LinkWithLinkBundle; import com.seong.shoutlink.domain.link.service.result.LinkPaginationResult; @@ -23,5 +24,7 @@ public interface LinkRepository { Optional findMemberLink(Long linkId, Member member); + Optional findHubLink(Long linkId, Hub hub); + void delete(Link link); } diff --git a/src/main/java/com/seong/shoutlink/domain/link/service/LinkService.java b/src/main/java/com/seong/shoutlink/domain/link/service/LinkService.java index 60273c8..72ea437 100644 --- a/src/main/java/com/seong/shoutlink/domain/link/service/LinkService.java +++ b/src/main/java/com/seong/shoutlink/domain/link/service/LinkService.java @@ -12,6 +12,7 @@ import com.seong.shoutlink.domain.link.service.event.CreateMemberLinkEvent; import com.seong.shoutlink.domain.link.service.request.CreateHubLinkCommand; import com.seong.shoutlink.domain.link.service.request.CreateLinkCommand; +import com.seong.shoutlink.domain.link.service.request.DeleteHubLinkCommand; import com.seong.shoutlink.domain.link.service.request.DeleteLinkCommand; import com.seong.shoutlink.domain.link.service.request.FindHubLinksCommand; import com.seong.shoutlink.domain.link.service.request.FindLinksCommand; @@ -107,6 +108,7 @@ public FindLinksResponse findHubLinks(FindHubLinksCommand command) { } @Override + @Transactional public DeleteLinkResponse deleteLink(DeleteLinkCommand command) { Member member = getMember(command.memberId()); Link link = linkRepository.findMemberLink(command.linkId(), member) @@ -115,6 +117,19 @@ public DeleteLinkResponse deleteLink(DeleteLinkCommand command) { return new DeleteLinkResponse(link.getLinkId()); } + @Override + @Transactional + public DeleteLinkResponse deleteHubLink(DeleteHubLinkCommand command) { + Hub hub = getHub(command.hubId()); + Member member = getMember(command.memberId()); + hub.checkMasterAuthority(member); + Link link = linkRepository.findHubLink(command.linkId(), hub) + .orElseThrow(() -> new ShoutLinkException("존재하지 않는 링크입니다.", ErrorCode.NOT_FOUND)); + linkRepository.delete(link); + return new DeleteLinkResponse(link.getLinkId()); + } + + private void checkAuthenticated(@Nullable Long nullableMemberId) { if(Objects.isNull(nullableMemberId)) { throw new ShoutLinkException("인증되지 않은 사용자입니다.", ErrorCode.UNAUTHENTICATED); diff --git a/src/main/java/com/seong/shoutlink/domain/link/service/LinkUseCase.java b/src/main/java/com/seong/shoutlink/domain/link/service/LinkUseCase.java index 656c829..b138524 100644 --- a/src/main/java/com/seong/shoutlink/domain/link/service/LinkUseCase.java +++ b/src/main/java/com/seong/shoutlink/domain/link/service/LinkUseCase.java @@ -2,6 +2,7 @@ import com.seong.shoutlink.domain.link.service.request.CreateHubLinkCommand; import com.seong.shoutlink.domain.link.service.request.CreateLinkCommand; +import com.seong.shoutlink.domain.link.service.request.DeleteHubLinkCommand; import com.seong.shoutlink.domain.link.service.request.DeleteLinkCommand; import com.seong.shoutlink.domain.link.service.request.FindHubLinksCommand; import com.seong.shoutlink.domain.link.service.request.FindLinksCommand; @@ -21,4 +22,6 @@ public interface LinkUseCase { FindLinksResponse findHubLinks(FindHubLinksCommand command); DeleteLinkResponse deleteLink(DeleteLinkCommand command); + + DeleteLinkResponse deleteHubLink(DeleteHubLinkCommand command); } diff --git a/src/main/java/com/seong/shoutlink/domain/link/service/request/DeleteHubLinkCommand.java b/src/main/java/com/seong/shoutlink/domain/link/service/request/DeleteHubLinkCommand.java new file mode 100644 index 0000000..3149aee --- /dev/null +++ b/src/main/java/com/seong/shoutlink/domain/link/service/request/DeleteHubLinkCommand.java @@ -0,0 +1,5 @@ +package com.seong.shoutlink.domain.link.service.request; + +public record DeleteHubLinkCommand(Long linkId, Long memberId, Long hubId) { + +} diff --git a/src/test/java/com/seong/shoutlink/domain/link/repository/StubLinkRepository.java b/src/test/java/com/seong/shoutlink/domain/link/repository/StubLinkRepository.java index b941bcc..9b1064d 100644 --- a/src/test/java/com/seong/shoutlink/domain/link/repository/StubLinkRepository.java +++ b/src/test/java/com/seong/shoutlink/domain/link/repository/StubLinkRepository.java @@ -1,6 +1,7 @@ package com.seong.shoutlink.domain.link.repository; import com.seong.shoutlink.domain.domain.Domain; +import com.seong.shoutlink.domain.hub.Hub; import com.seong.shoutlink.domain.link.Link; import com.seong.shoutlink.domain.link.LinkWithLinkBundle; import com.seong.shoutlink.domain.link.service.LinkRepository; @@ -77,6 +78,11 @@ public Optional findMemberLink(Long linkId, Member member) { return memory.values().stream().findFirst(); } + @Override + public Optional findHubLink(Long linkId, Hub hub) { + return memory.values().stream().findFirst(); + } + @Override public void delete(Link link) { memory.values().remove(link); diff --git a/src/test/java/com/seong/shoutlink/domain/link/service/LinkServiceTest.java b/src/test/java/com/seong/shoutlink/domain/link/service/LinkServiceTest.java index d78a955..8498686 100644 --- a/src/test/java/com/seong/shoutlink/domain/link/service/LinkServiceTest.java +++ b/src/test/java/com/seong/shoutlink/domain/link/service/LinkServiceTest.java @@ -12,11 +12,13 @@ import com.seong.shoutlink.domain.link.repository.StubLinkRepository; import com.seong.shoutlink.domain.link.service.request.CreateHubLinkCommand; import com.seong.shoutlink.domain.link.service.request.CreateLinkCommand; +import com.seong.shoutlink.domain.link.service.request.DeleteHubLinkCommand; import com.seong.shoutlink.domain.link.service.request.DeleteLinkCommand; import com.seong.shoutlink.domain.link.service.request.FindHubLinksCommand; import com.seong.shoutlink.domain.link.service.request.FindLinksCommand; import com.seong.shoutlink.domain.link.service.response.CreateHubLinkResponse; import com.seong.shoutlink.domain.link.service.response.CreateLinkResponse; +import com.seong.shoutlink.domain.link.service.response.DeleteLinkResponse; import com.seong.shoutlink.domain.link.service.response.FindLinksResponse; import com.seong.shoutlink.domain.linkbundle.LinkBundle; import com.seong.shoutlink.domain.linkbundle.repository.StubLinkBundleRepository; @@ -472,4 +474,114 @@ void notFound_WhenMemberNotFound() { .isEqualTo(ErrorCode.NOT_FOUND); } } + + @Nested + @DisplayName("deleteHubLink 호출 시") + class DeleteHubLinkTest { + + @Test + @DisplayName("성공: 허브 링크 삭제됨") + void deleteHubLink() { + //given + Member member = MemberFixture.member(); + Hub hub = HubFixture.hub(member); + Link link = LinkFixture.link(); + memberRepository.stub(member); + hubRepository.stub(hub); + linkRepository.stub(link); + DeleteHubLinkCommand command = new DeleteHubLinkCommand(link.getLinkId(), + member.getMemberId(), hub.getHubId()); + + //when + DeleteLinkResponse response = linkService.deleteHubLink(command); + + //then + assertThat(linkRepository.count()).isZero(); + } + + @Test + @DisplayName("예외(unauthorized): 회원이 허브의 마스터가 아님") + void unauthorized_WhenMemberIsNotHubMaster() { + //given + Member hubMaster = MemberFixture.member(); + Hub hub = HubFixture.hub(hubMaster); + Link link = LinkFixture.link(); + Member member = new Member(2L, "asdf1234@gmail.com", "asdf1234!", "asdf", + MemberRole.ROLE_USER); + memberRepository.stub(hubMaster); + memberRepository.stub(member); + hubRepository.stub(hub); + linkRepository.stub(link); + DeleteHubLinkCommand command = new DeleteHubLinkCommand(link.getLinkId(), + member.getMemberId(), hub.getHubId()); + + //when + Exception exception = catchException(() -> linkService.deleteHubLink(command)); + + //then + assertThat(exception).isInstanceOf(ShoutLinkException.class) + .extracting(e -> ((ShoutLinkException) e).getErrorCode()) + .isEqualTo(ErrorCode.UNAUTHORIZED); + } + + @Test + @DisplayName("예외(notFound): 존재하지 않는 허브") + void notFound_WhenHubNotFound() { + //given + Member member = MemberFixture.member(); + Link link = LinkFixture.link(); + memberRepository.stub(member); + linkRepository.stub(link); + DeleteHubLinkCommand command = new DeleteHubLinkCommand(link.getLinkId(), + member.getMemberId(), 1L); + + //when + Exception exception = catchException(() -> linkService.deleteHubLink(command)); + + //then + assertThat(exception).isInstanceOf(ShoutLinkException.class) + .extracting(e -> ((ShoutLinkException) e).getErrorCode()) + .isEqualTo(ErrorCode.NOT_FOUND); + } + + @Test + @DisplayName("예외(notFound): 존재하지 않는 회원") + void notFound_WhenMemberNotFound() { + //given + Hub hub = HubFixture.hub(MemberFixture.member()); + Link link = LinkFixture.link(); + hubRepository.stub(hub); + linkRepository.stub(link); + DeleteHubLinkCommand command = new DeleteHubLinkCommand(link.getLinkId(), + 1L, hub.getHubId()); + + //when + Exception exception = catchException(() -> linkService.deleteHubLink(command)); + + //then + assertThat(exception).isInstanceOf(ShoutLinkException.class) + .extracting(e -> ((ShoutLinkException) e).getErrorCode()) + .isEqualTo(ErrorCode.NOT_FOUND); + } + + @Test + @DisplayName("예외(notFound): 존재하지 않는 링크") + void notFound_WhenLinkNotFound() { + //given + Member member = MemberFixture.member(); + Hub hub = HubFixture.hub(member); + memberRepository.stub(member); + hubRepository.stub(hub); + DeleteHubLinkCommand command = new DeleteHubLinkCommand(1L, member.getMemberId(), + hub.getHubId()); + + //when + Exception exception = catchException(() -> linkService.deleteHubLink(command)); + + //then + assertThat(exception).isInstanceOf(ShoutLinkException.class) + .extracting(e -> ((ShoutLinkException) e).getErrorCode()) + .isEqualTo(ErrorCode.NOT_FOUND); + } + } } From 09462b320fd8240077aa52c8492e925328de2773 Mon Sep 17 00:00:00 2001 From: hseong3243 Date: Sun, 21 Apr 2024 21:10:02 +0900 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=ED=97=88=EB=B8=8C=20=EB=A7=81?= =?UTF-8?q?=ED=81=AC=20=EC=82=AD=EC=A0=9C=20=EC=98=81=EC=86=8D=EC=84=B1=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shoutlink/domain/link/repository/LinkJpaRepository.java | 5 +++++ .../shoutlink/domain/link/repository/LinkRepositoryImpl.java | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/seong/shoutlink/domain/link/repository/LinkJpaRepository.java b/src/main/java/com/seong/shoutlink/domain/link/repository/LinkJpaRepository.java index 3263128..c116a22 100644 --- a/src/main/java/com/seong/shoutlink/domain/link/repository/LinkJpaRepository.java +++ b/src/main/java/com/seong/shoutlink/domain/link/repository/LinkJpaRepository.java @@ -26,4 +26,9 @@ public interface LinkJpaRepository extends JpaRepository { + "join MemberLinkBundleEntity lb on l.linkBundleId = lb.linkBundleId " + "where l.linkId = :linkId and lb.memberId = :memberId") Optional findByIdAndMemberId(@Param("linkId") Long linkId, @Param("memberId") Long memberId); + + @Query("select l from LinkEntity l " + + "join HubLinkBundleEntity lb on l.linkBundleId = lb.linkBundleId " + + "where l.linkId = :linkId and lb.hubId = :hubId") + Optional findByIdAndHubId(@Param("linkId") Long linkId, @Param("hubId") Long hubId); } diff --git a/src/main/java/com/seong/shoutlink/domain/link/repository/LinkRepositoryImpl.java b/src/main/java/com/seong/shoutlink/domain/link/repository/LinkRepositoryImpl.java index 63df7a9..446df7a 100644 --- a/src/main/java/com/seong/shoutlink/domain/link/repository/LinkRepositoryImpl.java +++ b/src/main/java/com/seong/shoutlink/domain/link/repository/LinkRepositoryImpl.java @@ -79,7 +79,8 @@ public Optional findMemberLink(Long linkId, Member member) { @Override public Optional findHubLink(Long linkId, Hub hub) { - return Optional.empty(); + return linkJpaRepository.findByIdAndHubId(linkId, hub.getHubId()) + .map(LinkEntity::toDomain); } @Override From dc23821cca79c5b6962f74633c7579827b55f284 Mon Sep 17 00:00:00 2001 From: hseong3243 Date: Sun, 21 Apr 2024 21:14:09 +0900 Subject: [PATCH 4/4] =?UTF-8?q?feat:=20=ED=97=88=EB=B8=8C=20=EB=A7=81?= =?UTF-8?q?=ED=81=AC=20=EC=82=AD=EC=A0=9C=20api=20=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 + .../link/controller/LinkController.java | 11 + src/main/resources/static/docs/index.html | 232 +++++++++++++----- .../link/controller/LinkControllerTest.java | 27 ++ 4 files changed, 218 insertions(+), 62 deletions(-) diff --git a/src/docs/asciidoc/index.adoc b/src/docs/asciidoc/index.adoc index 961b467..2352916 100644 --- a/src/docs/asciidoc/index.adoc +++ b/src/docs/asciidoc/index.adoc @@ -119,6 +119,16 @@ operation::link-controller-test/delete-link[snippets='http-request,request-heade operation::link-controller-test/delete-link[snippets='http-response,response-fields'] +=== 허브 링크 삭제 + +==== request + +operation::link-controller-test/delete-hub-link[snippets='http-request,request-headers,path-parameters'] + +==== response + +operation::link-controller-test/delete-hub-link[snippets='http-response,response-fields'] + == 허브 === 허브 생성 diff --git a/src/main/java/com/seong/shoutlink/domain/link/controller/LinkController.java b/src/main/java/com/seong/shoutlink/domain/link/controller/LinkController.java index 8291547..4720333 100644 --- a/src/main/java/com/seong/shoutlink/domain/link/controller/LinkController.java +++ b/src/main/java/com/seong/shoutlink/domain/link/controller/LinkController.java @@ -7,6 +7,7 @@ import com.seong.shoutlink.domain.link.service.LinkUseCase; import com.seong.shoutlink.domain.link.service.request.CreateHubLinkCommand; import com.seong.shoutlink.domain.link.service.request.CreateLinkCommand; +import com.seong.shoutlink.domain.link.service.request.DeleteHubLinkCommand; import com.seong.shoutlink.domain.link.service.request.DeleteLinkCommand; import com.seong.shoutlink.domain.link.service.request.FindHubLinksCommand; import com.seong.shoutlink.domain.link.service.request.FindLinksCommand; @@ -94,4 +95,14 @@ public ResponseEntity deleteLink( new DeleteLinkCommand(memberId, linkId)); return ResponseEntity.ok(deleteLinkResponse); } + + @DeleteMapping("/hubs/{hubId}/links/{linkId}") + public ResponseEntity deleteHubLink( + @LoginUser Long memberId, + @PathVariable("hubId") Long hubId, + @PathVariable("linkId") Long linkId) { + DeleteLinkResponse response = linkUseCase.deleteHubLink( + new DeleteHubLinkCommand(linkId, memberId, hubId)); + return ResponseEntity.ok(response); + } } \ No newline at end of file diff --git a/src/main/resources/static/docs/index.html b/src/main/resources/static/docs/index.html index 8462859..9681533 100644 --- a/src/main/resources/static/docs/index.html +++ b/src/main/resources/static/docs/index.html @@ -471,6 +471,7 @@

API 문서

  • 허브 링크 생성
  • 허브 링크 목록 조회
  • 회원 링크 삭제
  • +
  • 허브 링크 삭제
  • 허브 @@ -651,7 +652,7 @@
    POST /api/link-bundles HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzEzNjk3MDc5LCJzdWIiOiIxIiwiZXhwIjoxNzEzNzAwNjc5LCJyb2xlIjoiUk9MRV9VU0VSIn0.dgA78BnrQKybFlhoStHl0TT8R5DvM-3RK8nUniPZqPc
    +Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzEzNzAxNjI5LCJzdWIiOiIxIiwiZXhwIjoxNzEzNzA1MjI5LCJyb2xlIjoiUk9MRV9VU0VSIn0.CAkcmImII9LETdcR0Q0FFXv4vf4gjYIyKUvLZA5nhSo
     Content-Length: 57
     Host: localhost:8080
     
    @@ -767,7 +768,7 @@ 
    GET /api/link-bundles HTTP/1.1
    -Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzEzNjk3MDc5LCJzdWIiOiIxIiwiZXhwIjoxNzEzNzAwNjc5LCJyb2xlIjoiUk9MRV9VU0VSIn0.dgA78BnrQKybFlhoStHl0TT8R5DvM-3RK8nUniPZqPc
    +Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzEzNzAxNjI5LCJzdWIiOiIxIiwiZXhwIjoxNzEzNzA1MjI5LCJyb2xlIjoiUk9MRV9VU0VSIn0.CAkcmImII9LETdcR0Q0FFXv4vf4gjYIyKUvLZA5nhSo
     Host: localhost:8080
    @@ -872,7 +873,7 @@
    POST /api/hubs/1/link-bundles HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzEzNjk3MDc5LCJzdWIiOiIxIiwiZXhwIjoxNzEzNzAwNjc5LCJyb2xlIjoiUk9MRV9VU0VSIn0.dgA78BnrQKybFlhoStHl0TT8R5DvM-3RK8nUniPZqPc
    +Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzEzNzAxNjI5LCJzdWIiOiIxIiwiZXhwIjoxNzEzNzA1MjI5LCJyb2xlIjoiUk9MRV9VU0VSIn0.CAkcmImII9LETdcR0Q0FFXv4vf4gjYIyKUvLZA5nhSo
     Content-Length: 53
     Host: localhost:8080
     
    @@ -1010,7 +1011,7 @@ 
    GET /api/hubs/1/link-bundles HTTP/1.1
    -Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzEzNjk3MDc5LCJzdWIiOiIxIiwiZXhwIjoxNzEzNzAwNjc5LCJyb2xlIjoiUk9MRV9VU0VSIn0.dgA78BnrQKybFlhoStHl0TT8R5DvM-3RK8nUniPZqPc
    +Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzEzNzAxNjI5LCJzdWIiOiIxIiwiZXhwIjoxNzEzNzA1MjI5LCJyb2xlIjoiUk9MRV9VU0VSIn0.CAkcmImII9LETdcR0Q0FFXv4vf4gjYIyKUvLZA5nhSo
     Host: localhost:8080
    @@ -1138,7 +1139,7 @@
    POST /api/links HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzEzNjk3MDc5LCJzdWIiOiIxIiwiZXhwIjoxNzEzNzAwNjc5LCJyb2xlIjoiUk9MRV9VU0VSIn0.dgA78BnrQKybFlhoStHl0TT8R5DvM-3RK8nUniPZqPc
    +Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzEzNzAxNjI5LCJzdWIiOiIxIiwiZXhwIjoxNzEzNzA1MjI5LCJyb2xlIjoiUk9MRV9VU0VSIn0.CAkcmImII9LETdcR0Q0FFXv4vf4gjYIyKUvLZA5nhSo
     Content-Length: 100
     Host: localhost:8080
     
    @@ -1260,7 +1261,7 @@ 
    GET /api/links?linkBundleId=1&page=0&size=10 HTTP/1.1
    -Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzEzNjk3MDc5LCJzdWIiOiIxIiwiZXhwIjoxNzEzNzAwNjc5LCJyb2xlIjoiUk9MRV9VU0VSIn0.dgA78BnrQKybFlhoStHl0TT8R5DvM-3RK8nUniPZqPc
    +Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzEzNzAxNjI5LCJzdWIiOiIxIiwiZXhwIjoxNzEzNzA1MjI5LCJyb2xlIjoiUk9MRV9VU0VSIn0.CAkcmImII9LETdcR0Q0FFXv4vf4gjYIyKUvLZA5nhSo
     Host: localhost:8080
    @@ -1327,7 +1328,7 @@
    POST /api/hubs/1/links HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzEzNjk3MDc5LCJzdWIiOiIxIiwiZXhwIjoxNzEzNzAwNjc5LCJyb2xlIjoiUk9MRV9VU0VSIn0.dgA78BnrQKybFlhoStHl0TT8R5DvM-3RK8nUniPZqPc
    +Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzEzNzAxNjI5LCJzdWIiOiIxIiwiZXhwIjoxNzEzNzA1MjI5LCJyb2xlIjoiUk9MRV9VU0VSIn0.CAkcmImII9LETdcR0Q0FFXv4vf4gjYIyKUvLZA5nhSo
     Content-Length: 69
     Host: localhost:8080
     
    @@ -1471,7 +1472,7 @@ 
    GET /api/hubs/1/links?linkBundleId=1&page=0&size=20 HTTP/1.1
    -Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzEzNjk3MDc5LCJzdWIiOiIxIiwiZXhwIjoxNzEzNzAwNjc5LCJyb2xlIjoiUk9MRV9VU0VSIn0.dgA78BnrQKybFlhoStHl0TT8R5DvM-3RK8nUniPZqPc
    +Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzEzNzAxNjI5LCJzdWIiOiIxIiwiZXhwIjoxNzEzNzA1MjI5LCJyb2xlIjoiUk9MRV9VU0VSIn0.CAkcmImII9LETdcR0Q0FFXv4vf4gjYIyKUvLZA5nhSo
     Host: localhost:8080
    @@ -1634,7 +1635,7 @@
    DELETE /api/links/1 HTTP/1.1
    -Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzEzNjk3MDc5LCJzdWIiOiIxIiwiZXhwIjoxNzEzNzAwNjc5LCJyb2xlIjoiUk9MRV9VU0VSIn0.dgA78BnrQKybFlhoStHl0TT8R5DvM-3RK8nUniPZqPc
    +Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzEzNzAxNjI5LCJzdWIiOiIxIiwiZXhwIjoxNzEzNzA1MjI5LCJyb2xlIjoiUk9MRV9VU0VSIn0.CAkcmImII9LETdcR0Q0FFXv4vf4gjYIyKUvLZA5nhSo
     Host: localhost:8080
    @@ -1728,6 +1729,113 @@
    +

    허브 링크 삭제

    +
    +

    request

    +
    +
    HTTP request
    +
    +
    +
    DELETE /api/hubs/1/links/1 HTTP/1.1
    +Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzEzNzAxNjI5LCJzdWIiOiIxIiwiZXhwIjoxNzEzNzA1MjI5LCJyb2xlIjoiUk9MRV9VU0VSIn0.CAkcmImII9LETdcR0Q0FFXv4vf4gjYIyKUvLZA5nhSo
    +Host: localhost:8080
    +
    +
    +
    +
    +
    Request headers
    + ++++ + + + + + + + + + + + + +
    NameDescription

    Authorization

    액세스 토큰

    +
    +
    +
    Path parameters
    + + ++++ + + + + + + + + + + + + + + + + +
    Table 1. /api/hubs/{hubId}/links/{linkId}
    ParameterDescription

    hubId

    허브 ID

    linkId

    링크 ID

    +
    +
    +
    +

    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: 18
    +
    +{
    +  "linkId" : 1
    +}
    +
    +
    +
    +
    +
    Response fields
    + +++++ + + + + + + + + + + + + + + +
    PathTypeDescription

    linkId

    Number

    삭제된 링크 ID

    +
    +
    +
    @@ -1736,14 +1844,14 @@

    허브

    허브 생성

    -

    request

    +

    request

    -
    HTTP request
    +
    HTTP request
    POST /api/hubs HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzEzNjk3MDc4LCJzdWIiOiIxIiwiZXhwIjoxNzEzNzAwNjc4LCJyb2xlIjoiUk9MRV9VU0VSIn0.Pn2wYf3T95eJ9FjqTb5DW31x4OPEUerPYySHMLNQrV0
    +Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzEzNzAxNjI5LCJzdWIiOiIxIiwiZXhwIjoxNzEzNzA1MjI5LCJyb2xlIjoiUk9MRV9VU0VSIn0.CAkcmImII9LETdcR0Q0FFXv4vf4gjYIyKUvLZA5nhSo
     Content-Length: 88
     Host: localhost:8080
     
    @@ -1756,7 +1864,7 @@ 
    -
    Request headers
    +
    Request headers
    @@ -1777,7 +1885,7 @@
    -
    Request fields
    +
    Request fields
    @@ -1812,9 +1920,9 @@
    -

    response

    +

    response

    -
    HTTP response
    +
    HTTP response
    HTTP/1.1 201 Created
    @@ -1831,7 +1939,7 @@ 
    -
    Response fields
    +
    Response fields
    @@ -1859,9 +1967,9 @@

    허브 목록 조회

    -

    request

    +

    request

    -
    HTTP request
    +
    HTTP request
    GET /api/hubs?page=0&size=0 HTTP/1.1
    @@ -1870,7 +1978,7 @@ 
    -
    Path parameters
    +
    Path parameters
    @@ -1897,9 +2005,9 @@
    -

    response

    +

    response

    -
    HTTP response
    +
    HTTP response
    HTTP/1.1 200 OK
    @@ -1928,7 +2036,7 @@ 
    -
    Response fields
    +
    Response fields
    Table 1. /api/hubs
    @@ -2006,9 +2114,9 @@

    허브 조회

    -

    request

    +

    request

    -
    HTTP request
    +
    HTTP request
    GET /api/hubs/1 HTTP/1.1
    @@ -2017,7 +2125,7 @@ 
    -
    Path parameters
    +
    Path parameters
    @@ -2040,9 +2148,9 @@
    -

    response

    +

    response

    -
    HTTP response
    +
    HTTP response
    HTTP/1.1 200 OK
    @@ -2068,19 +2176,19 @@ 

    사용자의 허브 목록 조회

    -

    request

    +

    request

    -
    HTTP request
    +
    HTTP request
    GET /api/hubs/me?page=0&size=10 HTTP/1.1
    -Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzEzNjk3MDc4LCJzdWIiOiIxIiwiZXhwIjoxNzEzNzAwNjc4LCJyb2xlIjoiUk9MRV9VU0VSIn0.Pn2wYf3T95eJ9FjqTb5DW31x4OPEUerPYySHMLNQrV0
    +Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzEzNzAxNjI5LCJzdWIiOiIxIiwiZXhwIjoxNzEzNzA1MjI5LCJyb2xlIjoiUk9MRV9VU0VSIn0.CAkcmImII9LETdcR0Q0FFXv4vf4gjYIyKUvLZA5nhSo
     Host: localhost:8080
    Table 1. /api/hubs/{hubId}
    @@ -2101,7 +2209,7 @@
    -
    Query parameters
    +
    Query parameters
    @@ -2127,9 +2235,9 @@
    -

    response

    +

    response

    -
    HTTP response
    +
    HTTP response
    HTTP/1.1 200 OK
    @@ -2158,7 +2266,7 @@ 
    -
    Response fields
    +
    Response fields
    @@ -2241,9 +2349,9 @@

    도메인

    루트 도메인 목록 조회

    -

    request

    +

    request

    -
    HTTP request
    +
    HTTP request
    GET /api/domains/search?keyword=searchKeyword&size=20 HTTP/1.1
    @@ -2252,7 +2360,7 @@ 
    -
    Query parameters
    +
    Query parameters
    @@ -2278,9 +2386,9 @@
    -

    response

    +

    response

    -
    HTTP response
    +
    HTTP response
    HTTP/1.1 200 OK
    @@ -2297,7 +2405,7 @@ 
    -
    Response fields
    +
    Response fields
    @@ -2325,9 +2433,9 @@

    도메인 목록 조회

    -

    request

    +

    request

    -
    HTTP request
    +
    HTTP request
    GET /api/domains?keyword=searchKeyword&page=0&size=10 HTTP/1.1
    @@ -2336,7 +2444,7 @@ 
    -
    Query parameters
    +
    Query parameters
    @@ -2366,9 +2474,9 @@
    -

    response

    +

    response

    -
    HTTP response
    +
    HTTP response
    HTTP/1.1 200 OK
    @@ -2390,7 +2498,7 @@ 
    -
    Response fields
    +
    Response fields
    @@ -2438,9 +2546,9 @@

    도메인 단건 조회

    -

    request

    +

    request

    -
    HTTP request
    +
    HTTP request
    GET /api/domains/1 HTTP/1.1
    @@ -2449,7 +2557,7 @@ 
    -
    Path parameters
    +
    Path parameters
    @@ -2472,9 +2580,9 @@
    -

    response

    +

    response

    -
    HTTP response
    +
    HTTP response
    HTTP/1.1 200 OK
    @@ -2492,7 +2600,7 @@ 
    -
    Response fields
    +
    Response fields
    Table 1. /api/domains/{domainId}
    @@ -2525,9 +2633,9 @@

    도메인 링크 목록 조회

    -

    request

    +

    request

    -
    HTTP request
    +
    HTTP request
    GET /api/domains/1/links?page=0&size=10 HTTP/1.1
    @@ -2536,7 +2644,7 @@ 
    -
    Path parameters
    +
    Path parameters
    @@ -2558,7 +2666,7 @@
    -
    Query parameters
    +
    Query parameters
    Table 1. /api/domains/{domainId}/links
    @@ -2584,9 +2692,9 @@
    -

    response

    +

    response

    -
    HTTP response
    +
    HTTP response
    HTTP/1.1 200 OK
    @@ -2609,7 +2717,7 @@ 
    -
    Response fields
    +
    Response fields
    @@ -2665,7 +2773,7 @@
    diff --git a/src/test/java/com/seong/shoutlink/domain/link/controller/LinkControllerTest.java b/src/test/java/com/seong/shoutlink/domain/link/controller/LinkControllerTest.java index 0277dc6..ab06b84 100644 --- a/src/test/java/com/seong/shoutlink/domain/link/controller/LinkControllerTest.java +++ b/src/test/java/com/seong/shoutlink/domain/link/controller/LinkControllerTest.java @@ -216,4 +216,31 @@ void deleteLink() throws Exception { ) )); } + + @Test + @DisplayName("성공: 허브 링크 삭제 api 호출 시") + void deleteHubLink() throws Exception { + //given + given(linkService.deleteHubLink(any())).willReturn(new DeleteLinkResponse(1L)); + + //when + ResultActions resultActions = mockMvc.perform( + delete("/api/hubs/{hubId}/links/{linkId}", 1L, 1L) + .header(AUTHORIZATION, bearerAccessToken)); + + //then + resultActions.andExpect(status().isOk()) + .andDo(restDocs.document( + requestHeaders( + headerWithName(AUTHORIZATION).description("액세스 토큰") + ), + pathParameters( + parameterWithName("hubId").description("허브 ID"), + parameterWithName("linkId").description("링크 ID") + ), + responseFields( + fieldWithPath("linkId").description("삭제된 링크 ID") + ) + )); + } } \ No newline at end of file