From 27b47eff87450d7a5f341a9bcede145a52cd1e98 Mon Sep 17 00:00:00 2001 From: hseong3243 Date: Mon, 12 Feb 2024 22:20:03 +0900 Subject: [PATCH 01/10] =?UTF-8?q?feat:=20`LinkBundleEntity`=EB=A5=BC=20?= =?UTF-8?q?=EC=83=81=EC=86=8D=ED=95=98=EB=8A=94=20`MemberLinkBundleEntity`?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/linkbundle/HubLinkBundle.java | 16 ++++++++++ .../domain/linkbundle/LinkBundle.java | 9 ++---- .../domain/linkbundle/MemberLinkBundle.java | 20 ++++++++++++ .../repository/LinkBundleEntity.java | 31 ++++++++++--------- .../repository/LinkBundleJpaRepository.java | 6 ++-- .../repository/LinkBundleRepositoryImpl.java | 7 +++-- .../repository/MemberLinkBundleEntity.java | 21 +++++++++++++ .../service/LinkBundleRepository.java | 3 +- .../linkbundle/service/LinkBundleService.java | 7 +++-- .../repository/FakeLinkBundleRepository.java | 12 +++---- .../service/LinkBundleServiceTest.java | 14 ++++++--- .../shoutlink/fixture/LinkBundleFixture.java | 2 +- 12 files changed, 107 insertions(+), 41 deletions(-) create mode 100644 src/main/java/com/seong/shoutlink/domain/linkbundle/HubLinkBundle.java create mode 100644 src/main/java/com/seong/shoutlink/domain/linkbundle/MemberLinkBundle.java create mode 100644 src/main/java/com/seong/shoutlink/domain/linkbundle/repository/MemberLinkBundleEntity.java diff --git a/src/main/java/com/seong/shoutlink/domain/linkbundle/HubLinkBundle.java b/src/main/java/com/seong/shoutlink/domain/linkbundle/HubLinkBundle.java new file mode 100644 index 0000000..c9404bb --- /dev/null +++ b/src/main/java/com/seong/shoutlink/domain/linkbundle/HubLinkBundle.java @@ -0,0 +1,16 @@ +package com.seong.shoutlink.domain.linkbundle; + +import com.seong.shoutlink.domain.hub.Hub; +import lombok.Getter; + +@Getter +public class HubLinkBundle { + + private final Hub hub; + private final LinkBundle linkBundle; + + public HubLinkBundle(Hub hub, LinkBundle linkBundle) { + this.hub = hub; + this.linkBundle = linkBundle; + } +} diff --git a/src/main/java/com/seong/shoutlink/domain/linkbundle/LinkBundle.java b/src/main/java/com/seong/shoutlink/domain/linkbundle/LinkBundle.java index ee1140f..dad61c2 100644 --- a/src/main/java/com/seong/shoutlink/domain/linkbundle/LinkBundle.java +++ b/src/main/java/com/seong/shoutlink/domain/linkbundle/LinkBundle.java @@ -1,6 +1,5 @@ package com.seong.shoutlink.domain.linkbundle; -import com.seong.shoutlink.domain.member.Member; import java.util.Objects; import lombok.Getter; @@ -10,17 +9,15 @@ public class LinkBundle { private Long linkBundleId; private String description; private boolean isDefault; - private Long memberId; - public LinkBundle(String description, boolean isDefault, Member member) { - this(null, description, isDefault, member.getMemberId()); + public LinkBundle(String description, boolean isDefault) { + this(null, description, isDefault); } - public LinkBundle(Long linkBundleId, String description, boolean isDefault, Long memberId) { + public LinkBundle(Long linkBundleId, String description, boolean isDefault) { this.linkBundleId = linkBundleId; this.description = description; this.isDefault = isDefault; - this.memberId = memberId; } public void initId(Long linkBundleId) { diff --git a/src/main/java/com/seong/shoutlink/domain/linkbundle/MemberLinkBundle.java b/src/main/java/com/seong/shoutlink/domain/linkbundle/MemberLinkBundle.java new file mode 100644 index 0000000..12dfd1f --- /dev/null +++ b/src/main/java/com/seong/shoutlink/domain/linkbundle/MemberLinkBundle.java @@ -0,0 +1,20 @@ +package com.seong.shoutlink.domain.linkbundle; + +import com.seong.shoutlink.domain.member.Member; +import lombok.Getter; + +@Getter +public class MemberLinkBundle { + + private final Member member; + private final LinkBundle linkBundle; + + public MemberLinkBundle(Member member, LinkBundle linkBundle) { + this.member = member; + this.linkBundle = linkBundle; + } + + public void initLinkBundleId(Long linkBundleId) { + linkBundle.initId(linkBundleId); + } +} diff --git a/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/LinkBundleEntity.java b/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/LinkBundleEntity.java index 4399312..70bf19f 100644 --- a/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/LinkBundleEntity.java +++ b/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/LinkBundleEntity.java @@ -1,18 +1,25 @@ package com.seong.shoutlink.domain.linkbundle.repository; import com.seong.shoutlink.domain.linkbundle.LinkBundle; +import com.seong.shoutlink.domain.linkbundle.MemberLinkBundle; +import com.seong.shoutlink.domain.member.Member; +import jakarta.persistence.DiscriminatorColumn; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @Entity @Getter +@Inheritance(strategy = InheritanceType.SINGLE_TABLE) +@DiscriminatorColumn @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class LinkBundleEntity { +public abstract class LinkBundleEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -20,33 +27,29 @@ public class LinkBundleEntity { private String description; private boolean isDefault; - private Long memberId; - private LinkBundleEntity( - Long linkBundleId, + protected LinkBundleEntity( String description, - boolean isDefault, - Long memberId) { - this.linkBundleId = linkBundleId; + boolean isDefault) { this.description = description; this.isDefault = isDefault; - this.memberId = memberId; } - public static LinkBundleEntity create(LinkBundle linkBundle) { - return new LinkBundleEntity( - linkBundle.getLinkBundleId(), + public static LinkBundleEntity create(MemberLinkBundle memberLinkBundle) { + LinkBundle linkBundle = memberLinkBundle.getLinkBundle(); + Member member = memberLinkBundle.getMember(); + return new MemberLinkBundleEntity( linkBundle.getDescription(), linkBundle.isDefault(), - linkBundle.getMemberId()); + member.getMemberId() + ); } public LinkBundle toDomain() { return new LinkBundle( linkBundleId, description, - isDefault, - memberId + isDefault ); } } diff --git a/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/LinkBundleJpaRepository.java b/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/LinkBundleJpaRepository.java index d7dea77..05c003b 100644 --- a/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/LinkBundleJpaRepository.java +++ b/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/LinkBundleJpaRepository.java @@ -9,9 +9,11 @@ public interface LinkBundleJpaRepository extends JpaRepository { @Modifying - @Query("update LinkBundleEntity lb set lb.isDefault = false" + @Query("update MemberLinkBundleEntity lb set lb.isDefault = false" + " where lb.isDefault = true and lb.memberId = :memberId") void updateDefaultBundleFalse(@Param("memberId") Long memberId); - List findAllByMemberId(Long memberId); + @Query("select lb from MemberLinkBundleEntity lb " + + "where lb.memberId = :memberId") + List findAllByMemberId(@Param("memberId") Long memberId); } diff --git a/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/LinkBundleRepositoryImpl.java b/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/LinkBundleRepositoryImpl.java index 11dce7c..5fd44bd 100644 --- a/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/LinkBundleRepositoryImpl.java +++ b/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/LinkBundleRepositoryImpl.java @@ -1,6 +1,7 @@ package com.seong.shoutlink.domain.linkbundle.repository; import com.seong.shoutlink.domain.linkbundle.LinkBundle; +import com.seong.shoutlink.domain.linkbundle.MemberLinkBundle; import com.seong.shoutlink.domain.linkbundle.service.LinkBundleRepository; import com.seong.shoutlink.domain.member.Member; import java.util.List; @@ -15,10 +16,10 @@ public class LinkBundleRepositoryImpl implements LinkBundleRepository { private final LinkBundleJpaRepository linkBundleJpaRepository; @Override - public Long save(LinkBundle linkBundle) { - LinkBundleEntity linkBundleEntity = LinkBundleEntity.create(linkBundle); + public Long save(MemberLinkBundle memberLinkBundle) { + LinkBundleEntity linkBundleEntity = LinkBundleEntity.create(memberLinkBundle); linkBundleJpaRepository.save(linkBundleEntity); - linkBundle.initId(linkBundleEntity.getLinkBundleId()); + memberLinkBundle.initLinkBundleId(linkBundleEntity.getLinkBundleId()); return linkBundleEntity.getLinkBundleId(); } diff --git a/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/MemberLinkBundleEntity.java b/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/MemberLinkBundleEntity.java new file mode 100644 index 0000000..ad4e96e --- /dev/null +++ b/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/MemberLinkBundleEntity.java @@ -0,0 +1,21 @@ +package com.seong.shoutlink.domain.linkbundle.repository; + +import jakarta.persistence.DiscriminatorValue; +import jakarta.persistence.Entity; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@DiscriminatorValue("member") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class MemberLinkBundleEntity extends LinkBundleEntity { + + private Long memberId; + + public MemberLinkBundleEntity(String description, boolean isDefault, Long memberId) { + super(description, isDefault); + this.memberId = memberId; + } +} diff --git a/src/main/java/com/seong/shoutlink/domain/linkbundle/service/LinkBundleRepository.java b/src/main/java/com/seong/shoutlink/domain/linkbundle/service/LinkBundleRepository.java index f0508ba..5b7fa3d 100644 --- a/src/main/java/com/seong/shoutlink/domain/linkbundle/service/LinkBundleRepository.java +++ b/src/main/java/com/seong/shoutlink/domain/linkbundle/service/LinkBundleRepository.java @@ -1,13 +1,14 @@ package com.seong.shoutlink.domain.linkbundle.service; import com.seong.shoutlink.domain.linkbundle.LinkBundle; +import com.seong.shoutlink.domain.linkbundle.MemberLinkBundle; import com.seong.shoutlink.domain.member.Member; import java.util.List; import java.util.Optional; public interface LinkBundleRepository { - Long save(LinkBundle linkBundle); + Long save(MemberLinkBundle memberLinkBundle); void updateDefaultBundleFalse(Member member); 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 8acacc2..4e37666 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 @@ -3,6 +3,7 @@ import com.seong.shoutlink.domain.exception.ErrorCode; import com.seong.shoutlink.domain.exception.ShoutLinkException; import com.seong.shoutlink.domain.linkbundle.LinkBundle; +import com.seong.shoutlink.domain.linkbundle.MemberLinkBundle; import com.seong.shoutlink.domain.linkbundle.service.request.FindLinkBundlesCommand; import com.seong.shoutlink.domain.linkbundle.service.response.CreateLinkBundleCommand; import com.seong.shoutlink.domain.linkbundle.service.response.CreateLinkBundleResponse; @@ -29,9 +30,9 @@ public CreateLinkBundleResponse createLinkBundle(CreateLinkBundleCommand command } LinkBundle linkBundle = new LinkBundle( command.description(), - command.isDefault(), - member); - return new CreateLinkBundleResponse(linkBundleRepository.save(linkBundle)); + command.isDefault()); + MemberLinkBundle memberLinkBundle = new MemberLinkBundle(member, linkBundle); + return new CreateLinkBundleResponse(linkBundleRepository.save(memberLinkBundle)); } public FindLinkBundlesResponse findLinkBundles(FindLinkBundlesCommand command) { diff --git a/src/test/java/com/seong/shoutlink/domain/linkbundle/repository/FakeLinkBundleRepository.java b/src/test/java/com/seong/shoutlink/domain/linkbundle/repository/FakeLinkBundleRepository.java index 3222df9..750a3c2 100644 --- a/src/test/java/com/seong/shoutlink/domain/linkbundle/repository/FakeLinkBundleRepository.java +++ b/src/test/java/com/seong/shoutlink/domain/linkbundle/repository/FakeLinkBundleRepository.java @@ -1,6 +1,7 @@ package com.seong.shoutlink.domain.linkbundle.repository; import com.seong.shoutlink.domain.linkbundle.LinkBundle; +import com.seong.shoutlink.domain.linkbundle.MemberLinkBundle; import com.seong.shoutlink.domain.linkbundle.service.LinkBundleRepository; import com.seong.shoutlink.domain.member.Member; import java.util.HashMap; @@ -26,22 +27,19 @@ public void stub(LinkBundle... linkBundles) { } @Override - public Long save(LinkBundle linkBundle) { - long nextId = getNextId(); - linkBundle.initId(nextId); - memory.put(nextId, linkBundle); - return nextId; + public Long save(MemberLinkBundle memberLinkBundle) { + return 1L; } @Override public void updateDefaultBundleFalse(Member member) { List defaultLinkBundle = memory.values().stream() - .filter(linkBundle -> linkBundle.isDefault() && linkBundle.getMemberId().equals(member.getMemberId())) + .filter(LinkBundle::isDefault) .toList(); defaultLinkBundle.forEach(lb -> { memory.remove(lb.getLinkBundleId()); memory.put(lb.getLinkBundleId(), - new LinkBundle(lb.getDescription(), false, member)); + new LinkBundle(lb.getDescription(), false)); }); } 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 17875b9..37a54f9 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 @@ -15,6 +15,7 @@ import com.seong.shoutlink.domain.member.Member; import com.seong.shoutlink.domain.member.repository.StubMemberRepository; import com.seong.shoutlink.fixture.MemberFixture; +import com.seong.shoutlink.fixture.StubHubRepository; import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -25,6 +26,7 @@ class LinkBundleServiceTest { private StubMemberRepository memberRepository; private FakeLinkBundleRepository linkBundleRepository; + private StubHubRepository stubHubRepository; @Nested @DisplayName("createLinkBundle 메서드 호출 시") @@ -38,7 +40,9 @@ void setUp() { savedMember = MemberFixture.member(); memberRepository = new StubMemberRepository(savedMember); linkBundleRepository = new FakeLinkBundleRepository(); - linkBundleService = new LinkBundleService(memberRepository, linkBundleRepository); + stubHubRepository = new StubHubRepository(); + linkBundleService = new LinkBundleService(memberRepository, stubHubRepository, + linkBundleRepository); } @Test @@ -88,7 +92,9 @@ class FindLinkBundlesTest { void setUp() { memberRepository = new StubMemberRepository(); linkBundleRepository = new FakeLinkBundleRepository(); - linkBundleService = new LinkBundleService(memberRepository, linkBundleRepository); + stubHubRepository = new StubHubRepository(); + linkBundleService = new LinkBundleService(memberRepository, stubHubRepository, + linkBundleRepository); } @Test @@ -98,8 +104,8 @@ void findLinkBundles() { Member stubMember = MemberFixture.member(); memberRepository.stub(stubMember); linkBundleRepository.stub( - new LinkBundle(1L, "기본", true, stubMember.getMemberId()), - new LinkBundle(2L, "두번째", false, stubMember.getMemberId()) + new LinkBundle(1L, "기본", true), + new LinkBundle(2L, "두번째", false) ); FindLinkBundlesCommand command = new FindLinkBundlesCommand(stubMember.getMemberId()); diff --git a/src/test/java/com/seong/shoutlink/fixture/LinkBundleFixture.java b/src/test/java/com/seong/shoutlink/fixture/LinkBundleFixture.java index b997d20..72d708f 100644 --- a/src/test/java/com/seong/shoutlink/fixture/LinkBundleFixture.java +++ b/src/test/java/com/seong/shoutlink/fixture/LinkBundleFixture.java @@ -10,6 +10,6 @@ public final class LinkBundleFixture { public static final boolean IS_DEFAULT = true; public static LinkBundle linkBundle(Member member) { - return new LinkBundle(LINK_BUNDLE_ID, DESCRIPTION, IS_DEFAULT, member.getMemberId()); + return new LinkBundle(LINK_BUNDLE_ID, DESCRIPTION, IS_DEFAULT); } } From a6836ee40e78790a859993f7d22a778fe0187289 Mon Sep 17 00:00:00 2001 From: hseong3243 Date: Mon, 12 Feb 2024 22:21:47 +0900 Subject: [PATCH 02/10] =?UTF-8?q?refactor:=20`SpringEventListener`?= =?UTF-8?q?=EC=9D=98=20=EC=9D=B4=EB=A6=84=EC=9D=84=20`LinkBundleEventListe?= =?UTF-8?q?ner`=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/seong/shoutlink/global/config/EventConfig.java | 6 +++--- ...ringEventListener.java => LinkBundleEventListener.java} | 7 ++++--- ...tListenerTest.java => LinkBundleEventListenerTest.java} | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) rename src/main/java/com/seong/shoutlink/global/event/{SpringEventListener.java => LinkBundleEventListener.java} (85%) rename src/test/java/com/seong/shoutlink/global/event/{SpringEventListenerTest.java => LinkBundleEventListenerTest.java} (93%) diff --git a/src/main/java/com/seong/shoutlink/global/config/EventConfig.java b/src/main/java/com/seong/shoutlink/global/config/EventConfig.java index 4684613..f87cf5b 100644 --- a/src/main/java/com/seong/shoutlink/global/config/EventConfig.java +++ b/src/main/java/com/seong/shoutlink/global/config/EventConfig.java @@ -2,7 +2,7 @@ import com.seong.shoutlink.domain.common.EventPublisher; import com.seong.shoutlink.domain.linkbundle.service.LinkBundleService; -import com.seong.shoutlink.global.event.SpringEventListener; +import com.seong.shoutlink.global.event.LinkBundleEventListener; import com.seong.shoutlink.global.event.SpringEventPublisher; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; @@ -17,7 +17,7 @@ public EventPublisher eventPublisher(ApplicationEventPublisher applicationEventP } @Bean - public SpringEventListener springEventListener(LinkBundleService linkBundleService) { - return new SpringEventListener(linkBundleService); + public LinkBundleEventListener springEventListener(LinkBundleService linkBundleService) { + return new LinkBundleEventListener(linkBundleService); } } diff --git a/src/main/java/com/seong/shoutlink/global/event/SpringEventListener.java b/src/main/java/com/seong/shoutlink/global/event/LinkBundleEventListener.java similarity index 85% rename from src/main/java/com/seong/shoutlink/global/event/SpringEventListener.java rename to src/main/java/com/seong/shoutlink/global/event/LinkBundleEventListener.java index e8fe60d..01744f9 100644 --- a/src/main/java/com/seong/shoutlink/global/event/SpringEventListener.java +++ b/src/main/java/com/seong/shoutlink/global/event/LinkBundleEventListener.java @@ -6,21 +6,22 @@ import lombok.RequiredArgsConstructor; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; -import org.springframework.transaction.event.TransactionPhase; import org.springframework.transaction.event.TransactionalEventListener; @RequiredArgsConstructor -public class SpringEventListener { +public class LinkBundleEventListener { private static final String DEFAULT_LINK_BUNDLE = "기본"; private final LinkBundleService linkBundleService; - @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) + @TransactionalEventListener @Transactional(propagation = Propagation.REQUIRES_NEW) public void createDefaultLinkBundle(CreateMemberEvent event) { CreateLinkBundleCommand command = new CreateLinkBundleCommand(event.memberId(), DEFAULT_LINK_BUNDLE, true); linkBundleService.createLinkBundle(command); } + + } diff --git a/src/test/java/com/seong/shoutlink/global/event/SpringEventListenerTest.java b/src/test/java/com/seong/shoutlink/global/event/LinkBundleEventListenerTest.java similarity index 93% rename from src/test/java/com/seong/shoutlink/global/event/SpringEventListenerTest.java rename to src/test/java/com/seong/shoutlink/global/event/LinkBundleEventListenerTest.java index 57e01ed..7cd2b85 100644 --- a/src/test/java/com/seong/shoutlink/global/event/SpringEventListenerTest.java +++ b/src/test/java/com/seong/shoutlink/global/event/LinkBundleEventListenerTest.java @@ -17,7 +17,7 @@ @SpringBootTest @RecordApplicationEvents -class SpringEventListenerTest { +class LinkBundleEventListenerTest { @Autowired private AuthService authService; @@ -44,7 +44,7 @@ void createDefaultLinkBundle() { //then LinkBundleEntity linkBundleEntity = em.createQuery( - "select lb from LinkBundleEntity lb where lb.memberId = :memberId", + "select mlb from MemberLinkBundleEntity mlb where mlb.memberId = :memberId", LinkBundleEntity.class) .setParameter("memberId", member.getMemberId()) .getSingleResult(); From a96d65886634cbb4a772664875e2af81d08ad0f1 Mon Sep 17 00:00:00 2001 From: hseong3243 Date: Mon, 12 Feb 2024 23:42:13 +0900 Subject: [PATCH 03/10] =?UTF-8?q?feat:=20=ED=97=88=EB=B8=8C=20=EB=A7=81?= =?UTF-8?q?=ED=81=AC=20=EB=AC=B6=EC=9D=8C=20=EC=83=9D=EC=84=B1=20=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4=20=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/exception/ErrorCode.java | 2 + .../com/seong/shoutlink/domain/hub/Hub.java | 5 + .../shoutlink/domain/hub/HubWithMaster.java | 25 +++++ .../shoutlink/domain/hub/HubWithMembers.java | 16 ---- .../hub/repository/HubRepositoryImpl.java | 16 +++- .../domain/hub/service/HubRepository.java | 7 +- .../domain/hub/service/HubService.java | 6 +- .../hub/service/event/CreateHubEvent.java | 2 +- .../repository/LinkBundleRepositoryImpl.java | 6 ++ .../service/LinkBundleRepository.java | 3 + .../linkbundle/service/LinkBundleService.java | 21 +++++ .../request/CreateHubLinkBundleCommand.java | 9 ++ .../seong/shoutlink/domain/member/Member.java | 4 + .../repository/FakeLinkBundleRepository.java | 6 ++ .../service/LinkBundleServiceTest.java | 92 +++++++++++++++++-- .../seong/shoutlink/fixture/HubFixture.java | 21 +++++ .../shoutlink/fixture/StubHubRepository.java | 23 ++++- 17 files changed, 228 insertions(+), 36 deletions(-) create mode 100644 src/main/java/com/seong/shoutlink/domain/hub/HubWithMaster.java delete mode 100644 src/main/java/com/seong/shoutlink/domain/hub/HubWithMembers.java create mode 100644 src/main/java/com/seong/shoutlink/domain/linkbundle/service/request/CreateHubLinkBundleCommand.java create mode 100644 src/test/java/com/seong/shoutlink/fixture/HubFixture.java diff --git a/src/main/java/com/seong/shoutlink/domain/exception/ErrorCode.java b/src/main/java/com/seong/shoutlink/domain/exception/ErrorCode.java index 49c7771..ae0c0f6 100644 --- a/src/main/java/com/seong/shoutlink/domain/exception/ErrorCode.java +++ b/src/main/java/com/seong/shoutlink/domain/exception/ErrorCode.java @@ -10,6 +10,7 @@ public enum ErrorCode { UNAUTHENTICATED("SL101", Constants.UNAUTHORIZED), INVALID_ACCESS_TOKEN("SL102", Constants.UNAUTHORIZED), EXPIRED_ACCESS_TOKEN("SL103", Constants.UNAUTHORIZED), + UNAUTHORIZED("SL301", Constants.FORBIDDEN), NOT_FOUND("SL401", Constants.NOT_FOUND), DUPLICATE_EMAIL("SL901", Constants.CONFLICT), DUPLICATE_NICKNAME("SL902", Constants.BAD_REQUEST); @@ -21,6 +22,7 @@ private static class Constants { private static final int BAD_REQUEST = 400; private static final int UNAUTHORIZED = 401; + private static final int FORBIDDEN = 401; private static final int NOT_FOUND = 404; private static final int CONFLICT = 409; } 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 cdcba1d..f79925b 100644 --- a/src/main/java/com/seong/shoutlink/domain/hub/Hub.java +++ b/src/main/java/com/seong/shoutlink/domain/hub/Hub.java @@ -19,6 +19,11 @@ public class Hub { private boolean isPrivate; public Hub(String name, String description, boolean isPrivate) { + this(null, name, description, isPrivate); + } + + public Hub(Long hubId, String name, String description, boolean isPrivate) { + this.hubId = hubId; this.name = validateName(name); this.description = validateDescription(description); this.isPrivate = isPrivate; diff --git a/src/main/java/com/seong/shoutlink/domain/hub/HubWithMaster.java b/src/main/java/com/seong/shoutlink/domain/hub/HubWithMaster.java new file mode 100644 index 0000000..82f754c --- /dev/null +++ b/src/main/java/com/seong/shoutlink/domain/hub/HubWithMaster.java @@ -0,0 +1,25 @@ +package com.seong.shoutlink.domain.hub; + +import com.seong.shoutlink.domain.exception.ErrorCode; +import com.seong.shoutlink.domain.exception.ShoutLinkException; +import com.seong.shoutlink.domain.member.Member; +import lombok.Getter; + +@Getter +public class HubWithMaster { + + private final Hub hub; + private final Member member; + + public HubWithMaster(Hub hub, Member member) { + this.hub = hub; + this.member = member; + } + + public void checkMasterAuthority(Long memberId) { + if(member.isEqualToMemberId(memberId)) { + return; + } + throw new ShoutLinkException("권한이 없습니다.", ErrorCode.UNAUTHORIZED); + } +} diff --git a/src/main/java/com/seong/shoutlink/domain/hub/HubWithMembers.java b/src/main/java/com/seong/shoutlink/domain/hub/HubWithMembers.java deleted file mode 100644 index cab80fc..0000000 --- a/src/main/java/com/seong/shoutlink/domain/hub/HubWithMembers.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.seong.shoutlink.domain.hub; - -import com.seong.shoutlink.domain.member.Member; -import lombok.Getter; - -@Getter -public class HubWithMembers { - - private final Hub hub; - private final Member member; - - public HubWithMembers(Hub hub, Member member) { - this.hub = hub; - this.member = member; - } -} diff --git a/src/main/java/com/seong/shoutlink/domain/hub/repository/HubRepositoryImpl.java b/src/main/java/com/seong/shoutlink/domain/hub/repository/HubRepositoryImpl.java index 1125f8d..1d327ed 100644 --- a/src/main/java/com/seong/shoutlink/domain/hub/repository/HubRepositoryImpl.java +++ b/src/main/java/com/seong/shoutlink/domain/hub/repository/HubRepositoryImpl.java @@ -1,11 +1,13 @@ package com.seong.shoutlink.domain.hub.repository; -import com.seong.shoutlink.domain.hub.HubWithMembers; +import com.seong.shoutlink.domain.hub.HubWithMaster; import com.seong.shoutlink.domain.hub.service.HubRepository; import com.seong.shoutlink.domain.hubMember.repository.HubMemberEntity; import com.seong.shoutlink.domain.hubMember.repository.HubMemberJpaRepository; +import java.util.Optional; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; @Repository @RequiredArgsConstructor @@ -15,12 +17,18 @@ public class HubRepositoryImpl implements HubRepository { private final HubMemberJpaRepository hubMemberJpaRepository; @Override - public Long save(HubWithMembers hubWithMembers) { - HubEntity hubEntity = HubEntity.create(hubWithMembers.getHub()); + @Transactional + public Long save(HubWithMaster hubWithMaster) { + HubEntity hubEntity = HubEntity.create(hubWithMaster.getHub()); hubJpaRepository.save(hubEntity); HubMemberEntity hubMemberEntity - = HubMemberEntity.create(hubWithMembers.getHub(), hubWithMembers.getMember()); + = HubMemberEntity.create(hubWithMaster.getHub(), hubWithMaster.getMember()); hubMemberJpaRepository.save(hubMemberEntity); return hubEntity.getHubId(); } + + @Override + public Optional findByIdWithHubMaster(Long hubId) { + return Optional.empty(); + } } diff --git a/src/main/java/com/seong/shoutlink/domain/hub/service/HubRepository.java b/src/main/java/com/seong/shoutlink/domain/hub/service/HubRepository.java index ac8ca70..86cff22 100644 --- a/src/main/java/com/seong/shoutlink/domain/hub/service/HubRepository.java +++ b/src/main/java/com/seong/shoutlink/domain/hub/service/HubRepository.java @@ -1,8 +1,11 @@ package com.seong.shoutlink.domain.hub.service; -import com.seong.shoutlink.domain.hub.HubWithMembers; +import com.seong.shoutlink.domain.hub.HubWithMaster; +import java.util.Optional; public interface HubRepository { - Long save(HubWithMembers hubWithMembers); + Long save(HubWithMaster hubWithMaster); + + Optional findByIdWithHubMaster(Long hubId); } diff --git a/src/main/java/com/seong/shoutlink/domain/hub/service/HubService.java b/src/main/java/com/seong/shoutlink/domain/hub/service/HubService.java index 33504aa..7730eab 100644 --- a/src/main/java/com/seong/shoutlink/domain/hub/service/HubService.java +++ b/src/main/java/com/seong/shoutlink/domain/hub/service/HubService.java @@ -4,7 +4,7 @@ import com.seong.shoutlink.domain.exception.ErrorCode; import com.seong.shoutlink.domain.exception.ShoutLinkException; import com.seong.shoutlink.domain.hub.Hub; -import com.seong.shoutlink.domain.hub.HubWithMembers; +import com.seong.shoutlink.domain.hub.HubWithMaster; import com.seong.shoutlink.domain.hub.service.event.CreateHubEvent; import com.seong.shoutlink.domain.hub.service.request.CreateHubCommand; import com.seong.shoutlink.domain.hub.service.response.CreateHubResponse; @@ -24,8 +24,8 @@ public class HubService { public CreateHubResponse createHub(CreateHubCommand command) { Member member = getMember(command.memberId()); Hub hub = new Hub(command.name(), command.description(), command.isPrivate()); - Long hubId = hubRepository.save(new HubWithMembers(hub, member)); - eventPublisher.publishEvent(new CreateHubEvent(hubId)); + Long hubId = hubRepository.save(new HubWithMaster(hub, member)); + eventPublisher.publishEvent(new CreateHubEvent(hubId, member.getMemberId())); return new CreateHubResponse(hubId); } diff --git a/src/main/java/com/seong/shoutlink/domain/hub/service/event/CreateHubEvent.java b/src/main/java/com/seong/shoutlink/domain/hub/service/event/CreateHubEvent.java index 296152d..6664f12 100644 --- a/src/main/java/com/seong/shoutlink/domain/hub/service/event/CreateHubEvent.java +++ b/src/main/java/com/seong/shoutlink/domain/hub/service/event/CreateHubEvent.java @@ -2,6 +2,6 @@ import com.seong.shoutlink.domain.common.Event; -public record CreateHubEvent(Long hubId) implements Event { +public record CreateHubEvent(Long hubId, Long memberId) implements Event { } diff --git a/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/LinkBundleRepositoryImpl.java b/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/LinkBundleRepositoryImpl.java index 5fd44bd..7fdb2ec 100644 --- a/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/LinkBundleRepositoryImpl.java +++ b/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/LinkBundleRepositoryImpl.java @@ -1,5 +1,6 @@ package com.seong.shoutlink.domain.linkbundle.repository; +import com.seong.shoutlink.domain.linkbundle.HubLinkBundle; import com.seong.shoutlink.domain.linkbundle.LinkBundle; import com.seong.shoutlink.domain.linkbundle.MemberLinkBundle; import com.seong.shoutlink.domain.linkbundle.service.LinkBundleRepository; @@ -40,4 +41,9 @@ public List findLinkBundlesThatMembersHave(Member member) { .map(LinkBundleEntity::toDomain) .toList(); } + + @Override + public Long save(HubLinkBundle hubLinkBundle) { + return null; + } } diff --git a/src/main/java/com/seong/shoutlink/domain/linkbundle/service/LinkBundleRepository.java b/src/main/java/com/seong/shoutlink/domain/linkbundle/service/LinkBundleRepository.java index 5b7fa3d..f71c867 100644 --- a/src/main/java/com/seong/shoutlink/domain/linkbundle/service/LinkBundleRepository.java +++ b/src/main/java/com/seong/shoutlink/domain/linkbundle/service/LinkBundleRepository.java @@ -1,5 +1,6 @@ package com.seong.shoutlink.domain.linkbundle.service; +import com.seong.shoutlink.domain.linkbundle.HubLinkBundle; import com.seong.shoutlink.domain.linkbundle.LinkBundle; import com.seong.shoutlink.domain.linkbundle.MemberLinkBundle; import com.seong.shoutlink.domain.member.Member; @@ -15,4 +16,6 @@ public interface LinkBundleRepository { Optional findById(Long linkBundleId); List findLinkBundlesThatMembersHave(Member member); + + Long save(HubLinkBundle hubLinkBundle); } 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 4e37666..dcdc8f7 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 @@ -2,8 +2,12 @@ import com.seong.shoutlink.domain.exception.ErrorCode; import com.seong.shoutlink.domain.exception.ShoutLinkException; +import com.seong.shoutlink.domain.hub.HubWithMaster; +import com.seong.shoutlink.domain.hub.service.HubRepository; +import com.seong.shoutlink.domain.linkbundle.HubLinkBundle; import com.seong.shoutlink.domain.linkbundle.LinkBundle; import com.seong.shoutlink.domain.linkbundle.MemberLinkBundle; +import com.seong.shoutlink.domain.linkbundle.service.request.CreateHubLinkBundleCommand; import com.seong.shoutlink.domain.linkbundle.service.request.FindLinkBundlesCommand; import com.seong.shoutlink.domain.linkbundle.service.response.CreateLinkBundleCommand; import com.seong.shoutlink.domain.linkbundle.service.response.CreateLinkBundleResponse; @@ -20,6 +24,7 @@ public class LinkBundleService { private final MemberRepository memberRepository; + private final HubRepository hubRepository; private final LinkBundleRepository linkBundleRepository; @Transactional @@ -46,4 +51,20 @@ private Member getMember(Long memberId) { return memberRepository.findById(memberId) .orElseThrow(() -> new ShoutLinkException("존재하지 않는 사용자입니다.", ErrorCode.NOT_FOUND)); } + + @Transactional + public CreateLinkBundleResponse createHubLinkBundle(CreateHubLinkBundleCommand command) { + HubWithMaster hubWithMaster = getHubWithMaster(command.hubId()); + hubWithMaster.checkMasterAuthority(command.memberId()); + LinkBundle linkBundle = new LinkBundle( + command.description(), + command.isDefault()); + HubLinkBundle hubLinkBundle = new HubLinkBundle(hubWithMaster.getHub(), linkBundle); + return new CreateLinkBundleResponse(linkBundleRepository.save(hubLinkBundle)); + } + + private HubWithMaster getHubWithMaster(Long hubId) { + return hubRepository.findByIdWithHubMaster(hubId) + .orElseThrow(() -> new ShoutLinkException("존재하지 않는 허브입니다.", ErrorCode.NOT_FOUND)); + } } diff --git a/src/main/java/com/seong/shoutlink/domain/linkbundle/service/request/CreateHubLinkBundleCommand.java b/src/main/java/com/seong/shoutlink/domain/linkbundle/service/request/CreateHubLinkBundleCommand.java new file mode 100644 index 0000000..32ba7e8 --- /dev/null +++ b/src/main/java/com/seong/shoutlink/domain/linkbundle/service/request/CreateHubLinkBundleCommand.java @@ -0,0 +1,9 @@ +package com.seong.shoutlink.domain.linkbundle.service.request; + +public record CreateHubLinkBundleCommand( + Long hubId, + Long memberId, + String description, + boolean isDefault) { + +} diff --git a/src/main/java/com/seong/shoutlink/domain/member/Member.java b/src/main/java/com/seong/shoutlink/domain/member/Member.java index 92c2374..04f339f 100644 --- a/src/main/java/com/seong/shoutlink/domain/member/Member.java +++ b/src/main/java/com/seong/shoutlink/domain/member/Member.java @@ -69,4 +69,8 @@ private void validateMemberRole(MemberRole memberRole) { throw new ShoutLinkException("회원 역할은 필수입니다.", ErrorCode.ILLEGAL_ARGUMENT); } } + + public boolean isEqualToMemberId(Long memberId) { + return Objects.equals(this.memberId, memberId); + } } diff --git a/src/test/java/com/seong/shoutlink/domain/linkbundle/repository/FakeLinkBundleRepository.java b/src/test/java/com/seong/shoutlink/domain/linkbundle/repository/FakeLinkBundleRepository.java index 750a3c2..bf0fa6c 100644 --- a/src/test/java/com/seong/shoutlink/domain/linkbundle/repository/FakeLinkBundleRepository.java +++ b/src/test/java/com/seong/shoutlink/domain/linkbundle/repository/FakeLinkBundleRepository.java @@ -1,5 +1,6 @@ package com.seong.shoutlink.domain.linkbundle.repository; +import com.seong.shoutlink.domain.linkbundle.HubLinkBundle; import com.seong.shoutlink.domain.linkbundle.LinkBundle; import com.seong.shoutlink.domain.linkbundle.MemberLinkBundle; import com.seong.shoutlink.domain.linkbundle.service.LinkBundleRepository; @@ -55,6 +56,11 @@ public List findLinkBundlesThatMembersHave(Member member) { return memory.values().stream().toList(); } + @Override + public Long save(HubLinkBundle hubLinkBundle) { + return 1L; + } + private long getNextId() { return memory.keySet().size() + 1; } 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 37a54f9..378daf2 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 @@ -5,8 +5,10 @@ import com.seong.shoutlink.domain.exception.ErrorCode; import com.seong.shoutlink.domain.exception.ShoutLinkException; +import com.seong.shoutlink.domain.hub.HubWithMaster; import com.seong.shoutlink.domain.linkbundle.LinkBundle; import com.seong.shoutlink.domain.linkbundle.repository.FakeLinkBundleRepository; +import com.seong.shoutlink.domain.linkbundle.service.request.CreateHubLinkBundleCommand; import com.seong.shoutlink.domain.linkbundle.service.request.FindLinkBundlesCommand; import com.seong.shoutlink.domain.linkbundle.service.response.CreateLinkBundleCommand; import com.seong.shoutlink.domain.linkbundle.service.response.CreateLinkBundleResponse; @@ -14,6 +16,7 @@ import com.seong.shoutlink.domain.linkbundle.service.response.FindLinkBundlesResponse; import com.seong.shoutlink.domain.member.Member; import com.seong.shoutlink.domain.member.repository.StubMemberRepository; +import com.seong.shoutlink.fixture.HubFixture; import com.seong.shoutlink.fixture.MemberFixture; import com.seong.shoutlink.fixture.StubHubRepository; import java.util.List; @@ -24,24 +27,24 @@ class LinkBundleServiceTest { + private LinkBundleService linkBundleService; private StubMemberRepository memberRepository; private FakeLinkBundleRepository linkBundleRepository; - private StubHubRepository stubHubRepository; + private StubHubRepository hubRepository; @Nested @DisplayName("createLinkBundle 메서드 호출 시") class CreateLinkBundleTest { private Member savedMember; - private LinkBundleService linkBundleService; @BeforeEach void setUp() { savedMember = MemberFixture.member(); memberRepository = new StubMemberRepository(savedMember); linkBundleRepository = new FakeLinkBundleRepository(); - stubHubRepository = new StubHubRepository(); - linkBundleService = new LinkBundleService(memberRepository, stubHubRepository, + hubRepository = new StubHubRepository(); + linkBundleService = new LinkBundleService(memberRepository, hubRepository, linkBundleRepository); } @@ -92,8 +95,8 @@ class FindLinkBundlesTest { void setUp() { memberRepository = new StubMemberRepository(); linkBundleRepository = new FakeLinkBundleRepository(); - stubHubRepository = new StubHubRepository(); - linkBundleService = new LinkBundleService(memberRepository, stubHubRepository, + hubRepository = new StubHubRepository(); + linkBundleService = new LinkBundleService(memberRepository, hubRepository, linkBundleRepository); } @@ -122,4 +125,81 @@ void findLinkBundles() { .isEqualTo(new FindLinkBundleResponse(2L, "두번째", false)); } } + + @Nested + @DisplayName("createHubLinkBundle 메서드 호출 시") + class CreateHubLinkBundleTest { + + @BeforeEach + void setUp() { + memberRepository = new StubMemberRepository(); + hubRepository = new StubHubRepository(); + linkBundleRepository = new FakeLinkBundleRepository(); + linkBundleService = new LinkBundleService(memberRepository, hubRepository, + linkBundleRepository); + } + + @Test + @DisplayName("성공: 허브 링크 번들 생성됨") + void createHubLinkBundle() { + //given + Member member = MemberFixture.member(); + HubWithMaster hubWithMaster = HubFixture.hubWithMaster(member); + memberRepository.stub(member); + hubRepository.stub(hubWithMaster); + CreateHubLinkBundleCommand command = new CreateHubLinkBundleCommand( + hubWithMaster.getHub().getHubId(), + member.getMemberId(), + "테스트 허브", + false); + + //when + CreateLinkBundleResponse response = linkBundleService.createHubLinkBundle(command); + + //then + assertThat(response.linkBundleId()).isEqualTo(1L); + } + + @Test + @DisplayName("예외(NotFound): 존재하지 않는 허브") + void notFound_WhenHubNotFound() { + //given + CreateHubLinkBundleCommand command = new CreateHubLinkBundleCommand(1L, 1L, "허브", + false); + + //when + Exception exception = catchException( + () -> linkBundleService.createHubLinkBundle(command)); + + //then + assertThat(exception).isInstanceOf(ShoutLinkException.class) + .extracting(e -> ((ShoutLinkException) e).getErrorCode()) + .isEqualTo(ErrorCode.NOT_FOUND); + } + + @Test + @DisplayName("예외(Unauthorized): 링크 묶음 생성 권한 없음") + void unauthorized_WhenMemberIsNotHubMaster() { + //given + Member member = MemberFixture.member(); + HubWithMaster hubWithMaster = HubFixture.hubWithMaster(member); + memberRepository.stub(member); + hubRepository.stub(hubWithMaster); + long unknownMemberId = member.getMemberId() + 1; + CreateHubLinkBundleCommand command = new CreateHubLinkBundleCommand( + hubWithMaster.getHub().getHubId(), + unknownMemberId, + "허브", + false); + + //when + Exception exception = catchException( + () -> linkBundleService.createHubLinkBundle(command)); + + //then + assertThat(exception).isInstanceOf(ShoutLinkException.class) + .extracting(e -> ((ShoutLinkException) e).getErrorCode()) + .isEqualTo(ErrorCode.UNAUTHORIZED); + } + } } diff --git a/src/test/java/com/seong/shoutlink/fixture/HubFixture.java b/src/test/java/com/seong/shoutlink/fixture/HubFixture.java new file mode 100644 index 0000000..e938bce --- /dev/null +++ b/src/test/java/com/seong/shoutlink/fixture/HubFixture.java @@ -0,0 +1,21 @@ +package com.seong.shoutlink.fixture; + +import com.seong.shoutlink.domain.hub.Hub; +import com.seong.shoutlink.domain.hub.HubWithMaster; +import com.seong.shoutlink.domain.member.Member; + +public final class HubFixture { + + public static final long HUB_ID = 1L; + public static final String NAME = "테스트 허브"; + public static final String DESCRIPTION = "허브 설명"; + public static final boolean IS_PRIVATE = false; + + public static Hub hub() { + return new Hub(HUB_ID, NAME, DESCRIPTION, IS_PRIVATE); + } + + public static HubWithMaster hubWithMaster(Member member) { + return new HubWithMaster(hub(), member); + } +} diff --git a/src/test/java/com/seong/shoutlink/fixture/StubHubRepository.java b/src/test/java/com/seong/shoutlink/fixture/StubHubRepository.java index e165eaf..774ab57 100644 --- a/src/test/java/com/seong/shoutlink/fixture/StubHubRepository.java +++ b/src/test/java/com/seong/shoutlink/fixture/StubHubRepository.java @@ -1,17 +1,32 @@ package com.seong.shoutlink.fixture; -import com.seong.shoutlink.domain.hub.Hub; -import com.seong.shoutlink.domain.hub.HubWithMembers; +import com.seong.shoutlink.domain.hub.HubWithMaster; import com.seong.shoutlink.domain.hub.service.HubRepository; import java.util.HashMap; import java.util.Map; +import java.util.Optional; public class StubHubRepository implements HubRepository { - private final Map memory = new HashMap<>(); + private final Map memory = new HashMap<>(); + + public void stub(HubWithMaster... hubWithMasters) { + for (HubWithMaster hubWithMaster : hubWithMasters) { + memory.put(getNextKey(), hubWithMaster); + } + } + + private long getNextKey() { + return memory.size() + 1; + } @Override - public Long save(HubWithMembers hubWithMembers) { + public Long save(HubWithMaster hubWithMaster) { return 1L; } + + @Override + public Optional findByIdWithHubMaster(Long hubId) { + return memory.values().stream().findFirst(); + } } From 8f8578f5ecb14827c67cde800837142206888aaf Mon Sep 17 00:00:00 2001 From: hseong3243 Date: Tue, 13 Feb 2024 00:08:25 +0900 Subject: [PATCH 04/10] =?UTF-8?q?feat:=20`Hub`=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=EC=97=90=20=ED=97=88=EB=B8=8C=EB=A5=BC=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=ED=95=9C=20=EC=82=AC=EC=9A=A9=EC=9E=90=20ID=EB=A5=BC?= =?UTF-8?q?=20=EC=9D=B8=EC=8A=A4=ED=84=B4=EC=8A=A4=20=EB=B3=80=EC=88=98?= =?UTF-8?q?=EB=A1=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/seong/shoutlink/domain/hub/Hub.java | 16 +++++++++++++--- .../hub/repository/HubRepositoryImpl.java | 9 +++++---- .../domain/hub/service/HubRepository.java | 3 ++- .../domain/hub/service/HubService.java | 7 +++---- .../hubMember/repository/HubMemberEntity.java | 19 +++++++++++-------- .../repository/HubMemberJpaRepository.java | 8 ++++++++ .../global/event/LinkBundleEventListener.java | 13 ++++++++++++- .../seong/shoutlink/fixture/HubFixture.java | 14 +++++++------- .../shoutlink/fixture/StubHubRepository.java | 3 ++- 9 files changed, 63 insertions(+), 29 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 f79925b..afc850f 100644 --- a/src/main/java/com/seong/shoutlink/domain/hub/Hub.java +++ b/src/main/java/com/seong/shoutlink/domain/hub/Hub.java @@ -2,6 +2,7 @@ import com.seong.shoutlink.domain.exception.ErrorCode; import com.seong.shoutlink.domain.exception.ShoutLinkException; +import com.seong.shoutlink.domain.member.Member; import java.text.MessageFormat; import java.util.Objects; import lombok.Getter; @@ -14,16 +15,18 @@ public class Hub { private static final int DESCRIPTION_MAX_SIZE = 200; private Long hubId; + private Long masterId; private String name; private String description; private boolean isPrivate; - public Hub(String name, String description, boolean isPrivate) { - this(null, name, description, isPrivate); + public Hub(Member master, String name, String description, boolean isPrivate) { + this(null, master.getMemberId(), name, description, isPrivate); } - public Hub(Long hubId, String name, String description, boolean isPrivate) { + public Hub(Long hubId, Long memberId, String name, String description, boolean isPrivate) { this.hubId = hubId; + this.masterId = memberId; this.name = validateName(name); this.description = validateDescription(description); this.isPrivate = isPrivate; @@ -52,4 +55,11 @@ private String validateDescription(String description) { } return description; } + + public void checkMasterAuthority(Long memberId) { + if(Objects.equals(masterId, memberId)) { + return; + } + throw new ShoutLinkException("권한이 없습니다.", ErrorCode.UNAUTHORIZED); + } } diff --git a/src/main/java/com/seong/shoutlink/domain/hub/repository/HubRepositoryImpl.java b/src/main/java/com/seong/shoutlink/domain/hub/repository/HubRepositoryImpl.java index 1d327ed..1af8866 100644 --- a/src/main/java/com/seong/shoutlink/domain/hub/repository/HubRepositoryImpl.java +++ b/src/main/java/com/seong/shoutlink/domain/hub/repository/HubRepositoryImpl.java @@ -1,5 +1,6 @@ package com.seong.shoutlink.domain.hub.repository; +import com.seong.shoutlink.domain.hub.Hub; import com.seong.shoutlink.domain.hub.HubWithMaster; import com.seong.shoutlink.domain.hub.service.HubRepository; import com.seong.shoutlink.domain.hubMember.repository.HubMemberEntity; @@ -18,17 +19,17 @@ public class HubRepositoryImpl implements HubRepository { @Override @Transactional - public Long save(HubWithMaster hubWithMaster) { - HubEntity hubEntity = HubEntity.create(hubWithMaster.getHub()); + public Long save(Hub hub) { + HubEntity hubEntity = HubEntity.create(hub); hubJpaRepository.save(hubEntity); HubMemberEntity hubMemberEntity - = HubMemberEntity.create(hubWithMaster.getHub(), hubWithMaster.getMember()); + = HubMemberEntity.create(hubEntity, hub.getMasterId()); hubMemberJpaRepository.save(hubMemberEntity); return hubEntity.getHubId(); } @Override public Optional findByIdWithHubMaster(Long hubId) { - return Optional.empty(); + return null; } } diff --git a/src/main/java/com/seong/shoutlink/domain/hub/service/HubRepository.java b/src/main/java/com/seong/shoutlink/domain/hub/service/HubRepository.java index 86cff22..d087071 100644 --- a/src/main/java/com/seong/shoutlink/domain/hub/service/HubRepository.java +++ b/src/main/java/com/seong/shoutlink/domain/hub/service/HubRepository.java @@ -1,11 +1,12 @@ package com.seong.shoutlink.domain.hub.service; +import com.seong.shoutlink.domain.hub.Hub; import com.seong.shoutlink.domain.hub.HubWithMaster; import java.util.Optional; public interface HubRepository { - Long save(HubWithMaster hubWithMaster); + Long save(Hub hub); Optional findByIdWithHubMaster(Long hubId); } diff --git a/src/main/java/com/seong/shoutlink/domain/hub/service/HubService.java b/src/main/java/com/seong/shoutlink/domain/hub/service/HubService.java index 7730eab..447ba5b 100644 --- a/src/main/java/com/seong/shoutlink/domain/hub/service/HubService.java +++ b/src/main/java/com/seong/shoutlink/domain/hub/service/HubService.java @@ -4,7 +4,6 @@ import com.seong.shoutlink.domain.exception.ErrorCode; import com.seong.shoutlink.domain.exception.ShoutLinkException; import com.seong.shoutlink.domain.hub.Hub; -import com.seong.shoutlink.domain.hub.HubWithMaster; import com.seong.shoutlink.domain.hub.service.event.CreateHubEvent; import com.seong.shoutlink.domain.hub.service.request.CreateHubCommand; import com.seong.shoutlink.domain.hub.service.response.CreateHubResponse; @@ -23,9 +22,9 @@ public class HubService { public CreateHubResponse createHub(CreateHubCommand command) { Member member = getMember(command.memberId()); - Hub hub = new Hub(command.name(), command.description(), command.isPrivate()); - Long hubId = hubRepository.save(new HubWithMaster(hub, member)); - eventPublisher.publishEvent(new CreateHubEvent(hubId, member.getMemberId())); + Hub hub = new Hub(member, command.name(), command.description(), command.isPrivate()); + Long hubId = hubRepository.save(hub); + eventPublisher.publishEvent(new CreateHubEvent(hubId, hub.getMasterId())); return new CreateHubResponse(hubId); } diff --git a/src/main/java/com/seong/shoutlink/domain/hubMember/repository/HubMemberEntity.java b/src/main/java/com/seong/shoutlink/domain/hubMember/repository/HubMemberEntity.java index d23ca9a..a11639e 100644 --- a/src/main/java/com/seong/shoutlink/domain/hubMember/repository/HubMemberEntity.java +++ b/src/main/java/com/seong/shoutlink/domain/hubMember/repository/HubMemberEntity.java @@ -1,15 +1,17 @@ package com.seong.shoutlink.domain.hubMember.repository; -import com.seong.shoutlink.domain.hub.Hub; +import com.seong.shoutlink.domain.hub.repository.HubEntity; import com.seong.shoutlink.domain.hubMember.HubMemberRole; -import com.seong.shoutlink.domain.member.Member; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -26,20 +28,21 @@ public class HubMemberEntity { @Column(nullable = false) private Long memberId; - @Column(nullable = false) - private Long hubId; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "hub_id", nullable = false, updatable = false) + private HubEntity hubEntity; @Column(nullable = false) @Enumerated(EnumType.STRING) private HubMemberRole hubMemberRole; - public HubMemberEntity(Long memberId, Long hubId, HubMemberRole hubMemberRole) { + public HubMemberEntity(Long memberId, HubEntity hubEntity, HubMemberRole hubMemberRole) { this.memberId = memberId; - this.hubId = hubId; + this.hubEntity = hubEntity; this.hubMemberRole = hubMemberRole; } - public static HubMemberEntity create(Hub hub, Member member) { - return new HubMemberEntity(member.getMemberId(), hub.getHubId(), HubMemberRole.MASTER); + public static HubMemberEntity create(HubEntity hubEntity, Long masterId) { + return new HubMemberEntity(masterId, hubEntity, HubMemberRole.MASTER); } } diff --git a/src/main/java/com/seong/shoutlink/domain/hubMember/repository/HubMemberJpaRepository.java b/src/main/java/com/seong/shoutlink/domain/hubMember/repository/HubMemberJpaRepository.java index d1c7b86..2f28ebb 100644 --- a/src/main/java/com/seong/shoutlink/domain/hubMember/repository/HubMemberJpaRepository.java +++ b/src/main/java/com/seong/shoutlink/domain/hubMember/repository/HubMemberJpaRepository.java @@ -1,7 +1,15 @@ package com.seong.shoutlink.domain.hubMember.repository; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; public interface HubMemberJpaRepository extends JpaRepository { + @Query("select hm from HubMemberEntity hm " + + "join hm.hubEntity h " + + "where h.hubId = :hubId " + + "and hm.hubMemberRole = com.seong.shoutlink.domain.hubMember.HubMemberRole.MASTER") + Optional findHubMasterByHubIdWithHub(@Param("hubId") Long hubId); } diff --git a/src/main/java/com/seong/shoutlink/global/event/LinkBundleEventListener.java b/src/main/java/com/seong/shoutlink/global/event/LinkBundleEventListener.java index 01744f9..977844c 100644 --- a/src/main/java/com/seong/shoutlink/global/event/LinkBundleEventListener.java +++ b/src/main/java/com/seong/shoutlink/global/event/LinkBundleEventListener.java @@ -1,7 +1,9 @@ package com.seong.shoutlink.global.event; import com.seong.shoutlink.domain.auth.service.event.CreateMemberEvent; +import com.seong.shoutlink.domain.hub.service.event.CreateHubEvent; import com.seong.shoutlink.domain.linkbundle.service.LinkBundleService; +import com.seong.shoutlink.domain.linkbundle.service.request.CreateHubLinkBundleCommand; import com.seong.shoutlink.domain.linkbundle.service.response.CreateLinkBundleCommand; import lombok.RequiredArgsConstructor; import org.springframework.transaction.annotation.Propagation; @@ -23,5 +25,14 @@ public void createDefaultLinkBundle(CreateMemberEvent event) { linkBundleService.createLinkBundle(command); } - + @TransactionalEventListener + @Transactional(propagation = Propagation.REQUIRES_NEW) + public void createDefaultHubLinkBundle(CreateHubEvent event) { + CreateHubLinkBundleCommand command = new CreateHubLinkBundleCommand( + event.hubId(), + event.memberId(), + DEFAULT_LINK_BUNDLE, + true); + linkBundleService.createHubLinkBundle(command); + } } diff --git a/src/test/java/com/seong/shoutlink/fixture/HubFixture.java b/src/test/java/com/seong/shoutlink/fixture/HubFixture.java index e938bce..cf68995 100644 --- a/src/test/java/com/seong/shoutlink/fixture/HubFixture.java +++ b/src/test/java/com/seong/shoutlink/fixture/HubFixture.java @@ -6,16 +6,16 @@ public final class HubFixture { - public static final long HUB_ID = 1L; - public static final String NAME = "테스트 허브"; - public static final String DESCRIPTION = "허브 설명"; - public static final boolean IS_PRIVATE = false; + private static final long HUB_ID = 1L; + private static final String NAME = "테스트 허브"; + private static final String DESCRIPTION = "허브 설명"; + private static final boolean IS_PRIVATE = false; - public static Hub hub() { - return new Hub(HUB_ID, NAME, DESCRIPTION, IS_PRIVATE); + public static Hub hub(Member member) { + return new Hub(HUB_ID, member.getMemberId(), NAME, DESCRIPTION, IS_PRIVATE); } public static HubWithMaster hubWithMaster(Member member) { - return new HubWithMaster(hub(), member); + return new HubWithMaster(hub(member), member); } } diff --git a/src/test/java/com/seong/shoutlink/fixture/StubHubRepository.java b/src/test/java/com/seong/shoutlink/fixture/StubHubRepository.java index 774ab57..860bbaf 100644 --- a/src/test/java/com/seong/shoutlink/fixture/StubHubRepository.java +++ b/src/test/java/com/seong/shoutlink/fixture/StubHubRepository.java @@ -1,5 +1,6 @@ package com.seong.shoutlink.fixture; +import com.seong.shoutlink.domain.hub.Hub; import com.seong.shoutlink.domain.hub.HubWithMaster; import com.seong.shoutlink.domain.hub.service.HubRepository; import java.util.HashMap; @@ -21,7 +22,7 @@ private long getNextKey() { } @Override - public Long save(HubWithMaster hubWithMaster) { + public Long save(Hub hub) { return 1L; } From 0df64c28990565a9de519170892186cb3d07cf5d Mon Sep 17 00:00:00 2001 From: hseong3243 Date: Wed, 14 Feb 2024 21:24:41 +0900 Subject: [PATCH 05/10] =?UTF-8?q?feat:=20=ED=97=88=EB=B8=8C=20=EB=A7=81?= =?UTF-8?q?=ED=81=AC=20=EB=AC=B6=EC=9D=8C=20=EC=84=9C=EB=B9=84=EC=8A=A4=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=EC=97=90=20`Hub`=EC=97=94=ED=8B=B0=ED=8B=B0?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=EC=82=AC=ED=95=AD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/hub/repository/HubRepositoryImpl.java | 6 +++--- .../domain/hub/service/HubRepository.java | 3 +-- .../hubMember/repository/HubMemberEntity.java | 10 ++++++++++ .../linkbundle/service/LinkBundleService.java | 12 ++++++------ .../linkbundle/service/LinkBundleServiceTest.java | 14 +++++++------- .../seong/shoutlink/fixture/StubHubRepository.java | 11 +++++------ 6 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src/main/java/com/seong/shoutlink/domain/hub/repository/HubRepositoryImpl.java b/src/main/java/com/seong/shoutlink/domain/hub/repository/HubRepositoryImpl.java index 1af8866..43dd7a5 100644 --- a/src/main/java/com/seong/shoutlink/domain/hub/repository/HubRepositoryImpl.java +++ b/src/main/java/com/seong/shoutlink/domain/hub/repository/HubRepositoryImpl.java @@ -1,7 +1,6 @@ package com.seong.shoutlink.domain.hub.repository; import com.seong.shoutlink.domain.hub.Hub; -import com.seong.shoutlink.domain.hub.HubWithMaster; import com.seong.shoutlink.domain.hub.service.HubRepository; import com.seong.shoutlink.domain.hubMember.repository.HubMemberEntity; import com.seong.shoutlink.domain.hubMember.repository.HubMemberJpaRepository; @@ -29,7 +28,8 @@ public Long save(Hub hub) { } @Override - public Optional findByIdWithHubMaster(Long hubId) { - return null; + public Optional findById(Long hubId) { + return hubMemberJpaRepository.findHubMasterByHubIdWithHub(hubId) + .map(HubMemberEntity::toHub); } } diff --git a/src/main/java/com/seong/shoutlink/domain/hub/service/HubRepository.java b/src/main/java/com/seong/shoutlink/domain/hub/service/HubRepository.java index d087071..b3f2f7f 100644 --- a/src/main/java/com/seong/shoutlink/domain/hub/service/HubRepository.java +++ b/src/main/java/com/seong/shoutlink/domain/hub/service/HubRepository.java @@ -1,12 +1,11 @@ package com.seong.shoutlink.domain.hub.service; import com.seong.shoutlink.domain.hub.Hub; -import com.seong.shoutlink.domain.hub.HubWithMaster; import java.util.Optional; public interface HubRepository { Long save(Hub hub); - Optional findByIdWithHubMaster(Long hubId); + Optional findById(Long hubId); } diff --git a/src/main/java/com/seong/shoutlink/domain/hubMember/repository/HubMemberEntity.java b/src/main/java/com/seong/shoutlink/domain/hubMember/repository/HubMemberEntity.java index a11639e..cbe9346 100644 --- a/src/main/java/com/seong/shoutlink/domain/hubMember/repository/HubMemberEntity.java +++ b/src/main/java/com/seong/shoutlink/domain/hubMember/repository/HubMemberEntity.java @@ -1,5 +1,6 @@ package com.seong.shoutlink.domain.hubMember.repository; +import com.seong.shoutlink.domain.hub.Hub; import com.seong.shoutlink.domain.hub.repository.HubEntity; import com.seong.shoutlink.domain.hubMember.HubMemberRole; import jakarta.persistence.Column; @@ -45,4 +46,13 @@ public HubMemberEntity(Long memberId, HubEntity hubEntity, HubMemberRole hubMemb public static HubMemberEntity create(HubEntity hubEntity, Long masterId) { return new HubMemberEntity(masterId, hubEntity, HubMemberRole.MASTER); } + + public Hub toHub() { + return new Hub( + hubEntity.getHubId(), + memberId, + hubEntity.getName(), + hubEntity.getDescription(), + hubEntity.isPrivate()); + } } 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 dcdc8f7..f7d94da 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 @@ -2,7 +2,7 @@ import com.seong.shoutlink.domain.exception.ErrorCode; import com.seong.shoutlink.domain.exception.ShoutLinkException; -import com.seong.shoutlink.domain.hub.HubWithMaster; +import com.seong.shoutlink.domain.hub.Hub; import com.seong.shoutlink.domain.hub.service.HubRepository; import com.seong.shoutlink.domain.linkbundle.HubLinkBundle; import com.seong.shoutlink.domain.linkbundle.LinkBundle; @@ -54,17 +54,17 @@ private Member getMember(Long memberId) { @Transactional public CreateLinkBundleResponse createHubLinkBundle(CreateHubLinkBundleCommand command) { - HubWithMaster hubWithMaster = getHubWithMaster(command.hubId()); - hubWithMaster.checkMasterAuthority(command.memberId()); + Hub hub = getHub(command.hubId()); + hub.checkMasterAuthority(command.memberId()); LinkBundle linkBundle = new LinkBundle( command.description(), command.isDefault()); - HubLinkBundle hubLinkBundle = new HubLinkBundle(hubWithMaster.getHub(), linkBundle); + HubLinkBundle hubLinkBundle = new HubLinkBundle(hub, linkBundle); return new CreateLinkBundleResponse(linkBundleRepository.save(hubLinkBundle)); } - private HubWithMaster getHubWithMaster(Long hubId) { - return hubRepository.findByIdWithHubMaster(hubId) + private Hub getHub(Long hubId) { + return hubRepository.findById(hubId) .orElseThrow(() -> new ShoutLinkException("존재하지 않는 허브입니다.", ErrorCode.NOT_FOUND)); } } 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 378daf2..73a7dd7 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 @@ -5,7 +5,7 @@ import com.seong.shoutlink.domain.exception.ErrorCode; import com.seong.shoutlink.domain.exception.ShoutLinkException; -import com.seong.shoutlink.domain.hub.HubWithMaster; +import com.seong.shoutlink.domain.hub.Hub; import com.seong.shoutlink.domain.linkbundle.LinkBundle; import com.seong.shoutlink.domain.linkbundle.repository.FakeLinkBundleRepository; import com.seong.shoutlink.domain.linkbundle.service.request.CreateHubLinkBundleCommand; @@ -144,11 +144,11 @@ void setUp() { void createHubLinkBundle() { //given Member member = MemberFixture.member(); - HubWithMaster hubWithMaster = HubFixture.hubWithMaster(member); + Hub hub = HubFixture.hub(member); memberRepository.stub(member); - hubRepository.stub(hubWithMaster); + hubRepository.stub(hub); CreateHubLinkBundleCommand command = new CreateHubLinkBundleCommand( - hubWithMaster.getHub().getHubId(), + hub.getHubId(), member.getMemberId(), "테스트 허브", false); @@ -182,12 +182,12 @@ void notFound_WhenHubNotFound() { void unauthorized_WhenMemberIsNotHubMaster() { //given Member member = MemberFixture.member(); - HubWithMaster hubWithMaster = HubFixture.hubWithMaster(member); + Hub hub = HubFixture.hub(member); memberRepository.stub(member); - hubRepository.stub(hubWithMaster); + hubRepository.stub(hub); long unknownMemberId = member.getMemberId() + 1; CreateHubLinkBundleCommand command = new CreateHubLinkBundleCommand( - hubWithMaster.getHub().getHubId(), + hub.getHubId(), unknownMemberId, "허브", false); diff --git a/src/test/java/com/seong/shoutlink/fixture/StubHubRepository.java b/src/test/java/com/seong/shoutlink/fixture/StubHubRepository.java index 860bbaf..1ab4c79 100644 --- a/src/test/java/com/seong/shoutlink/fixture/StubHubRepository.java +++ b/src/test/java/com/seong/shoutlink/fixture/StubHubRepository.java @@ -1,7 +1,6 @@ package com.seong.shoutlink.fixture; import com.seong.shoutlink.domain.hub.Hub; -import com.seong.shoutlink.domain.hub.HubWithMaster; import com.seong.shoutlink.domain.hub.service.HubRepository; import java.util.HashMap; import java.util.Map; @@ -9,11 +8,11 @@ public class StubHubRepository implements HubRepository { - private final Map memory = new HashMap<>(); + private final Map memory = new HashMap<>(); - public void stub(HubWithMaster... hubWithMasters) { - for (HubWithMaster hubWithMaster : hubWithMasters) { - memory.put(getNextKey(), hubWithMaster); + public void stub(Hub... hubs) { + for (Hub hub : hubs) { + memory.put(getNextKey(), hub); } } @@ -27,7 +26,7 @@ public Long save(Hub hub) { } @Override - public Optional findByIdWithHubMaster(Long hubId) { + public Optional findById(Long hubId) { return memory.values().stream().findFirst(); } } From 7a4bc4f5e6b7bfed172dbc335d9971f7fae85a53 Mon Sep 17 00:00:00 2001 From: hseong3243 Date: Wed, 14 Feb 2024 21:35:33 +0900 Subject: [PATCH 06/10] =?UTF-8?q?feat:=20=ED=97=88=EB=B8=8C=20=EB=A7=81?= =?UTF-8?q?=ED=81=AC=20=EB=AC=B6=EC=9D=8C=20=EC=83=9D=EC=84=B1=20=EC=98=81?= =?UTF-8?q?=EC=86=8D=EC=84=B1=20=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 --- .../domain/linkbundle/HubLinkBundle.java | 4 ++++ .../repository/HubLinkBundleEntity.java | 21 +++++++++++++++++++ .../repository/LinkBundleEntity.java | 12 +++++++++++ .../repository/LinkBundleRepositoryImpl.java | 5 ++++- 4 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/seong/shoutlink/domain/linkbundle/repository/HubLinkBundleEntity.java diff --git a/src/main/java/com/seong/shoutlink/domain/linkbundle/HubLinkBundle.java b/src/main/java/com/seong/shoutlink/domain/linkbundle/HubLinkBundle.java index c9404bb..de186bf 100644 --- a/src/main/java/com/seong/shoutlink/domain/linkbundle/HubLinkBundle.java +++ b/src/main/java/com/seong/shoutlink/domain/linkbundle/HubLinkBundle.java @@ -13,4 +13,8 @@ public HubLinkBundle(Hub hub, LinkBundle linkBundle) { this.hub = hub; this.linkBundle = linkBundle; } + + public void initLinkBundleId(Long linkBundleId) { + linkBundle.initId(linkBundleId); + } } diff --git a/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/HubLinkBundleEntity.java b/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/HubLinkBundleEntity.java new file mode 100644 index 0000000..284191c --- /dev/null +++ b/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/HubLinkBundleEntity.java @@ -0,0 +1,21 @@ +package com.seong.shoutlink.domain.linkbundle.repository; + +import jakarta.persistence.DiscriminatorValue; +import jakarta.persistence.Entity; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@DiscriminatorValue("hub") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class HubLinkBundleEntity extends LinkBundleEntity { + + private Long hubId; + + public HubLinkBundleEntity(String description, boolean isDefault, Long hubId) { + super(description, isDefault); + this.hubId = hubId; + } +} diff --git a/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/LinkBundleEntity.java b/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/LinkBundleEntity.java index 70bf19f..bd86766 100644 --- a/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/LinkBundleEntity.java +++ b/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/LinkBundleEntity.java @@ -1,5 +1,7 @@ package com.seong.shoutlink.domain.linkbundle.repository; +import com.seong.shoutlink.domain.hub.Hub; +import com.seong.shoutlink.domain.linkbundle.HubLinkBundle; import com.seong.shoutlink.domain.linkbundle.LinkBundle; import com.seong.shoutlink.domain.linkbundle.MemberLinkBundle; import com.seong.shoutlink.domain.member.Member; @@ -45,6 +47,16 @@ public static LinkBundleEntity create(MemberLinkBundle memberLinkBundle) { ); } + public static LinkBundleEntity create(HubLinkBundle hubLinkBundle) { + LinkBundle linkBundle = hubLinkBundle.getLinkBundle(); + Hub hub = hubLinkBundle.getHub(); + return new HubLinkBundleEntity( + linkBundle.getDescription(), + linkBundle.isDefault(), + hub.getHubId() + ); + } + public LinkBundle toDomain() { return new LinkBundle( linkBundleId, diff --git a/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/LinkBundleRepositoryImpl.java b/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/LinkBundleRepositoryImpl.java index 7fdb2ec..c67845d 100644 --- a/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/LinkBundleRepositoryImpl.java +++ b/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/LinkBundleRepositoryImpl.java @@ -44,6 +44,9 @@ public List findLinkBundlesThatMembersHave(Member member) { @Override public Long save(HubLinkBundle hubLinkBundle) { - return null; + LinkBundleEntity linkBundleEntity = LinkBundleEntity.create(hubLinkBundle); + linkBundleJpaRepository.save(linkBundleEntity); + hubLinkBundle.initLinkBundleId(linkBundleEntity.getLinkBundleId()); + return linkBundleEntity.getLinkBundleId(); } } From 9496fde0334426a5ae3e98e3e98df9ad910bfef4 Mon Sep 17 00:00:00 2001 From: hseong3243 Date: Wed, 14 Feb 2024 21:47:11 +0900 Subject: [PATCH 07/10] =?UTF-8?q?feat:=20=ED=97=88=EB=B8=8C=20=EB=A7=81?= =?UTF-8?q?=ED=81=AC=20=EB=AC=B6=EC=9D=8C=20=EC=83=9D=EC=84=B1=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 --- .../controller/LinkBundleController.java | 16 ++++++++ .../repository/HubLinkBundleEntity.java | 2 +- .../controller/LinkBundleControllerTest.java | 37 +++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/seong/shoutlink/domain/linkbundle/controller/LinkBundleController.java b/src/main/java/com/seong/shoutlink/domain/linkbundle/controller/LinkBundleController.java index 409bed4..cea5c07 100644 --- a/src/main/java/com/seong/shoutlink/domain/linkbundle/controller/LinkBundleController.java +++ b/src/main/java/com/seong/shoutlink/domain/linkbundle/controller/LinkBundleController.java @@ -3,6 +3,7 @@ import com.seong.shoutlink.domain.auth.LoginUser; import com.seong.shoutlink.domain.linkbundle.controller.request.CreateLinkBundleRequest; import com.seong.shoutlink.domain.linkbundle.service.LinkBundleService; +import com.seong.shoutlink.domain.linkbundle.service.request.CreateHubLinkBundleCommand; import com.seong.shoutlink.domain.linkbundle.service.request.FindLinkBundlesCommand; import com.seong.shoutlink.domain.linkbundle.service.response.CreateLinkBundleCommand; import com.seong.shoutlink.domain.linkbundle.service.response.CreateLinkBundleResponse; @@ -12,6 +13,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -43,4 +45,18 @@ public ResponseEntity findLinkBundles( new FindLinkBundlesCommand(memberId)); return ResponseEntity.ok(response); } + + @PostMapping("/hubs/{hubId}/link-bundles") + public ResponseEntity createHubLinkBundle( + @LoginUser Long memberId, + @PathVariable("hubId") Long hubId, + @Valid @RequestBody CreateLinkBundleRequest request) { + CreateLinkBundleResponse response = linkBundleService.createHubLinkBundle( + new CreateHubLinkBundleCommand( + hubId, + memberId, + request.description(), + request.isDefault())); + return ResponseEntity.status(HttpStatus.CREATED).body(response); + } } diff --git a/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/HubLinkBundleEntity.java b/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/HubLinkBundleEntity.java index 284191c..c2d6974 100644 --- a/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/HubLinkBundleEntity.java +++ b/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/HubLinkBundleEntity.java @@ -11,7 +11,7 @@ @DiscriminatorValue("hub") @NoArgsConstructor(access = AccessLevel.PROTECTED) public class HubLinkBundleEntity extends LinkBundleEntity { - + private Long hubId; public HubLinkBundleEntity(String description, boolean isDefault, Long hubId) { diff --git a/src/test/java/com/seong/shoutlink/domain/linkbundle/controller/LinkBundleControllerTest.java b/src/test/java/com/seong/shoutlink/domain/linkbundle/controller/LinkBundleControllerTest.java index 507aaab..732041f 100644 --- a/src/test/java/com/seong/shoutlink/domain/linkbundle/controller/LinkBundleControllerTest.java +++ b/src/test/java/com/seong/shoutlink/domain/linkbundle/controller/LinkBundleControllerTest.java @@ -9,6 +9,8 @@ import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import com.seong.shoutlink.base.BaseControllerTest; @@ -87,4 +89,39 @@ void findLinkBundles() throws Exception { ) )); } + + @Test + @DisplayName("성공: 허브 링크 묶음 생성 API 호출 시") + void createHubLinkBundle() throws Exception { + //given + CreateLinkBundleRequest request = new CreateLinkBundleRequest("설명", false); + Long hubId = 1L; + CreateLinkBundleResponse response = new CreateLinkBundleResponse(1L); + + given(linkBundleService.createHubLinkBundle(any())).willReturn(response); + + //when + ResultActions resultActions = mockMvc.perform(post("/api/hubs/{hubId}/link-bundles", hubId) + .header(AUTHORIZATION, bearerAccessToken) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))); + + //then + resultActions.andExpect(status().isCreated()) + .andDo(restDocs.document( + requestHeaders( + headerWithName(AUTHORIZATION).description("액세스 토큰") + ), + pathParameters( + parameterWithName("hubId").description("허브 ID") + ), + requestFields( + fieldWithPath("description").type(JsonFieldType.STRING).description("설명"), + fieldWithPath("isDefault").type(JsonFieldType.BOOLEAN).description("기본 여부") + ), + responseFields( + fieldWithPath("linkBundleId").type(JsonFieldType.NUMBER).description("링크 묶음 ID") + ) + )); + } } \ No newline at end of file From ba912e32e96a87d002ea3969f7c32859ff72299d Mon Sep 17 00:00:00 2001 From: hseong3243 Date: Wed, 14 Feb 2024 21:50:47 +0900 Subject: [PATCH 08/10] =?UTF-8?q?docs:=20=ED=97=88=EB=B8=8C=20=EB=A7=81?= =?UTF-8?q?=ED=81=AC=20=EB=AC=B6=EC=9D=8C=20=EC=83=9D=EC=84=B1=20api=20?= =?UTF-8?q?=EB=AC=B8=EC=84=9C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/docs/asciidoc/index.adoc | 9 + src/main/resources/static/docs/index.html | 204 +++++++++++++++++++--- 2 files changed, 184 insertions(+), 29 deletions(-) diff --git a/src/docs/asciidoc/index.adoc b/src/docs/asciidoc/index.adoc index 123c5e1..ce1029f 100644 --- a/src/docs/asciidoc/index.adoc +++ b/src/docs/asciidoc/index.adoc @@ -51,6 +51,15 @@ operation::link-bundle-controller-test/find-link-bundles[snippets='http-request, operation::link-bundle-controller-test/find-link-bundles[snippets='http-response,response-fields'] +=== 허브 링크 묶음 생성 + +==== request + +operation::link-bundle-controller-test/create-hub-link-bundle[snippets='http-request,request-headers,path-parameters,request-fields'] + +==== response + +operation::link-bundle-controller-test/create-hub-link-bundle[snippets='http-response,response-fields'] == 링크 diff --git a/src/main/resources/static/docs/index.html b/src/main/resources/static/docs/index.html index e396a84..747a710 100644 --- a/src/main/resources/static/docs/index.html +++ b/src/main/resources/static/docs/index.html @@ -460,6 +460,7 @@

API 문서

  • 링크 @@ -703,7 +704,7 @@
    POST /api/link-bundles HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzA3NTc1OTY4LCJzdWIiOiIxIiwiZXhwIjoxNzA3NTc5NTY4LCJyb2xlIjoiUk9MRV9VU0VSIn0.khABcLP_2-tJN9dEFH_Ot6o6HXFnYWRVKU4rQsUcL0A
    +Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzA3OTE0OTc3LCJzdWIiOiIxIiwiZXhwIjoxNzA3OTE4NTc3LCJyb2xlIjoiUk9MRV9VU0VSIn0._cTswVG43ZajzCb6T36TqvfyUKRv3j_ZNaTdEqATWgw
     Content-Length: 57
     Host: localhost:8080
     
    @@ -819,7 +820,7 @@ 
    GET /api/link-bundles HTTP/1.1
    -Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzA3NTc1OTY4LCJzdWIiOiIxIiwiZXhwIjoxNzA3NTc5NTY4LCJyb2xlIjoiUk9MRV9VU0VSIn0.khABcLP_2-tJN9dEFH_Ot6o6HXFnYWRVKU4rQsUcL0A
    +Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzA3OTE0OTc3LCJzdWIiOiIxIiwiZXhwIjoxNzA3OTE4NTc3LCJyb2xlIjoiUk9MRV9VU0VSIn0._cTswVG43ZajzCb6T36TqvfyUKRv3j_ZNaTdEqATWgw
     Host: localhost:8080
    @@ -914,6 +915,145 @@
    +

    허브 링크 묶음 생성

    +
    +

    request

    +
    +
    HTTP request
    +
    +
    +
    POST /api/hubs/1/link-bundles HTTP/1.1
    +Content-Type: application/json;charset=UTF-8
    +Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzA3OTE0OTc3LCJzdWIiOiIxIiwiZXhwIjoxNzA3OTE4NTc3LCJyb2xlIjoiUk9MRV9VU0VSIn0._cTswVG43ZajzCb6T36TqvfyUKRv3j_ZNaTdEqATWgw
    +Content-Length: 53
    +Host: localhost:8080
    +
    +{
    +  "description" : "설명",
    +  "isDefault" : false
    +}
    +
    +
    +
    +
    +
    Request headers
    + ++++ + + + + + + + + + + + + +
    NameDescription

    Authorization

    액세스 토큰

    +
    +
    +
    Path parameters
    + + ++++ + + + + + + + + + + + + +
    Table 1. /api/hubs/{hubId}/link-bundles
    ParameterDescription

    hubId

    허브 ID

    +
    +
    +
    Request fields
    + +++++ + + + + + + + + + + + + + + + + + + + +
    PathTypeDescription

    description

    String

    설명

    isDefault

    Boolean

    기본 여부

    +
    +
    +
    +

    response

    +
    +
    HTTP response
    +
    +
    +
    HTTP/1.1 201 Created
    +Vary: Origin
    +Vary: Access-Control-Request-Method
    +Vary: Access-Control-Request-Headers
    +Content-Type: application/json;charset=UTF-8
    +Content-Length: 24
    +
    +{
    +  "linkBundleId" : 1
    +}
    +
    +
    +
    +
    +
    Response fields
    + +++++ + + + + + + + + + + + + + + +
    PathTypeDescription

    linkBundleId

    Number

    링크 묶음 ID

    +
    +
    +
    @@ -922,14 +1062,14 @@

    링크

    링크 생성

    -

    request

    +

    request

    -
    HTTP request
    +
    HTTP request
    POST /api/links HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzA3NTc1OTY4LCJzdWIiOiIxIiwiZXhwIjoxNzA3NTc5NTY4LCJyb2xlIjoiUk9MRV9VU0VSIn0.khABcLP_2-tJN9dEFH_Ot6o6HXFnYWRVKU4rQsUcL0A
    +Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzA3OTE0OTc3LCJzdWIiOiIxIiwiZXhwIjoxNzA3OTE4NTc3LCJyb2xlIjoiUk9MRV9VU0VSIn0._cTswVG43ZajzCb6T36TqvfyUKRv3j_ZNaTdEqATWgw
     Content-Length: 100
     Host: localhost:8080
     
    @@ -942,7 +1082,7 @@ 
    -
    Request headers
    +
    Request headers
    @@ -963,7 +1103,7 @@
    -
    Request fields
    +
    Request fields
    @@ -998,9 +1138,9 @@
    -

    response

    +

    response

    -
    HTTP response
    +
    HTTP response
    HTTP/1.1 201 Created
    @@ -1017,7 +1157,7 @@ 
    -
    Response fields
    +
    Response fields
    @@ -1045,19 +1185,19 @@

    링크 목록 조회

    -

    request

    +

    request

    -
    HTTP request
    +
    HTTP request
    GET /api/links?linkBundleId=1&page=0&size=10 HTTP/1.1
    -Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzA3NTc1OTY4LCJzdWIiOiIxIiwiZXhwIjoxNzA3NTc5NTY4LCJyb2xlIjoiUk9MRV9VU0VSIn0.khABcLP_2-tJN9dEFH_Ot6o6HXFnYWRVKU4rQsUcL0A
    +Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzA3OTE0OTc3LCJzdWIiOiIxIiwiZXhwIjoxNzA3OTE4NTc3LCJyb2xlIjoiUk9MRV9VU0VSIn0._cTswVG43ZajzCb6T36TqvfyUKRv3j_ZNaTdEqATWgw
     Host: localhost:8080
    @@ -1078,7 +1218,7 @@
    -
    Query parameters
    +
    Query parameters
    @@ -1108,9 +1248,9 @@
    -

    response

    +

    response

    -
    HTTP response
    +
    HTTP response
    HTTP/1.1 200 OK
    @@ -1133,7 +1273,7 @@ 
    -
    Response fields
    +
    Response fields
    @@ -1191,26 +1331,27 @@

    허브

    허브 생성

    -

    request

    +

    request

    -
    HTTP request
    +
    HTTP request
    POST /api/hubs HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzA3NTc1OTY4LCJzdWIiOiIxIiwiZXhwIjoxNzA3NTc5NTY4LCJyb2xlIjoiUk9MRV9VU0VSIn0.khABcLP_2-tJN9dEFH_Ot6o6HXFnYWRVKU4rQsUcL0A
    -Content-Length: 65
    +Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0IiwiaWF0IjoxNzA3OTE0OTc3LCJzdWIiOiIxIiwiZXhwIjoxNzA3OTE4NTc3LCJyb2xlIjoiUk9MRV9VU0VSIn0._cTswVG43ZajzCb6T36TqvfyUKRv3j_ZNaTdEqATWgw
    +Content-Length: 88
     Host: localhost:8080
     
     {
       "name" : "허브 이름",
    -  "description" : "허브 설명"
    +  "description" : "허브 설명",
    +  "isPrivate" : false
     }
    @@ -1231,7 +1372,7 @@
    -
    Request fields
    +
    Request fields
    @@ -1256,14 +1397,19 @@

    String

    + + + + +

    허브 설명

    isPrivate

    Boolean

    허브 공개 여부r

    -

    response

    +

    response

    -
    HTTP response
    +
    HTTP response
    HTTP/1.1 201 Created
    @@ -1280,7 +1426,7 @@ 
    -
    Response fields
    +
    Response fields
    @@ -1311,7 +1457,7 @@
    From cc8c274dc922c07418922c5bc40ddf87f518dd68 Mon Sep 17 00:00:00 2001 From: hseong3243 Date: Wed, 14 Feb 2024 22:02:25 +0900 Subject: [PATCH 09/10] =?UTF-8?q?fix:=20=ED=97=88=EB=B8=8C=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?`@Transactional`=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/hub/service/HubService.java | 2 ++ .../event/LinkBundleEventListenerTest.java | 35 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/main/java/com/seong/shoutlink/domain/hub/service/HubService.java b/src/main/java/com/seong/shoutlink/domain/hub/service/HubService.java index 447ba5b..43b96c1 100644 --- a/src/main/java/com/seong/shoutlink/domain/hub/service/HubService.java +++ b/src/main/java/com/seong/shoutlink/domain/hub/service/HubService.java @@ -11,6 +11,7 @@ import com.seong.shoutlink.domain.member.service.MemberRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service @RequiredArgsConstructor @@ -20,6 +21,7 @@ public class HubService { private final HubRepository hubRepository; private final EventPublisher eventPublisher; + @Transactional public CreateHubResponse createHub(CreateHubCommand command) { Member member = getMember(command.memberId()); Hub hub = new Hub(member, command.name(), command.description(), command.isPrivate()); diff --git a/src/test/java/com/seong/shoutlink/global/event/LinkBundleEventListenerTest.java b/src/test/java/com/seong/shoutlink/global/event/LinkBundleEventListenerTest.java index 7cd2b85..f383d1c 100644 --- a/src/test/java/com/seong/shoutlink/global/event/LinkBundleEventListenerTest.java +++ b/src/test/java/com/seong/shoutlink/global/event/LinkBundleEventListenerTest.java @@ -4,8 +4,12 @@ import com.seong.shoutlink.domain.auth.service.AuthService; import com.seong.shoutlink.domain.auth.service.request.CreateMemberCommand; +import com.seong.shoutlink.domain.hub.service.HubService; +import com.seong.shoutlink.domain.hub.service.request.CreateHubCommand; +import com.seong.shoutlink.domain.hub.service.response.CreateHubResponse; import com.seong.shoutlink.domain.linkbundle.repository.LinkBundleEntity; import com.seong.shoutlink.domain.member.Member; +import com.seong.shoutlink.domain.member.service.MemberRepository; import com.seong.shoutlink.fixture.MemberFixture; import jakarta.persistence.EntityManager; import org.junit.jupiter.api.DisplayName; @@ -22,6 +26,12 @@ class LinkBundleEventListenerTest { @Autowired private AuthService authService; + @Autowired + private HubService hubService; + + @Autowired + private MemberRepository memberRepository; + @Autowired private EntityManager em; @@ -51,4 +61,29 @@ void createDefaultLinkBundle() { assertThat(linkBundleEntity.getDescription()).isEqualTo("기본"); } } + + @Nested + @DisplayName("createHubEvent 수신 시") + class CreateHubEventTest { + + @Test + @DisplayName("성공: 기본 허브 링크 번들 생성됨") + void createDefaultHubLinkBundle() { + //given + Member member = MemberFixture.member(); + Long memberId = memberRepository.save(member); + CreateHubCommand command = new CreateHubCommand(memberId, "허브", "설명", false); + + //when + CreateHubResponse response = hubService.createHub(command); + + //then + LinkBundleEntity linkBundleEntity = em.createQuery( + "select hlb from HubLinkBundleEntity hlb " + + "where hlb.hubId = :hubId", LinkBundleEntity.class) + .setParameter("hubId", response.hubId()) + .getSingleResult(); + assertThat(linkBundleEntity.getDescription()).isEqualTo("기본"); + } + } } From 26013674d34736f0a5a55c1ab106a24afaced02a Mon Sep 17 00:00:00 2001 From: hseong3243 Date: Wed, 14 Feb 2024 22:30:31 +0900 Subject: [PATCH 10/10] =?UTF-8?q?test:=20=ED=86=B5=ED=95=A9=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EB=A5=BC=20=EC=9C=84=ED=95=9C=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=EB=B2=A0=EC=9D=B4=EC=8A=A4=20=ED=81=B4?= =?UTF-8?q?=EB=A6=AC=EB=84=88=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/hub/repository/HubEntity.java | 2 + .../hubMember/repository/HubMemberEntity.java | 2 + .../domain/link/repository/LinkEntity.java | 2 + .../repository/HubLinkBundleEntity.java | 2 + .../repository/LinkBundleEntity.java | 2 + .../repository/MemberLinkBundleEntity.java | 2 + .../member/repository/MemberEntity.java | 2 + .../shoutlink/base/BaseIntegrationTest.java | 17 +++++++ .../seong/shoutlink/base/DatabaseCleaner.java | 47 +++++++++++++++++++ .../event/LinkBundleEventListenerTest.java | 5 +- 10 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 src/test/java/com/seong/shoutlink/base/BaseIntegrationTest.java create mode 100644 src/test/java/com/seong/shoutlink/base/DatabaseCleaner.java diff --git a/src/main/java/com/seong/shoutlink/domain/hub/repository/HubEntity.java b/src/main/java/com/seong/shoutlink/domain/hub/repository/HubEntity.java index 4d53bae..afad4bf 100644 --- a/src/main/java/com/seong/shoutlink/domain/hub/repository/HubEntity.java +++ b/src/main/java/com/seong/shoutlink/domain/hub/repository/HubEntity.java @@ -6,6 +6,7 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.Table; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -13,6 +14,7 @@ @Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) +@Table(name = "hub") public class HubEntity { @Id diff --git a/src/main/java/com/seong/shoutlink/domain/hubMember/repository/HubMemberEntity.java b/src/main/java/com/seong/shoutlink/domain/hubMember/repository/HubMemberEntity.java index cbe9346..e5b8071 100644 --- a/src/main/java/com/seong/shoutlink/domain/hubMember/repository/HubMemberEntity.java +++ b/src/main/java/com/seong/shoutlink/domain/hubMember/repository/HubMemberEntity.java @@ -13,6 +13,7 @@ import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -20,6 +21,7 @@ @Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) +@Table(name = "hub_member") public class HubMemberEntity { @Id diff --git a/src/main/java/com/seong/shoutlink/domain/link/repository/LinkEntity.java b/src/main/java/com/seong/shoutlink/domain/link/repository/LinkEntity.java index 87fa420..282612e 100644 --- a/src/main/java/com/seong/shoutlink/domain/link/repository/LinkEntity.java +++ b/src/main/java/com/seong/shoutlink/domain/link/repository/LinkEntity.java @@ -7,6 +7,7 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.Table; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -14,6 +15,7 @@ @Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) +@Table(name = "link") public class LinkEntity { @Id diff --git a/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/HubLinkBundleEntity.java b/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/HubLinkBundleEntity.java index c2d6974..d8f3d79 100644 --- a/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/HubLinkBundleEntity.java +++ b/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/HubLinkBundleEntity.java @@ -2,6 +2,7 @@ import jakarta.persistence.DiscriminatorValue; import jakarta.persistence.Entity; +import jakarta.persistence.Table; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -10,6 +11,7 @@ @Getter @DiscriminatorValue("hub") @NoArgsConstructor(access = AccessLevel.PROTECTED) +@Table(name = "hub_link_bundle") public class HubLinkBundleEntity extends LinkBundleEntity { private Long hubId; diff --git a/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/LinkBundleEntity.java b/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/LinkBundleEntity.java index bd86766..1cc1e01 100644 --- a/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/LinkBundleEntity.java +++ b/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/LinkBundleEntity.java @@ -12,6 +12,7 @@ import jakarta.persistence.Id; import jakarta.persistence.Inheritance; import jakarta.persistence.InheritanceType; +import jakarta.persistence.Table; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -21,6 +22,7 @@ @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn @NoArgsConstructor(access = AccessLevel.PROTECTED) +@Table(name = "link_bundle") public abstract class LinkBundleEntity { @Id diff --git a/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/MemberLinkBundleEntity.java b/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/MemberLinkBundleEntity.java index ad4e96e..435ee18 100644 --- a/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/MemberLinkBundleEntity.java +++ b/src/main/java/com/seong/shoutlink/domain/linkbundle/repository/MemberLinkBundleEntity.java @@ -2,6 +2,7 @@ import jakarta.persistence.DiscriminatorValue; import jakarta.persistence.Entity; +import jakarta.persistence.Table; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -10,6 +11,7 @@ @Getter @DiscriminatorValue("member") @NoArgsConstructor(access = AccessLevel.PROTECTED) +@Table(name = "member_link_bundle") public class MemberLinkBundleEntity extends LinkBundleEntity { private Long memberId; diff --git a/src/main/java/com/seong/shoutlink/domain/member/repository/MemberEntity.java b/src/main/java/com/seong/shoutlink/domain/member/repository/MemberEntity.java index 5b31914..ad29976 100644 --- a/src/main/java/com/seong/shoutlink/domain/member/repository/MemberEntity.java +++ b/src/main/java/com/seong/shoutlink/domain/member/repository/MemberEntity.java @@ -8,12 +8,14 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.Table; import lombok.Getter; import lombok.NoArgsConstructor; @Entity @Getter @NoArgsConstructor +@Table(name = "member") public class MemberEntity { @Id diff --git a/src/test/java/com/seong/shoutlink/base/BaseIntegrationTest.java b/src/test/java/com/seong/shoutlink/base/BaseIntegrationTest.java new file mode 100644 index 0000000..04ddcd7 --- /dev/null +++ b/src/test/java/com/seong/shoutlink/base/BaseIntegrationTest.java @@ -0,0 +1,17 @@ +package com.seong.shoutlink.base; + +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +public class BaseIntegrationTest { + + @Autowired + private DatabaseCleaner databaseCleaner; + + @BeforeEach + void setUp() { + databaseCleaner.clear(); + } +} diff --git a/src/test/java/com/seong/shoutlink/base/DatabaseCleaner.java b/src/test/java/com/seong/shoutlink/base/DatabaseCleaner.java new file mode 100644 index 0000000..18d6ebe --- /dev/null +++ b/src/test/java/com/seong/shoutlink/base/DatabaseCleaner.java @@ -0,0 +1,47 @@ +package com.seong.shoutlink.base; + +import com.seong.shoutlink.domain.linkbundle.repository.HubLinkBundleEntity; +import com.seong.shoutlink.domain.linkbundle.repository.MemberLinkBundleEntity; +import jakarta.persistence.EntityManager; +import jakarta.persistence.Table; +import jakarta.persistence.metamodel.Type; +import java.util.List; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +@Component +@Transactional +public class DatabaseCleaner { + + private final EntityManager entityManager; + private final List tableNames; + + public DatabaseCleaner(EntityManager em) { + this.entityManager = em; + tableNames = entityManager.getMetamodel() + .getEntities() + .stream() + .map(Type::getJavaType) + .filter(j -> + !(j.isAssignableFrom(HubLinkBundleEntity.class) + || j.isAssignableFrom(MemberLinkBundleEntity.class)) + ) + .map(javaType -> javaType.getAnnotation(Table.class)) + .map(Table::name) + .toList(); + } + + public void clear() { + entityManager.flush(); + entityManager.createNativeQuery("SET REFERENTIAL_INTEGRITY FALSE") + .executeUpdate(); + + for (String tableName : tableNames) { + entityManager.createNativeQuery("truncate table " + tableName + " RESTART IDENTITY ") + .executeUpdate(); + } + + entityManager.createNativeQuery("SET REFERENTIAL_INTEGRITY true") + .executeUpdate(); + } +} diff --git a/src/test/java/com/seong/shoutlink/global/event/LinkBundleEventListenerTest.java b/src/test/java/com/seong/shoutlink/global/event/LinkBundleEventListenerTest.java index f383d1c..9fabafb 100644 --- a/src/test/java/com/seong/shoutlink/global/event/LinkBundleEventListenerTest.java +++ b/src/test/java/com/seong/shoutlink/global/event/LinkBundleEventListenerTest.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import com.seong.shoutlink.base.BaseIntegrationTest; import com.seong.shoutlink.domain.auth.service.AuthService; import com.seong.shoutlink.domain.auth.service.request.CreateMemberCommand; import com.seong.shoutlink.domain.hub.service.HubService; @@ -16,12 +17,10 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.event.RecordApplicationEvents; -@SpringBootTest @RecordApplicationEvents -class LinkBundleEventListenerTest { +class LinkBundleEventListenerTest extends BaseIntegrationTest { @Autowired private AuthService authService;