Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: 사용자 회원 가입 시 기본 링크 분류가 만들어진다. #15

Merged
merged 5 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading