Skip to content

Commit

Permalink
feat: 이메일 중복 확인 로직을 구현한다. (#142)
Browse files Browse the repository at this point in the history
* feat: 보호소 이메일 중복 확인 서비스 로직을 구현한다.

* feat: 보호소 이메일 중복 확인 api를 구현한다.

* feat: 봉사자 이메일 중복 확인 서비스 로직을 구현한다.

* feat: 봉사자 이메일 중복 확인 API를 구현한다.
  • Loading branch information
hseong3243 authored Nov 7, 2023
1 parent b7a1261 commit 10e2755
Show file tree
Hide file tree
Showing 17 changed files with 214 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.clova.anifriends.domain.shelter.controller;

import com.clova.anifriends.domain.auth.resolver.LoginUser;
import com.clova.anifriends.domain.shelter.dto.CheckDuplicateShelterEmailRequest;
import com.clova.anifriends.domain.shelter.dto.CheckDuplicateShelterResponse;
import com.clova.anifriends.domain.shelter.dto.FindShelterDetailResponse;
import com.clova.anifriends.domain.shelter.dto.FindShelterMyPageResponse;
import com.clova.anifriends.domain.shelter.service.ShelterService;
Expand All @@ -22,6 +24,14 @@ public class ShelterController {

private final ShelterService shelterService;

@PostMapping("/shelters/email")
public ResponseEntity<CheckDuplicateShelterResponse> checkDuplicateShelterEmail(
@RequestBody @Valid CheckDuplicateShelterEmailRequest checkDuplicateShelterEmailRequest) {
CheckDuplicateShelterResponse checkDuplicateShelterResponse
= shelterService.checkDuplicateEmail(checkDuplicateShelterEmailRequest.email());
return ResponseEntity.ok(checkDuplicateShelterResponse);
}

@PostMapping("/shelters")
public ResponseEntity<Void> registerShelter(
@RequestBody @Valid RegisterShelterRequest registerShelterRequest) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.clova.anifriends.domain.shelter.dto;

import jakarta.validation.constraints.NotBlank;

public record CheckDuplicateShelterEmailRequest(
@NotBlank(message = "이메일은 필수값입니다.")
String email) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.clova.anifriends.domain.shelter.dto;

public record CheckDuplicateShelterResponse(
boolean isDuplicated) {

public static CheckDuplicateShelterResponse from(boolean isDuplicated) {
return new CheckDuplicateShelterResponse(isDuplicated);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.clova.anifriends.domain.shelter.exception;

import com.clova.anifriends.global.exception.ConflictException;
import com.clova.anifriends.global.exception.ErrorCode;

public class ShelterConflictException extends ConflictException {

public ShelterConflictException(String message) {
super(ErrorCode.ALREADY_EXISTS, message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@
public interface ShelterRepository extends JpaRepository<Shelter, Long> {

Optional<Shelter> findByEmail(ShelterEmail email);

boolean existsByEmail(ShelterEmail email);
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.clova.anifriends.domain.shelter.service;

import com.clova.anifriends.domain.shelter.Shelter;
import com.clova.anifriends.domain.shelter.dto.CheckDuplicateShelterResponse;
import com.clova.anifriends.domain.shelter.dto.FindShelterDetailResponse;
import com.clova.anifriends.domain.shelter.dto.FindShelterMyPageResponse;
import com.clova.anifriends.domain.shelter.exception.ShelterBadRequestException;
import com.clova.anifriends.domain.shelter.exception.ShelterNotFoundException;
import com.clova.anifriends.domain.shelter.repository.ShelterRepository;
import com.clova.anifriends.domain.shelter.wrapper.ShelterEmail;
import java.text.MessageFormat;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -53,19 +55,25 @@ private String encodePassword(String password) {
}

private void validatePasswordNotNull(String password) {
if(Objects.isNull(password)) {
if (Objects.isNull(password)) {
throw new ShelterBadRequestException("패스워드는 필수값입니다.");
}
}

private void validatePasswordLength(String password) {
if(password.length() < MIN_PASSWORD_LENGTH || password.length() > MAX_PASSWORD_LENGTH) {
if (password.length() < MIN_PASSWORD_LENGTH || password.length() > MAX_PASSWORD_LENGTH) {
throw new ShelterBadRequestException(
MessageFormat.format("패스워드는 {0}자 이상, {1}자 이하여야 합니다.",
MIN_PASSWORD_LENGTH, MAX_PASSWORD_LENGTH));
}
}

@Transactional(readOnly = true)
public CheckDuplicateShelterResponse checkDuplicateEmail(String email) {
boolean isDuplicated = shelterRepository.existsByEmail(new ShelterEmail(email));
return CheckDuplicateShelterResponse.from(isDuplicated);
}

@Transactional(readOnly = true)
public FindShelterDetailResponse findShelterDetail(
Long shelterId
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.clova.anifriends.domain.volunteer.controller;

import com.clova.anifriends.domain.auth.resolver.LoginUser;
import com.clova.anifriends.domain.volunteer.dto.request.CheckDuplicateVolunteerEmailRequest;
import com.clova.anifriends.domain.volunteer.dto.request.RegisterVolunteerRequest;
import com.clova.anifriends.domain.volunteer.dto.response.CheckDuplicateVolunteerEmailResponse;
import com.clova.anifriends.domain.volunteer.dto.response.FindVolunteerMyPageResponse;
import com.clova.anifriends.domain.volunteer.dto.response.FindVolunteerProfileResponse;
import com.clova.anifriends.domain.volunteer.service.VolunteerService;
Expand All @@ -24,6 +26,15 @@ public class VolunteerController {
private final VolunteerService volunteerService;
private static final String BASE_URI = "/api/volunteers/";

@PostMapping("/volunteers/email")
public ResponseEntity<CheckDuplicateVolunteerEmailResponse> checkDuplicateVolunteerEmail(
@RequestBody @Valid CheckDuplicateVolunteerEmailRequest checkDuplicateVolunteerEmailRequest) {
String targetEmail = checkDuplicateVolunteerEmailRequest.email();
CheckDuplicateVolunteerEmailResponse checkDuplicateVolunteerEmailResponse
= volunteerService.checkDuplicateVolunteerEmail(targetEmail);
return ResponseEntity.ok(checkDuplicateVolunteerEmailResponse);
}

@PostMapping("/volunteers")
public ResponseEntity<Void> registerVolunteer(
@RequestBody @Valid RegisterVolunteerRequest registerVolunteerRequest
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.clova.anifriends.domain.volunteer.dto.request;

import jakarta.validation.constraints.NotBlank;

public record CheckDuplicateVolunteerEmailRequest(
@NotBlank(message = "이메일은 필수값입니다.")
String email) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.clova.anifriends.domain.volunteer.dto.response;

public record CheckDuplicateVolunteerEmailResponse(boolean isDuplicated) {

public static CheckDuplicateVolunteerEmailResponse from(boolean isDuplicated) {
return new CheckDuplicateVolunteerEmailResponse(isDuplicated);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@
public interface VolunteerRepository extends JpaRepository<Volunteer, Long> {

Optional<Volunteer> findByEmail(VolunteerEmail email);

boolean existsByEmail(VolunteerEmail email);
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.clova.anifriends.domain.volunteer.service;

import com.clova.anifriends.domain.volunteer.Volunteer;
import com.clova.anifriends.domain.volunteer.dto.response.CheckDuplicateVolunteerEmailResponse;
import com.clova.anifriends.domain.volunteer.dto.response.FindVolunteerMyPageResponse;
import com.clova.anifriends.domain.volunteer.dto.response.FindVolunteerProfileResponse;
import com.clova.anifriends.domain.volunteer.exception.VolunteerNotFoundException;
import com.clova.anifriends.domain.volunteer.repository.VolunteerRepository;
import com.clova.anifriends.domain.volunteer.wrapper.VolunteerEmail;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -15,6 +17,12 @@ public class VolunteerService {

private final VolunteerRepository volunteerRepository;

@Transactional(readOnly = true)
public CheckDuplicateVolunteerEmailResponse checkDuplicateVolunteerEmail(String email) {
boolean isDuplicated = volunteerRepository.existsByEmail(new VolunteerEmail(email));
return CheckDuplicateVolunteerEmailResponse.from(isDuplicated);
}

@Transactional
public Long registerVolunteer(
String email,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ public SecurityFilterChain filterChain(
.requestMatchers(HttpMethod.GET, "/api/volunteers/*/recruitments/completed").permitAll()
.requestMatchers(HttpMethod.GET, "/api/shelters/*/recruitments").permitAll()
.requestMatchers(HttpMethod.GET, "/api/volunteers/*/reviews").permitAll()
.requestMatchers(HttpMethod.POST, "/api/volunteers/email").permitAll()
.requestMatchers(HttpMethod.POST, "/api/shelters/email").permitAll()
.requestMatchers(HttpMethod.POST, "/api/volunteers").permitAll()
.requestMatchers(HttpMethod.POST, "/api/shelters").permitAll()
.requestMatchers("/api/shelters/**").hasRole(ROLE_SHELTER)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import com.clova.anifriends.base.BaseControllerTest;
import com.clova.anifriends.docs.format.DocumentationFormatGenerator;
import com.clova.anifriends.domain.shelter.Shelter;
import com.clova.anifriends.domain.shelter.dto.CheckDuplicateShelterEmailRequest;
import com.clova.anifriends.domain.shelter.dto.CheckDuplicateShelterResponse;
import com.clova.anifriends.domain.shelter.dto.FindShelterDetailResponse;
import com.clova.anifriends.domain.shelter.dto.FindShelterMyPageResponse;
import com.clova.anifriends.domain.shelter.support.ShelterFixture;
Expand All @@ -30,6 +32,36 @@

class ShelterControllerTest extends BaseControllerTest {

@Test
@DisplayName("보호소 이메일 중복 확인 api 호출 시")
void checkDuplicateShelterEmail() throws Exception {
//given
CheckDuplicateShelterEmailRequest checkDuplicateShelterEmailRequest
= new CheckDuplicateShelterEmailRequest("[email protected]");
CheckDuplicateShelterResponse checkDuplicateShelterResponse
= new CheckDuplicateShelterResponse(true);

given(shelterService.checkDuplicateEmail(anyString())).willReturn(
checkDuplicateShelterResponse);

//when
ResultActions resultActions = mockMvc.perform(post("/api/shelters/email")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(checkDuplicateShelterEmailRequest)));

//then
resultActions.andExpect(status().isOk())
.andDo(restDocs.document(
requestFields(
fieldWithPath("email").type(JsonFieldType.STRING).description("보호소 이메일")
),
responseFields(
fieldWithPath("isDuplicated").type(JsonFieldType.BOOLEAN)
.description("이메일 중복 여부")
)
));
}

@Test
@DisplayName("보호소 회원가입 api 호출 시")
void registerShelter() throws Exception {
Expand Down Expand Up @@ -66,13 +98,16 @@ void registerShelter() throws Exception {
.attributes(DocumentationFormatGenerator.getConstraint("1자 이상, 20자 이하")),
fieldWithPath("address").type(JsonFieldType.STRING).description("보호소 주소")
.attributes(DocumentationFormatGenerator.getConstraint("1자 이상, 100자 이하")),
fieldWithPath("addressDetail").type(JsonFieldType.STRING).description("보호소 상세 주소")
fieldWithPath("addressDetail").type(JsonFieldType.STRING)
.description("보호소 상세 주소")
.attributes(DocumentationFormatGenerator.getConstraint("1자 이상, 100자 이하")),
fieldWithPath("phoneNumber").type(JsonFieldType.STRING).description("보호소 전화번호")
.attributes(DocumentationFormatGenerator.getConstraint("- 포함, 전화번호 형식 준수")),
fieldWithPath("sparePhoneNumber").type(JsonFieldType.STRING).description("보호소 임시 전화번호")
fieldWithPath("sparePhoneNumber").type(JsonFieldType.STRING)
.description("보호소 임시 전화번호")
.attributes(DocumentationFormatGenerator.getConstraint("- 포함, 전화번호 형식 준수")),
fieldWithPath("isOpenedAddress").type(JsonFieldType.BOOLEAN).description("보호소 주소 공개 여부")
fieldWithPath("isOpenedAddress").type(JsonFieldType.BOOLEAN)
.description("보호소 주소 공개 여부")
),
responseHeaders(
headerWithName("Location").description("생성된 리소스 접근 가능 위치")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import com.clova.anifriends.domain.shelter.Shelter;
import com.clova.anifriends.domain.shelter.ShelterImage;
import com.clova.anifriends.domain.shelter.dto.CheckDuplicateShelterResponse;
import com.clova.anifriends.domain.shelter.dto.FindShelterDetailResponse;
import com.clova.anifriends.domain.shelter.dto.FindShelterMyPageResponse;
import com.clova.anifriends.domain.shelter.exception.ShelterBadRequestException;
Expand Down Expand Up @@ -54,6 +55,28 @@ public boolean matches(CharSequence rawPassword, String encodedPassword) {
Shelter givenShelter;
ShelterImage givenShelterImage;

@Nested
@DisplayName("checkDuplicateEmail 실행 시")
class CheckDuplicateEmailTest {

@Test
@DisplayName("성공")
void checkDuplicateEmail() {
//given
String email = "[email protected]";

given(shelterRepository.existsByEmail(any())).willReturn(true);

//when
CheckDuplicateShelterResponse response
= shelterService.checkDuplicateEmail(email);

//then
assertThat(response.isDuplicated()).isTrue();
}

}

@Nested
@DisplayName("registerShelter 실행 시")
class RegisterShelterTest {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.BDDMockito.given;
import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders;
import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.payload.JsonFieldType.BOOLEAN;
import static org.springframework.restdocs.payload.JsonFieldType.NUMBER;
import static org.springframework.restdocs.payload.JsonFieldType.STRING;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
Expand All @@ -18,9 +20,12 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import com.clova.anifriends.base.BaseControllerTest;
import com.clova.anifriends.docs.format.DocumentationFormatGenerator;
import com.clova.anifriends.domain.applicant.wrapper.ApplicantStatus;
import com.clova.anifriends.domain.volunteer.Volunteer;
import com.clova.anifriends.domain.volunteer.dto.request.CheckDuplicateVolunteerEmailRequest;
import com.clova.anifriends.domain.volunteer.dto.request.RegisterVolunteerRequest;
import com.clova.anifriends.domain.volunteer.dto.response.CheckDuplicateVolunteerEmailResponse;
import com.clova.anifriends.domain.volunteer.dto.response.FindVolunteerMyPageResponse;
import com.clova.anifriends.domain.volunteer.dto.response.FindVolunteerProfileResponse;
import com.clova.anifriends.domain.volunteer.support.VolunteerDtoFixture;
Expand All @@ -33,6 +38,36 @@

class VolunteerControllerTest extends BaseControllerTest {

@Test
@DisplayName("봉사자 이메일 중복 확인 API 호출 시")
void checkDuplicateVolunteerEmail() throws Exception {
//given
CheckDuplicateVolunteerEmailRequest checkDuplicateVolunteerEmailRequest
= VolunteerDtoFixture.checkDuplicateVolunteerEmailRequest();
CheckDuplicateVolunteerEmailResponse checkDuplicateVolunteerEmailResponse
= new CheckDuplicateVolunteerEmailResponse(false);

given(volunteerService.checkDuplicateVolunteerEmail(anyString()))
.willReturn(checkDuplicateVolunteerEmailResponse);

//when
ResultActions resultActions = mockMvc.perform(post("/api/volunteers/email")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(checkDuplicateVolunteerEmailRequest)));

//then
resultActions.andExpect(status().isOk())
.andDo(restDocs.document(
requestFields(
fieldWithPath("email").type(STRING).description("사용자 이메일")
.attributes(DocumentationFormatGenerator.getConstraint("@를 포함한 이메일 형식"))
),
responseFields(
fieldWithPath("isDuplicated").type(BOOLEAN).description("이메일 중복 여부")
)
));
}

@Test
@DisplayName("봉사자 회원가입 API 호출 시")
void registerVolunteer() throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.clova.anifriends.domain.volunteer.Volunteer;
import com.clova.anifriends.domain.volunteer.VolunteerImage;
import com.clova.anifriends.domain.volunteer.dto.request.RegisterVolunteerRequest;
import com.clova.anifriends.domain.volunteer.dto.response.CheckDuplicateVolunteerEmailResponse;
import com.clova.anifriends.domain.volunteer.dto.response.FindVolunteerMyPageResponse;
import com.clova.anifriends.domain.volunteer.dto.response.FindVolunteerProfileResponse;
import com.clova.anifriends.domain.volunteer.repository.VolunteerRepository;
Expand All @@ -34,6 +35,27 @@ class VolunteerServiceTest {
@Mock
VolunteerRepository volunteerRepository;

@Nested
@DisplayName("checkDuplicateVolunteerEmail 메서드 실행 시")
class CheckDuplicateVolunteerEmailTest {

@Test
@DisplayName("성공")
void checkDuplicateVolunteerEmail() {
//given
String email = "[email protected]";

given(volunteerRepository.existsByEmail(any())).willReturn(true);

//when
CheckDuplicateVolunteerEmailResponse response
= volunteerService.checkDuplicateVolunteerEmail(email);

//then
assertThat(response.isDuplicated()).isTrue();
}
}

@Nested
@DisplayName("registerVolunteer 메서드 실행 시")
class RegisterVolunteerTest {
Expand Down
Loading

0 comments on commit 10e2755

Please sign in to comment.