Skip to content

Commit

Permalink
refactor #77 : migrate checkUsernameAPI's module
Browse files Browse the repository at this point in the history
- Moved checkUsernameAPI logic and tests from user domain to auth domain.
- Updated endpoint.
  • Loading branch information
seonpilKim committed Oct 9, 2024
1 parent 8418b72 commit 3407379
Show file tree
Hide file tree
Showing 20 changed files with 417 additions and 55 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.titi.titi_user.adapter.in.web.api;
package com.titi.titi_auth.adapter.in.web.api;

import org.hibernate.validator.constraints.Length;
import org.springframework.http.MediaType;
Expand All @@ -16,13 +16,13 @@
import lombok.Builder;
import lombok.RequiredArgsConstructor;

import com.titi.titi_user.application.port.in.CheckUsernameUseCase;
import com.titi.titi_user.common.TiTiUserBusinessCodes;
import com.titi.titi_auth.application.port.in.CheckUsernameUseCase;
import com.titi.titi_auth.common.TiTiAuthBusinessCodes;

@Validated
@RestController
@RequiredArgsConstructor
class CheckUsernameController implements UserApi {
public class CheckUsernameController implements AuthApi {

private final CheckUsernameUseCase checkUsernameUseCase;

Expand All @@ -31,7 +31,7 @@ class CheckUsernameController implements UserApi {
@GetMapping(value = "/members/check", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<CheckUsernameResponseBody> checkUsername(@NotBlank @Email @Length(max = 30) @RequestParam String username) {
final CheckUsernameUseCase.Result result = this.checkUsernameUseCase.invoke(CheckUsernameUseCase.Command.builder().username(username).build());
final TiTiUserBusinessCodes businessCodes = result.isPresent() ? TiTiUserBusinessCodes.ALREADY_EXISTS_USERNAME : TiTiUserBusinessCodes.DOES_NOT_EXIST_USERNAME;
final TiTiAuthBusinessCodes businessCodes = result.isPresent() ? TiTiAuthBusinessCodes.ALREADY_EXISTS_USERNAME : TiTiAuthBusinessCodes.DOES_NOT_EXIST_USERNAME;
return ResponseEntity.status(businessCodes.getStatus())
.body(
CheckUsernameResponseBody.builder()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.titi.titi_auth.adapter.out.persistence;

import java.util.Optional;

import org.springframework.stereotype.Component;

import lombok.RequiredArgsConstructor;

import com.titi.titi_auth.application.port.out.persistence.FindAccountPort;
import com.titi.titi_auth.data.jpa.entity.mapper.EntityMapper;
import com.titi.titi_auth.data.jpa.repository.AccountEntityRepository;
import com.titi.titi_auth.domain.Account;

@Component
@RequiredArgsConstructor
public class FindAccountPortAdapter implements FindAccountPort {

private final AccountEntityRepository accountEntityRepository;

@Override
public Optional<Account> invoke(Account account) {
return this.accountEntityRepository.findByEntity(EntityMapper.INSTANCE.toEntity(account))
.map(EntityMapper.INSTANCE::toDomain);
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.titi.titi_user.application.port.in;
package com.titi.titi_auth.application.port.in;

import lombok.Builder;

Expand All @@ -20,4 +20,4 @@ record Result(

}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.titi.titi_auth.application.port.out.persistence;

import java.util.Optional;

import com.titi.titi_auth.domain.Account;

public interface FindAccountPort {

Optional<Account> invoke(Account account);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.titi.titi_auth.application.service;

import org.springframework.stereotype.Service;

import lombok.RequiredArgsConstructor;

import com.titi.titi_auth.application.port.in.CheckUsernameUseCase;
import com.titi.titi_auth.application.port.out.persistence.FindAccountPort;
import com.titi.titi_auth.domain.Account;

@Service
@RequiredArgsConstructor
public class CheckUsernameService implements CheckUsernameUseCase {

private final FindAccountPort findAccountPort;

@Override
public Result invoke(Command command) {
final Account account = Account.builder().username(command.username()).build();
return CheckUsernameUseCase.Result.builder()
.isPresent(this.findAccountPort.invoke(account).isPresent())
.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public enum TiTiAuthBusinessCodes {
MISMATCHED_AUTH_CODE(200, "AU1003", "The authentication code does not match."),
REISSUE_ACCESS_TOKEN_SUCCESS(200, "AU1004", "Successfully reissued the Access Token."),
REISSUE_ACCESS_TOKEN_FAILURE_INVALID_REFRESH_TOKEN(200, "AU1005", "The Refresh Token is invalid, so the reissuance of the Access Token has failed."),
DOES_NOT_EXIST_USERNAME(200, "AU1006", "The username does not exist."),
ALREADY_EXISTS_USERNAME(200, "AU1007", "The username already exists."),

GENERATE_AUTH_CODE_FAILURE(500, "AU7000", "Failed to generate and transmit the authentication code. Please try again later."),
VERIFY_AUTH_CODE_FAILURE(500, "AU7001", "Authentication code verification failed. Please try again later."),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.titi.titi_auth.data.jpa.entity;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import com.titi.infrastructure.persistence.jpa.entity.BaseEntity;
import com.titi.titi_auth.domain.AccountStatus;
import com.titi.titi_auth.domain.Authority;

@Entity(name = "accounts")
@Getter
@Builder(toBuilder = true)
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class AccountEntity extends BaseEntity {

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

@Column(nullable = false)
private String username;

@Column(nullable = false)
private String password;

@Enumerated(value = EnumType.STRING)
@Column(nullable = false)
private Authority authority;

@Enumerated(value = EnumType.STRING)
@Column(nullable = false)
private AccountStatus accountStatus;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.titi.titi_auth.data.jpa.entity.mapper;

import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

import com.titi.titi_auth.data.jpa.entity.AccountEntity;
import com.titi.titi_auth.domain.Account;

@Mapper
public interface EntityMapper {

EntityMapper INSTANCE = Mappers.getMapper(EntityMapper.class);

AccountEntity toEntity(Account account);

Account toDomain(AccountEntity accountEntity);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.titi.titi_auth.data.jpa.repository;

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

import com.titi.titi_auth.data.jpa.entity.AccountEntity;
import com.titi.titi_auth.data.jpa.repository.querydsl.AccountEntityQuerydsl;

public interface AccountEntityRepository extends JpaRepository<AccountEntity, Long>, AccountEntityQuerydsl {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.titi.titi_auth.data.jpa.repository.querydsl;

import java.util.Optional;

import com.titi.titi_auth.data.jpa.entity.AccountEntity;

public interface AccountEntityQuerydsl {

Optional<AccountEntity> findByEntity(AccountEntity accountEntity);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.titi.titi_auth.data.jpa.repository.querydsl;

import static com.titi.titi_auth.data.jpa.entity.QAccountEntity.*;

import java.util.Optional;

import org.springframework.stereotype.Repository;

import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;

import com.titi.titi_auth.data.jpa.entity.AccountEntity;

@Repository
@RequiredArgsConstructor
public class AccountEntityQuerydslImpl implements AccountEntityQuerydsl {

private final JPAQueryFactory jpaQueryFactory;

@Override
public Optional<AccountEntity> findByEntity(AccountEntity entity) {
return Optional.ofNullable(
jpaQueryFactory
.selectFrom(accountEntity)
.where(
idEq(entity),
usernameEq(entity),
authorityEq(entity),
accountStatusEq(entity)
)
.fetchOne()
);
}

private BooleanExpression idEq(AccountEntity entity) {
return entity.getId() != null ? accountEntity.id.eq(entity.getId()) : null;
}

private BooleanExpression usernameEq(AccountEntity entity) {
return entity.getUsername() != null ? accountEntity.username.eq(entity.getUsername()) : null;
}

private BooleanExpression authorityEq(AccountEntity entity) {
return entity.getAuthority() != null ? accountEntity.authority.eq(entity.getAuthority()) : null;
}

private BooleanExpression accountStatusEq(AccountEntity entity) {
return entity.getAccountStatus() != null ? accountEntity.accountStatus.eq(entity.getAccountStatus()) : null;
}

}
14 changes: 14 additions & 0 deletions src/main/java/com/titi/titi_auth/domain/Account.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.titi.titi_auth.domain;

import lombok.Builder;

@Builder
public record Account(
Long id,
String username,
String password,
Authority authority,
AccountStatus accountStatus
) {

}
33 changes: 33 additions & 0 deletions src/main/java/com/titi/titi_auth/domain/AccountStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.titi.titi_auth.domain;

public enum AccountStatus {
/**
* The account is in a normal and usable state. <br/>
* The account owner can log in and access the application.
*/
ACTIVATED,
/**
* An account exists but is in an unusable state. <br/>
* This could be a temporary condition, or it might occur when the account owner deactivates the account without deleting it. <br/>
* Inactive accounts are typically eligible for reactivation.
*/
DEACTIVATED,
/**
* The account is temporarily suspended. <br/>
* This can occur due to security issues, payment problems, or transitions to a dormant state. <br/>
* In this state, the account owner cannot access the application.
*/
SUSPENDED,
/**
* The account is permanently suspended. <br/>
* This can occur due to violations of the terms of service, among other reasons. <br/>
* In this state, the account owner cannot access the application.
*/
BLOCKED,
/**
* The account has been permanently deleted. <br/>
* This typically occurs when the account owner initiates the deletion or when an administrator removes the account. <br/>
* Deleted accounts cannot be reactivated.
*/
DELETED
}
5 changes: 5 additions & 0 deletions src/main/java/com/titi/titi_auth/domain/Authority.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.titi.titi_auth.domain;

public enum Authority {
MEMBER, ADMIN
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
@AllArgsConstructor
public enum TiTiUserBusinessCodes {

DOES_NOT_EXIST_USERNAME(200, "USR1000", "The username does not exist."),
ALREADY_EXISTS_USERNAME(200, "USR1001", "The username already exists."),
REGISTER_MEMBER_SUCCESS(200, "USR1002", "Successfully completed the regular membership registration."),
REGISTER_MEMBER_FAILURE_INVALID_AUTH_TOKEN(200, "USR1003", "The registration has failed due to an invalid Auth Token."),
REGISTER_MEMBER_FAILURE_ALREADY_EXISTS_USERNAME(200, "USR1004", "The registration has failed as the username already exists."),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.titi.titi_user.adapter.in.web.api;
package com.titi.titi_auth.adapter.in.web.api;

import static org.mockito.ArgumentMatchers.*;
import static org.mockito.BDDMockito.*;
Expand All @@ -19,8 +19,8 @@
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;

import com.titi.titi_user.application.port.in.CheckUsernameUseCase;
import com.titi.titi_user.common.TiTiUserBusinessCodes;
import com.titi.titi_auth.application.port.in.CheckUsernameUseCase;
import com.titi.titi_auth.common.TiTiAuthBusinessCodes;

@WebMvcTest(controllers = CheckUsernameController.class)
class CheckUsernameControllerTest {
Expand All @@ -33,13 +33,13 @@ class CheckUsernameControllerTest {

private static Stream<Arguments> whenSuccessToCheckUsername() {
return Stream.of(
Arguments.of(false, TiTiUserBusinessCodes.DOES_NOT_EXIST_USERNAME),
Arguments.of(true, TiTiUserBusinessCodes.ALREADY_EXISTS_USERNAME)
Arguments.of(false, TiTiAuthBusinessCodes.DOES_NOT_EXIST_USERNAME),
Arguments.of(true, TiTiAuthBusinessCodes.ALREADY_EXISTS_USERNAME)
);
}

private ResultActions mockCheckUsername(String username) throws Exception {
return mockMvc.perform(get("/api/user/members/check")
return mockMvc.perform(get("/api/auth/members/check")
.queryParam("username", username)
.accept(MediaType.APPLICATION_JSON)
.with(csrf()));
Expand All @@ -48,7 +48,7 @@ private ResultActions mockCheckUsername(String username) throws Exception {
@ParameterizedTest
@MethodSource
@WithMockUser
void whenSuccessToCheckUsername(boolean isPresent, TiTiUserBusinessCodes businessCodes) throws Exception {
void whenSuccessToCheckUsername(boolean isPresent, TiTiAuthBusinessCodes businessCodes) throws Exception {
// given
given(checkUsernameUseCase.invoke(any())).willReturn(CheckUsernameUseCase.Result.builder().isPresent(isPresent).build());
final String username = "[email protected]";
Expand Down
Loading

0 comments on commit 3407379

Please sign in to comment.