ADD session maintaining
- @LoginAccount 파라메타를 command 객체로 바꾸는 ArgumentResolver 추가 - .editorconfig 에서 로그파일 (app.log)를 무시하도록 설정 추가
This commit is contained in:
@@ -8,6 +8,15 @@ charset = utf-8
|
||||
# [newline-eof]
|
||||
insert_final_newline = true
|
||||
|
||||
# Ignore
|
||||
[*.log]
|
||||
charset = unset
|
||||
end_of_line = unset
|
||||
insert_final_newline = unset
|
||||
trim_trailing_whitespace = unset
|
||||
indent_style = unset
|
||||
indent_size = unset
|
||||
|
||||
[*.bat]
|
||||
end_of_line = crlf
|
||||
|
||||
@@ -23,4 +32,4 @@ tab_width = 4
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# [line-length-100]
|
||||
max_line_length = 100
|
||||
max_line_length = 100
|
||||
|
||||
@@ -90,6 +90,7 @@ jacocoTestCoverageVerification {
|
||||
enabled = true
|
||||
element = 'CLASS'
|
||||
excludes = [
|
||||
"*.*ArgumentResolver",
|
||||
"*.YouAndMeApplication",
|
||||
"*.*Request",
|
||||
"*.*Response",
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package com.yam.app.account.application;
|
||||
|
||||
import com.yam.app.account.domain.AccountPrincipal;
|
||||
import com.yam.app.account.domain.ConfirmRegisterAccountProcessor;
|
||||
import com.yam.app.account.domain.LoginAccountProcessor;
|
||||
import com.yam.app.account.domain.RegisterAccountEvent;
|
||||
import com.yam.app.account.domain.RegisterAccountProcessor;
|
||||
import com.yam.app.account.presentation.AccountResponse;
|
||||
import com.yam.app.account.presentation.ConfirmRegisterAccountRequestCommand;
|
||||
import com.yam.app.account.presentation.GetSessionAccountCommand;
|
||||
import com.yam.app.account.presentation.LoginAccountRequestCommand;
|
||||
import com.yam.app.account.presentation.RegisterAccountRequestCommand;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
@@ -20,16 +22,19 @@ public class AccountFacade {
|
||||
private final ApplicationEventPublisher publisher;
|
||||
private final ConfirmRegisterAccountProcessor confirmRegisterProcessor;
|
||||
private final LoginAccountProcessor loginProcessor;
|
||||
private final AccountPrincipal accountPrincipal;
|
||||
|
||||
public AccountFacade(RegisterAccountProcessor registerProcessor,
|
||||
AccountTranslator translator, ApplicationEventPublisher publisher,
|
||||
ConfirmRegisterAccountProcessor confirmRegisterProcessor,
|
||||
LoginAccountProcessor loginProcessor) {
|
||||
LoginAccountProcessor loginProcessor,
|
||||
AccountPrincipal accountPrincipal) {
|
||||
this.registerProcessor = registerProcessor;
|
||||
this.translator = translator;
|
||||
this.publisher = publisher;
|
||||
this.confirmRegisterProcessor = confirmRegisterProcessor;
|
||||
this.loginProcessor = loginProcessor;
|
||||
this.accountPrincipal = accountPrincipal;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -48,7 +53,13 @@ public class AccountFacade {
|
||||
confirmRegisterProcessor.registerConfirm(request.getToken(), request.getEmail());
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public void login(LoginAccountRequestCommand request) {
|
||||
loginProcessor.login(request.getEmail(), request.getPassword());
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public AccountResponse getSessionAccount(GetSessionAccountCommand command) {
|
||||
return translator.toResponse(accountPrincipal.getAccount(command.getEmail()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.yam.app.account.domain;
|
||||
|
||||
public final class AccountPrincipal {
|
||||
|
||||
private final AccountReader accountReader;
|
||||
|
||||
public AccountPrincipal(AccountReader accountReader) {
|
||||
this.accountReader = accountReader;
|
||||
}
|
||||
|
||||
public Account getAccount(String email) {
|
||||
return accountReader.findByEmail(email)
|
||||
.orElseThrow(IllegalStateException::new);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.yam.app.account.infrastructure;
|
||||
|
||||
import com.yam.app.account.domain.AccountPrincipal;
|
||||
import com.yam.app.account.domain.AccountReader;
|
||||
import com.yam.app.account.domain.AccountRepository;
|
||||
import com.yam.app.account.domain.ConfirmRegisterAccountProcessor;
|
||||
@@ -69,4 +70,9 @@ public class AppConfiguration {
|
||||
return new LoginAccountProcessor(accountReader, passwordEncrypter);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AccountPrincipal accountPrincipal(AccountReader accountReader) {
|
||||
return new AccountPrincipal(accountReader);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import javax.validation.Valid;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -35,4 +37,16 @@ public final class AccountQueryApi {
|
||||
LoginSessionUtils.setLoginAccountEmail(request.getEmail());
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@GetMapping("/api/accounts/me")
|
||||
public ResponseEntity<AccountResponse> getAccount(
|
||||
@LoginAccount GetSessionAccountCommand request) {
|
||||
return ResponseEntity.ok(accountFacade.getSessionAccount(request));
|
||||
}
|
||||
|
||||
// LoginAccountMethodArgumentResolver 테스트를 위해 임시로 만든 @ExceptionHandler
|
||||
@ExceptionHandler
|
||||
public ResponseEntity<Void> defaultException(IllegalStateException e) {
|
||||
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.yam.app.account.presentation;
|
||||
|
||||
import javax.validation.constraints.Email;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public final class GetSessionAccountCommand {
|
||||
|
||||
@NotBlank
|
||||
@Email
|
||||
private String email;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.yam.app.account.presentation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface LoginAccount {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.yam.app.account.presentation;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
@Component
|
||||
public final class LoginAccountMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return parameter.hasParameterAnnotation(LoginAccount.class)
|
||||
&& GetSessionAccountCommand.class.isAssignableFrom(parameter.getParameterType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
|
||||
HttpSession session = ((HttpServletRequest) webRequest.getNativeRequest())
|
||||
.getSession(false);
|
||||
|
||||
if (session == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
var command = new GetSessionAccountCommand();
|
||||
command.setEmail((String) session.getAttribute(LoginSessionUtils.LOGIN_ACCOUNT_EMAIL));
|
||||
return command;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.yam.app.account.presentation;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class WebConfiguration implements WebMvcConfigurer {
|
||||
|
||||
private final LoginAccountMethodArgumentResolver loginAccountMethodArgumentResolver;
|
||||
|
||||
public WebConfiguration(
|
||||
LoginAccountMethodArgumentResolver loginAccountMethodArgumentResolver) {
|
||||
this.loginAccountMethodArgumentResolver = loginAccountMethodArgumentResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
|
||||
resolvers.add(loginAccountMethodArgumentResolver);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -23,4 +23,7 @@ alter table account
|
||||
insert into account(email, email_check_token, email_check_token_generated_at, email_verified,
|
||||
joined_at, last_modified_at, nickname, password, withdraw, role)
|
||||
values ('jiwonDev@gmail.com', 'emailchecktoken', now(), false, now(), now(), 'jiwon', 'password!',
|
||||
false, 'DEFAULT'),
|
||||
('loginCheck@gmail.com', 'emailchecktoken1', now(), true, now(), now(), 'loginCheck',
|
||||
'$2a$10$EqbMbYB0vcZnuA5CClqa9uiLDnjA6pCjxn208ZchzA2q3ofqnkhcq',
|
||||
false, 'DEFAULT');
|
||||
|
||||
@@ -8,13 +8,18 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.yam.app.account.presentation.LoginAccountRequestCommand;
|
||||
import com.yam.app.account.presentation.LoginSessionUtils;
|
||||
import com.yam.app.account.presentation.RegisterAccountRequestCommand;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockHttpSession;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
@@ -24,12 +29,23 @@ import org.springframework.test.web.servlet.MockMvc;
|
||||
@ActiveProfiles("test")
|
||||
final class AccountIntegrationTests {
|
||||
|
||||
private MockHttpSession session;
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
session = new MockHttpSession();
|
||||
session.setAttribute(LoginSessionUtils.LOGIN_ACCOUNT_EMAIL, "loginCheck@gmail.com");
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void clean() {
|
||||
session.clearAttributes();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("새로운 계정을 등록하는 회원가입 시나리오")
|
||||
void new_account_request_in_register_correctly() throws Exception {
|
||||
@@ -73,4 +89,44 @@ final class AccountIntegrationTests {
|
||||
.andExpect(redirectedUrl("http://localhost:3000/login"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("로그인 요청을 성공하여 서버의 세션 등록을 완료하는 시나리오")
|
||||
void login_success_get_session() throws Exception {
|
||||
//Arrange
|
||||
LoginAccountRequestCommand request = new LoginAccountRequestCommand();
|
||||
request.setEmail("loginCheck@gmail.com");
|
||||
request.setPassword("password!");
|
||||
|
||||
//Act
|
||||
final var actions = mockMvc.perform(post("/api/accounts/login")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(request))
|
||||
);
|
||||
|
||||
//Assert
|
||||
actions
|
||||
.andDo(print())
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("로그인한 회원이 세션에서 자신의 정보를 받아오는 시나리오")
|
||||
void login_member_get_account_session_request() throws Exception {
|
||||
//Act
|
||||
final var actions = mockMvc.perform(get("/api/accounts/me")
|
||||
.session(session)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.contentType(MediaType.APPLICATION_JSON));
|
||||
|
||||
//Assert
|
||||
actions
|
||||
.andDo(print())
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.id").isNumber())
|
||||
.andExpect(jsonPath("$.email").isString())
|
||||
.andExpect(jsonPath("$.nickname").isString());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,12 +2,17 @@ package com.yam.app.account.presentation;
|
||||
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.yam.app.account.application.AccountFacade;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -18,6 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockHttpSession;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
@DisplayName("Account Query HTTP API")
|
||||
@@ -35,6 +41,58 @@ class AccountQueryApiTest {
|
||||
@DisplayName("Login HTTP API")
|
||||
class LoginApi {
|
||||
|
||||
private final String loggedInEmail = "loginCheck@gmail.com";
|
||||
private MockHttpSession session;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
session = new MockHttpSession();
|
||||
session.setAttribute(LoginSessionUtils.LOGIN_ACCOUNT_EMAIL, loggedInEmail);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void clean() {
|
||||
session.clearAttributes();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("세션이 없는 상태로 Account 정보를 요청하면 401 에러를 반환한다.")
|
||||
void no_current_session_account_request() throws Exception {
|
||||
//Act
|
||||
final var actions = mockMvc.perform(get("/api/accounts/me")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.contentType(MediaType.APPLICATION_JSON));
|
||||
|
||||
//Assert
|
||||
actions
|
||||
.andDo(print())
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("세션이 있는 상태로 Account 정보를 요청하면 200과 사용자 정보를 반환한다.")
|
||||
void no_current_session_account_reqsuest() throws Exception {
|
||||
//Arrange
|
||||
var command = new GetSessionAccountCommand();
|
||||
command.setEmail(loggedInEmail);
|
||||
when(accountFacade.getSessionAccount(command)).thenReturn(
|
||||
new AccountResponse(1L, loggedInEmail, "hello"));
|
||||
|
||||
//Act
|
||||
final var actions = mockMvc.perform(get("/api/accounts/me")
|
||||
.session(session)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.contentType(MediaType.APPLICATION_JSON));
|
||||
|
||||
//Assert
|
||||
actions
|
||||
.andDo(print())
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.id").isNumber())
|
||||
.andExpect(jsonPath("$.email").isString())
|
||||
.andExpect(jsonPath("$.nickname").isString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("정상적인 이메일과 비밀번호를 보내 로그인에 성공하고 200을 반환한다.")
|
||||
void login_success() throws Exception {
|
||||
@@ -45,7 +103,7 @@ class AccountQueryApiTest {
|
||||
doNothing().when(accountFacade).login(request);
|
||||
|
||||
//Act
|
||||
var actions = mockMvc.perform(post("/api/accounts/login")
|
||||
final var actions = mockMvc.perform(post("/api/accounts/login")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(request))
|
||||
@@ -135,7 +193,7 @@ class AccountQueryApiTest {
|
||||
request.setPassword(args);
|
||||
|
||||
//Act
|
||||
var actions = mockMvc.perform(post("/api/accounts/login")
|
||||
final var actions = mockMvc.perform(post("/api/accounts/login")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(request))
|
||||
|
||||
Reference in New Issue
Block a user