Skip to content

Commit

Permalink
Merge pull request #35 from this-is-spear/refactor/account
Browse files Browse the repository at this point in the history
계좌 조회를 문서화한다.
  • Loading branch information
this-is-spear committed Jan 17, 2024
2 parents 43158bc + 67d8ce6 commit 5c515a9
Show file tree
Hide file tree
Showing 12 changed files with 128 additions and 62 deletions.
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,13 @@ jacocoTestCoverageVerification {
limit {
counter = 'LINE'
value = 'COVEREDRATIO'
minimum = 0.80
minimum = 0.50
}

limit {
counter = 'BRANCH'
value = 'COVEREDRATIO'
minimum = 0.80
minimum = 0.50
}

excludes = [
Expand Down
40 changes: 30 additions & 10 deletions src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,36 @@ endif::[]

== Banking API

=== 계좌 개설

==== HTTP request

include::{snippets}/create-account/http-request.adoc[]

==== HTTP response

include::{snippets}/create-account/http-response.adoc[]

=== 계좌 조회

==== HTTP request

include::{snippets}/accounts/http-request.adoc[]

==== HTTP response

include::{snippets}/accounts/http-response.adoc[]

=== 이체 대상 확인

==== HTTP request

include::{snippets}/targets/http-request.adoc[]

==== HTTP response

include::{snippets}/targets/http-response.adoc[]

=== 기록 조회

==== HTTP request
Expand Down Expand Up @@ -52,16 +82,6 @@ include::{snippets}/transfer/http-request.adoc[]

include::{snippets}/transfer/http-response.adoc[]

=== 이체 대상 확인

==== HTTP request

include::{snippets}/targets/http-request.adoc[]

==== HTTP response

include::{snippets}/targets/http-response.adoc[]

== Member API

=== 회원가입
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,8 @@ private void validateMember(String principal, Account account) {
}
}

public TargetResponses getTargets(String principal, String stringAccountNumber) {
final var accountNumber = new AccountNumber(stringAccountNumber);
final var account = accountService.getAccountByAccountNumber(accountNumber);

public TargetResponses getTargets(String principal) {
final var member = memberService.findByEmail(principal);
if (!member.getId().equals(account.getUserId())) {
throw new InvalidMemberException();
}

final var friendIds = friendService.findFriends(member.getId())
.stream()
.map(Friend::getToMemberId)
Expand Down Expand Up @@ -137,4 +130,9 @@ public List<AccountNumber> findAccounts(String principal) {
.map(Account::getAccountNumber)
.toList();
}

public AccountNumber createAccount(String username) {
final var member = memberService.findByEmail(username);
return accountService.createAccount(member.getId()).getAccountNumber();
}
}
25 changes: 21 additions & 4 deletions src/main/java/bankingapi/banking/domain/AccountService.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,27 @@ public List<AccountHistory> findAccountHistoriesByFromAccountNumber(Account acco
return accountHistoryRepository.findByFromAccountNumber(account.getAccountNumber());
}


public List<Account> getAccountByMemberId(Long memberId) {
return accountRepository.findByUserId(memberId);
}

public Account createAccount(Long id) {
AccountNumber accountNumber;

do {
accountNumber = AccountNumberGenerator.generate();
} while (accountRepository.findByAccountNumber(accountNumber).isPresent());

return accountRepository.save(
Account.builder()
.accountNumber(accountNumber)
.balance(Money.zero())
.userId(id)
.build()
);
}

private Account getAccountByAccountNumberWithOptimisticLock(AccountNumber accountNumber) {
return accountRepository.findByAccountNumberWithOptimisticLock(accountNumber).orElseThrow();
}
Expand All @@ -99,8 +120,4 @@ private void recordCompletionTransferMoney(Account fromAccount, Account toAccoun
accountHistoryRepository.save(AccountHistory.recordWithdrawHistory(fromAccount, toAccount, money));
accountHistoryRepository.save(AccountHistory.recordDepositHistory(toAccount, fromAccount, money));
}

public List<Account> getAccountByMemberId(Long memberId) {
return accountRepository.findByUserId(memberId);
}
}
17 changes: 11 additions & 6 deletions src/main/java/bankingapi/banking/ui/AccountController.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import java.util.List;

@RestController
@RequestMapping("account")
@RequestMapping("/accounts")
@RequiredArgsConstructor
public class AccountController {

Expand Down Expand Up @@ -69,19 +69,24 @@ public ResponseEntity<Void> transferMoney(@AuthenticationPrincipal UserDetails p
}

@GetMapping(
value = "/{accountNumber}/transfer/targets",
value = "/transfer/targets",
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<TargetResponses> getTargets(@AuthenticationPrincipal UserDetails principal,
@PathVariable String accountNumber) {
return ResponseEntity.ok(accountApplicationService.getTargets(principal.getUsername(), accountNumber));
public ResponseEntity<TargetResponses> getTargets(@AuthenticationPrincipal UserDetails principal) {
return ResponseEntity.ok(accountApplicationService.getTargets(principal.getUsername()));
}

@GetMapping(
value = "/{accountNumber}/targets",
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<List<AccountNumber>> findAccounts(@AuthenticationPrincipal UserDetails principal) {
return ResponseEntity.ok(accountApplicationService.findAccounts(principal.getUsername()));
}

@PostMapping(
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<AccountNumber> createAccount(@AuthenticationPrincipal UserDetails principal) {
return ResponseEntity.ok(accountApplicationService.createAccount(principal.getUsername()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
Expand Down Expand Up @@ -34,7 +35,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
.requestMatchers("/docs/index.html").permitAll()
.requestMatchers("/members/register").anonymous()
.requestMatchers("/login").anonymous()
.requestMatchers("/account/**").authenticated()
.requestMatchers("/accounts/**").authenticated()
.requestMatchers("/members/**").authenticated()
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class WebMvcConfiguration implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
PathMatcherInterceptor matcherInterceptor = new PathMatcherInterceptor(idempotentRequestInterceptor,
customPathContainer).includePathPattern("/account/**", HttpMethod.POST);
customPathContainer).includePathPattern("/accounts/**", HttpMethod.POST);
registry.addInterceptor(matcherInterceptor)
.addPathPatterns();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ void withdraw_concurrency_10times() throws Exception {
transferParams.put("amount", transferMoney);

return mockMvc.perform(
post("/account/{accountNumber}/transfer", fromAccountNumber)
post("/accounts/{accountNumber}/transfer", fromAccountNumber)
.with(user(username).password(password).roles("MEMBER"))
.contentType(MediaType.APPLICATION_JSON)
.header(IDEMPOTENT_KEY, UUID.randomUUID().toString())
Expand All @@ -340,7 +340,7 @@ void withdraw_concurrency_10times() throws Exception {
depositParams.put("amount", depositMoney);

return mockMvc.perform(
post("/account/{accountNumber}/deposit", accountNumber)
post("/accounts/{accountNumber}/deposit", accountNumber)
.with(user(username).password(password).roles("MEMBER"))
.contentType(MediaType.APPLICATION_JSON)
.header(IDEMPOTENT_KEY, UUID.randomUUID().toString())
Expand All @@ -354,7 +354,7 @@ void withdraw_concurrency_10times() throws Exception {
depositParams.put("amount", depositMoney);

return mockMvc.perform(
post("/account/{accountNumber}/withdraw", accountNumber)
post("/accounts/{accountNumber}/withdraw", accountNumber)
.with(user(username).password(password).roles("MEMBER"))
.contentType(MediaType.APPLICATION_JSON)
.header(IDEMPOTENT_KEY, UUID.randomUUID().toString())
Expand All @@ -364,7 +364,7 @@ void withdraw_concurrency_10times() throws Exception {

private ResultActions 계좌_조회_요청(String accountNumber, String username, String password) throws Exception {
return mockMvc.perform(
get("/account/{accountNumber}/history", accountNumber)
get("/accounts/{accountNumber}/history", accountNumber)
.with(user(username).password(password).roles("MEMBER"))
.accept(MediaType.APPLICATION_JSON)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ void deposit_and_withdraw_5000() throws Exception {
depositParams.put("amount", 입금할_돈);

var 계좌_입금 = mockMvc.perform(
post("/account/{accountNumber}/deposit", 나의계좌)
post("/accounts/{accountNumber}/deposit", 나의계좌)
.with(user(이메일).password(비밀번호).roles("MEMBER"))
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(depositParams))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,27 +177,16 @@ void transfer_accessInvalidMember() {
@Test
@DisplayName("계좌 이체할 상대방을 찾는다.")
void getTargets() {
when(accountService.getAccountByAccountNumber(AccountFixture.계좌번호)).thenReturn(계좌);
when(memberService.findByEmail(EMAIL)).thenReturn(사용자);
when(friendService.findFriends(사용자_ID)).thenReturn(List.of(new Friend(사용자_ID, 상대방_ID)));
when(memberService.findAllById(List.of(상대방_ID))).thenReturn(List.of(상대방));
when(accountService.getFriendAccounts(List.of(상대방_ID))).thenReturn(List.of(상대방_계좌));

var responses = assertDoesNotThrow(
() -> accountApplicationService.getTargets(EMAIL, 계좌.getAccountNumber().getNumber()));
() -> accountApplicationService.getTargets(EMAIL));
assertThat(responses.targets()).hasSize(1);
}

@Test
@DisplayName("계좌 이체할 상대방을 찾을 때, 본인이 아니면 InvalidMemberException 예외가 발생한다.")
void getTargets_accessInvalidMember() {
when(accountService.getAccountByAccountNumber(계좌.getAccountNumber())).thenReturn(계좌);
when(memberService.findByEmail(EMAIL)).thenReturn(상대방);
assertThatThrownBy(
() -> accountApplicationService.getTargets(EMAIL, 계좌.getAccountNumber().getNumber())
).isInstanceOf(InvalidMemberException.class);
}

@Test
@DisplayName("자신의 계좌를 조회한다.")
void findAccounts() {
Expand Down
54 changes: 41 additions & 13 deletions src/test/java/bankingapi/documentation/AccountDocumentation.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import java.util.UUID;

import bankingapi.banking.domain.AccountNumber;
import org.junit.jupiter.api.Test;
import org.springframework.http.MediaType;

Expand All @@ -27,9 +28,8 @@ void getHistory() throws Exception {
when(accountApplicationService.getHistory(USER.getUsername(), 계좌_번호))
.thenReturn(계좌_내역);
mockMvc.perform(
get("/account/{accountNumber}/history", 계좌_번호)
get("/accounts/{accountNumber}/history", 계좌_번호)
.with(csrf())
.with(user(USER.getUsername()).roles("MEMBER"))
).andExpect(status().isOk())
.andDo(document(
"history",
Expand All @@ -44,12 +44,11 @@ void getHistory() throws Exception {
void deposit() throws Exception {
doNothing().when(accountApplicationService).deposit(USER.getUsername(), 계좌_번호, 이만원);
mockMvc.perform(
post("/account/{accountNumber}/deposit", 계좌_번호)
post("/accounts/{accountNumber}/deposit", 계좌_번호)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(이만원))
.header(IDEMPOTENT_KEY, UUID.randomUUID().toString())
.with(csrf())
.with(user(USER.getUsername()).roles("MEMBER"))
).andExpect(status().isOk())
.andDo(document(
"deposit",
Expand All @@ -64,12 +63,11 @@ void deposit() throws Exception {
void withdraw() throws Exception {
doNothing().when(accountApplicationService).withdraw(USER.getUsername(), 계좌_번호, 이만원);
mockMvc.perform(
post("/account/{accountNumber}/withdraw", 계좌_번호)
post("/accounts/{accountNumber}/withdraw", 계좌_번호)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(이만원))
.header(IDEMPOTENT_KEY, UUID.randomUUID().toString())
.with(csrf())
.with(user(USER.getUsername()).roles("MEMBER"))
).andExpect(status().isOk())
.andDo(document(
"withdraw",
Expand All @@ -86,12 +84,11 @@ void transfer() throws Exception {
doNothing().when(accountApplicationService).transfer(USER.getUsername(), 계좌_번호, command);

mockMvc.perform(
post("/account/{accountNumber}/transfer", 계좌_번호)
post("/accounts/{accountNumber}/transfer", 계좌_번호)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(command))
.header(IDEMPOTENT_KEY, UUID.randomUUID().toString())
.with(csrf())
.with(user(USER.getUsername()).roles("MEMBER"))
).andExpect(status().isOk())
.andDo(document(
"transfer",
Expand All @@ -104,17 +101,48 @@ void transfer() throws Exception {
@Test
@WithMockMember
void getTargets() throws Exception {
when(accountApplicationService.getTargets(USER.getUsername(), 계좌_번호)).thenReturn(타겟목록);
when(accountApplicationService.getTargets(USER.getUsername())).thenReturn(타겟목록);
mockMvc.perform(
get("/account/{accountNumber}/transfer/targets", 계좌_번호)
.with(user(USER.getUsername()).roles("MEMBER"))
get("/accounts/transfer/targets")
.with(csrf())
).andExpect(status().isOk())
.andDo(print())
.andDo(document(
"targets",
getDocumentRequest(),
getDocumentResponse(),
pathParameters(parameterWithName("accountNumber").description("사용자의 계좌 정보"))
getDocumentResponse()
));
}

@Test
@WithMockMember
void findAccounts() throws Exception {
when(accountApplicationService.findAccounts(USER.getUsername())).thenReturn(계좌목록);
mockMvc.perform(
get("/accounts")
.with(csrf())
).andExpect(status().isOk())
.andDo(print())
.andDo(document(
"accounts",
getDocumentRequest(),
getDocumentResponse()
));
}

@Test
@WithMockMember
void createAccount() throws Exception {
when(accountApplicationService.createAccount(USER.getUsername())).thenReturn(new AccountNumber(계좌_번호));
mockMvc.perform(
post("/accounts")
.with(csrf())
).andExpect(status().isOk())
.andDo(print())
.andDo(document(
"create-account",
getDocumentRequest(),
getDocumentResponse()
));
}
}
Loading

0 comments on commit 5c515a9

Please sign in to comment.