Skip to content

Commit

Permalink
Feat: 사용자 회원 가입 시 기본 링크 분류가 만들어진다.
Browse files Browse the repository at this point in the history
Feat: 사용자 회원 가입 시 기본 링크 분류가 만들어진다.
  • Loading branch information
hseong3243 authored Jan 29, 2024
2 parents 7872a17 + 1fcff56 commit 0da1f03
Show file tree
Hide file tree
Showing 16 changed files with 366 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,22 @@

import com.seong.shoutlink.domain.auth.JwtProvider;
import com.seong.shoutlink.domain.auth.PasswordEncoder;
import com.seong.shoutlink.domain.auth.service.event.CreateMemberEvent;
import com.seong.shoutlink.domain.auth.service.request.CreateMemberCommand;
import com.seong.shoutlink.domain.auth.service.request.LoginCommand;
import com.seong.shoutlink.domain.auth.service.response.CreateMemberResponse;
import com.seong.shoutlink.domain.auth.service.response.LoginResponse;
import com.seong.shoutlink.domain.auth.service.response.TokenResponse;
import com.seong.shoutlink.domain.common.EventPublisher;
import com.seong.shoutlink.domain.exception.ErrorCode;
import com.seong.shoutlink.domain.exception.ShoutLinkException;
import com.seong.shoutlink.domain.member.Member;
import com.seong.shoutlink.domain.member.MemberRole;
import com.seong.shoutlink.domain.member.service.MemberRepository;
import com.seong.shoutlink.domain.exception.ErrorCode;
import com.seong.shoutlink.domain.exception.ShoutLinkException;
import java.util.regex.Pattern;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
Expand All @@ -26,6 +29,7 @@ public class AuthService {
private final MemberRepository memberRepository;
private final PasswordEncoder passwordEncoder;
private final JwtProvider jwtProvider;
private final EventPublisher eventPublisher;

private void validatePassword(CreateMemberCommand command) {
if(!PASSWORD_PATTEN.matcher(command.password()).matches()) {
Expand All @@ -34,6 +38,7 @@ private void validatePassword(CreateMemberCommand command) {
}
}

@Transactional
public CreateMemberResponse createMember(CreateMemberCommand command) {
validatePassword(command);
memberRepository.findByEmail(command.email())
Expand All @@ -49,7 +54,9 @@ public CreateMemberResponse createMember(CreateMemberCommand command) {
passwordEncoder.encode(command.password()),
command.nickname(),
MemberRole.ROLE_USER);
return new CreateMemberResponse(memberRepository.save(member));
Long memberId = memberRepository.save(member);
eventPublisher.publishEvent(new CreateMemberEvent(memberId));
return new CreateMemberResponse(memberId);
}

public LoginResponse login(LoginCommand command) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.seong.shoutlink.domain.auth.service.event;

import com.seong.shoutlink.domain.common.Event;

public record CreateMemberEvent(Long memberId) implements Event {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.seong.shoutlink.domain.linkbundle;

import com.seong.shoutlink.domain.member.Member;
import lombok.Getter;

@Getter
public class LinkBundle {

private Long linkBundleId;
private String description;
private boolean isDefault;
private Member member;

public LinkBundle(String description, boolean isDefault, Member member) {
this.description = description;
this.isDefault = isDefault;
this.member = member;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.seong.shoutlink.domain.linkbundle.repository;

import com.seong.shoutlink.domain.linkbundle.LinkBundle;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class LinkBundleEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long linkBundleId;

private String description;
private boolean isDefault;
private Long memberId;

private LinkBundleEntity(
Long linkBundleId,
String description,
boolean isDefault,
Long memberId) {
this.linkBundleId = linkBundleId;
this.description = description;
this.isDefault = isDefault;
this.memberId = memberId;
}

public static LinkBundleEntity create(LinkBundle linkBundle) {
return new LinkBundleEntity(
linkBundle.getLinkBundleId(),
linkBundle.getDescription(),
linkBundle.isDefault(),
linkBundle.getMember().getMemberId());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.seong.shoutlink.domain.linkbundle.repository;

import org.springframework.data.jpa.repository.JpaRepository;

public interface LinkBundleJpaRepository extends JpaRepository<LinkBundleEntity, Long> {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.seong.shoutlink.domain.linkbundle.repository;

import com.seong.shoutlink.domain.linkbundle.LinkBundle;
import com.seong.shoutlink.domain.linkbundle.service.LinkBundleRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

@Repository
@RequiredArgsConstructor
public class LinkBundleRepositoryImpl implements LinkBundleRepository {

private final LinkBundleJpaRepository linkBundleJpaRepository;

@Override
public Long save(LinkBundle linkBundle) {
LinkBundleEntity linkBundleEntity = LinkBundleEntity.create(linkBundle);
linkBundleJpaRepository.save(linkBundleEntity);
return linkBundleEntity.getLinkBundleId();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.seong.shoutlink.domain.linkbundle.service;

import com.seong.shoutlink.domain.linkbundle.LinkBundle;

public interface LinkBundleRepository {

Long save(LinkBundle linkBundle);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.seong.shoutlink.domain.linkbundle.service;

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.service.response.CreateLinkBundleCommand;
import com.seong.shoutlink.domain.linkbundle.service.response.CreateLinkBundleResponse;
import com.seong.shoutlink.domain.member.Member;
import com.seong.shoutlink.domain.member.service.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class LinkBundleService {

private final MemberRepository memberRepository;
private final LinkBundleRepository linkBundleRepository;

@Transactional
public CreateLinkBundleResponse createLinkBundle(CreateLinkBundleCommand command) {
Member member = memberRepository.findById(command.memberId())
.orElseThrow(() -> new ShoutLinkException("존재하지 않는 사용자입니다.", ErrorCode.NOT_FOUND));
LinkBundle linkBundle = new LinkBundle(command.description(), command.isDefault(), member);
return new CreateLinkBundleResponse(linkBundleRepository.save(linkBundle));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.seong.shoutlink.domain.linkbundle.service.response;

public record CreateLinkBundleCommand(Long memberId, String description, boolean isDefault) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.seong.shoutlink.domain.linkbundle.service.response;

public record CreateLinkBundleResponse(Long linkBundleId) {

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.seong.shoutlink.global.config;

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.SpringEventPublisher;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
Expand All @@ -13,4 +15,9 @@ public class EventConfig {
public EventPublisher eventPublisher(ApplicationEventPublisher applicationEventPublisher) {
return new SpringEventPublisher(applicationEventPublisher);
}

@Bean
public SpringEventListener springEventListener(LinkBundleService linkBundleService) {
return new SpringEventListener(linkBundleService);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.seong.shoutlink.global.event;

import com.seong.shoutlink.domain.auth.service.event.CreateMemberEvent;
import com.seong.shoutlink.domain.linkbundle.service.LinkBundleService;
import com.seong.shoutlink.domain.linkbundle.service.response.CreateLinkBundleCommand;
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 {

private static final String DEFAULT_LINK_BUNDLE = "기본";

private final LinkBundleService linkBundleService;

@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void createDefaultLinkBundle(CreateMemberEvent event) {
CreateLinkBundleCommand command
= new CreateLinkBundleCommand(event.memberId(), DEFAULT_LINK_BUNDLE, true);
linkBundleService.createLinkBundle(command);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@
import com.seong.shoutlink.domain.auth.service.request.LoginCommand;
import com.seong.shoutlink.domain.auth.service.response.CreateMemberResponse;
import com.seong.shoutlink.domain.auth.service.response.LoginResponse;
import com.seong.shoutlink.domain.common.StubEventPublisher;
import com.seong.shoutlink.domain.exception.ErrorCode;
import com.seong.shoutlink.domain.exception.ShoutLinkException;
import com.seong.shoutlink.domain.member.Member;
import com.seong.shoutlink.domain.member.MemberRole;
import com.seong.shoutlink.domain.member.repository.StubMemberRepository;
import com.seong.shoutlink.global.auth.jwt.JJwtProvider;
import com.seong.shoutlink.domain.exception.ErrorCode;
import com.seong.shoutlink.domain.exception.ShoutLinkException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
Expand All @@ -37,6 +38,7 @@ class CreateMemberTest {
Member savedMember;
StubMemberRepository memberRepository;
AuthService authService;
StubEventPublisher eventPublisher;

@BeforeEach
void setUp() {
Expand All @@ -46,7 +48,12 @@ void setUp() {
MemberRole memberRole = MemberRole.ROLE_USER;
savedMember = new Member(email, password, nickname, memberRole);
memberRepository = new StubMemberRepository(savedMember);
authService = new AuthService(memberRepository, passwordEncoder, jwtProvider);
eventPublisher = new StubEventPublisher();
authService = new AuthService(
memberRepository,
passwordEncoder,
jwtProvider,
eventPublisher);
}

@ParameterizedTest
Expand Down Expand Up @@ -121,6 +128,20 @@ void duplicateNickname_WhenNicknameIsDuplicate() {
.extracting(e -> ((ShoutLinkException) e).getErrorCode())
.isEqualTo(ErrorCode.DUPLICATE_NICKNAME);
}

@Test
@DisplayName("성공: 회원 생성 이벤트 발행함")
void publishCreateMemberEvent() {
//given
CreateMemberCommand command
= new CreateMemberCommand("email@email.com", "asdf123!", "nickname");

//when
CreateMemberResponse response = authService.createMember(command);

//then
assertThat(eventPublisher.getPublishEventCount()).isEqualTo(1);
}
}

@Nested
Expand All @@ -131,6 +152,7 @@ class LoginTest {
Member savedMember;
StubMemberRepository memberRepository;
AuthService authService;
StubEventPublisher eventPublisher;

@BeforeEach
void setUp() {
Expand All @@ -141,7 +163,12 @@ void setUp() {
MemberRole memberRole = MemberRole.ROLE_USER;
savedMember = new Member(1L, email, password, nickname, memberRole);
memberRepository = new StubMemberRepository(savedMember);
authService = new AuthService(memberRepository, passwordEncoder, jwtProvider);
eventPublisher = new StubEventPublisher();
authService = new AuthService(
memberRepository,
passwordEncoder,
jwtProvider,
eventPublisher);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.seong.shoutlink.domain.linkbundle.repository;

import com.seong.shoutlink.domain.linkbundle.LinkBundle;
import com.seong.shoutlink.domain.linkbundle.service.LinkBundleRepository;
import java.util.HashMap;
import java.util.Map;

public class FakeLinkBundleRepository implements LinkBundleRepository {

private final Map<Long, LinkBundle> memory = new HashMap<>();

@Override
public Long save(LinkBundle linkBundle) {
long nextId = getNextId();
memory.put(nextId, linkBundle);
return nextId;
}

private long getNextId() {
return memory.keySet().size() + 1;
}
}
Loading

0 comments on commit 0da1f03

Please sign in to comment.