Skip to content
This repository has been archived by the owner on May 19, 2024. It is now read-only.

Commit

Permalink
[WEAV-49] 로그인 기능 애플리케이션 레이어 구현 (#19)
Browse files Browse the repository at this point in the history
Co-authored-by: 유도진 <[email protected]>
  • Loading branch information
waterfogSW and dojinyou authored Jan 26, 2024
1 parent b49a51d commit 08dd9ba
Show file tree
Hide file tree
Showing 11 changed files with 335 additions and 0 deletions.
5 changes: 5 additions & 0 deletions application/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,10 @@ bootJar.enabled = false
jar.enabled = true

dependencies {
implementation(project(":support:common"))
implementation(project(":domain"))

implementation("org.springframework.boot:spring-boot:${Version.SPRING_BOOT}")
testImplementation(testFixtures(project(":domain")))
testFixturesImplementation(testFixtures(project(":domain")))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.studentcenter.weave.application.common.config

import org.springframework.boot.context.properties.ConfigurationPropertiesScan
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration


@Configuration
@ComponentScan(basePackages = ["com.studentcenter.weave.application"])
@EnableConfigurationProperties
@ConfigurationPropertiesScan(basePackages = ["com.studentcenter.weave.application"])
class ApplicationConfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.studentcenter.weave.application.port.inbound

import com.studentcenter.weave.domain.enum.SocialLoginProvider

fun interface UserSocialLoginUseCase {

fun invoke(command: Command): Result

data class Command(
val socialLoginProvider: SocialLoginProvider,
val idToken: String,
)

sealed class Result {

data class Success(
val accessToken: String,
val refreshToken: String,
) : Result()

data class NotRegistered(
val registerToken: String,
) : Result()

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.studentcenter.weave.application.service

import com.studentcenter.weave.application.vo.UserTokenClaims
import com.studentcenter.weave.domain.entity.User
import com.studentcenter.weave.domain.enum.SocialLoginProvider
import com.studentcenter.weave.support.common.vo.Email

interface UserTokenService {

fun resolveIdToken(
idToken: String,
provider: SocialLoginProvider,
): UserTokenClaims.IdToken

fun generateRegisterToken(
email: Email,
provider: SocialLoginProvider,
): String

fun resolveRegisterToken(
registerToken: String,
): UserTokenClaims.RegisterToken

fun generateAccessToken(
user: User,
): String

fun resolveAccessToken(
accessToken: String,
): UserTokenClaims.AccessToken

fun generateRefreshToken(
user: User,
): String

fun resolveRefreshToken(
refreshToken: String,
): UserTokenClaims.RefreshToken

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.studentcenter.weave.application.service.application

import com.studentcenter.weave.application.port.inbound.UserSocialLoginUseCase
import com.studentcenter.weave.application.service.UserTokenService
import com.studentcenter.weave.application.service.domain.UserAuthInfoDomainService
import com.studentcenter.weave.application.service.domain.UserDomainService
import com.studentcenter.weave.application.vo.UserTokenClaims
import com.studentcenter.weave.domain.entity.User
import com.studentcenter.weave.domain.entity.UserAuthInfo
import org.springframework.stereotype.Service

@Service
class UserSocialLoginApplicationService(
private val userTokenService: UserTokenService,
private val userDomainService: UserDomainService,
private val userAuthInfoDomainService: UserAuthInfoDomainService,
) : UserSocialLoginUseCase {

override fun invoke(command: UserSocialLoginUseCase.Command): UserSocialLoginUseCase.Result {
val idTokenClaims: UserTokenClaims.IdToken = userTokenService.resolveIdToken(
idToken = command.idToken,
provider = command.socialLoginProvider,
)

val userAuthInfo: UserAuthInfo = userAuthInfoDomainService.findByEmail(idTokenClaims.email)
?: return UserSocialLoginUseCase.Result.NotRegistered(
registerToken = userTokenService.generateRegisterToken(
email = idTokenClaims.email,
provider = command.socialLoginProvider,
),
)

val user: User = userDomainService.getById(userAuthInfo.userId)
return UserSocialLoginUseCase.Result.Success(
accessToken = userTokenService.generateAccessToken(user),
refreshToken = userTokenService.generateRefreshToken(user)
)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.studentcenter.weave.application.service.domain

import com.studentcenter.weave.domain.entity.UserAuthInfo
import com.studentcenter.weave.support.common.vo.Email

interface UserAuthInfoDomainService {

fun findByEmail(email: Email): UserAuthInfo?

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.studentcenter.weave.application.service.domain

import com.studentcenter.weave.domain.entity.User
import java.util.UUID


interface UserDomainService {

fun getById(id: UUID): User

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.studentcenter.weave.application.vo

import com.studentcenter.weave.domain.enum.SocialLoginProvider
import com.studentcenter.weave.domain.vo.Nickname
import com.studentcenter.weave.support.common.vo.Email
import com.studentcenter.weave.support.common.vo.Url
import java.util.*

sealed class UserTokenClaims {

data class IdToken(
val nickname: Nickname,
val email: Email,
) : UserTokenClaims()

data class AccessToken(
val userId: UUID,
val nickname: Nickname,
val email: Email,
val avatar: Url?,
) : UserTokenClaims()

data class RegisterToken(
val nickname: Nickname,
val email: Email,
val socialLoginProvider: SocialLoginProvider,
) : UserTokenClaims()

data class RefreshToken(
val userId: UUID,
) : UserTokenClaims()

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.studentcenter.weave.application.service.application

import com.studentcenter.weave.application.port.inbound.UserSocialLoginUseCase
import com.studentcenter.weave.application.service.UserTokenServiceStub
import com.studentcenter.weave.application.service.domain.UserAuthInfoDomainService
import com.studentcenter.weave.application.service.domain.UserDomainServiceStub
import com.studentcenter.weave.domain.entity.UserAuthInfo
import com.studentcenter.weave.domain.entity.UserAuthInfoFixtureFactory
import com.studentcenter.weave.domain.enum.SocialLoginProvider
import io.kotest.core.spec.style.DescribeSpec
import io.kotest.matchers.types.shouldBeTypeOf
import io.mockk.every
import io.mockk.mockk

class UserSocialLoginApplicationServiceTest : DescribeSpec({

describe("UserSocialLoginApplicationService") {

val authInfoDomainServiceMock: UserAuthInfoDomainService =
mockk<UserAuthInfoDomainService>()
val userAuthInfoFixture: UserAuthInfo = UserAuthInfoFixtureFactory.create()
val sut = UserSocialLoginApplicationService(
userTokenService = UserTokenServiceStub(),
userDomainService = UserDomainServiceStub(),
userAuthInfoDomainService = authInfoDomainServiceMock,
)

context("소셜 로그인 성공시") {

every {
authInfoDomainServiceMock.findByEmail(userAuthInfoFixture.email)
} returns UserAuthInfoFixtureFactory.create()

enumValues<SocialLoginProvider>().forEach { socialLoginProvider ->

it("Access TokenRefresh Token을 응답한다 : ${socialLoginProvider.name}") {
// arrange
val idToken = "idToken"
val command = UserSocialLoginUseCase.Command(
socialLoginProvider = socialLoginProvider,
idToken = idToken,
)

// act
val result: UserSocialLoginUseCase.Result = sut.invoke(command)

// assert
result.shouldBeTypeOf<UserSocialLoginUseCase.Result.Success>()
result.accessToken.shouldBeTypeOf<String>()
result.refreshToken.shouldBeTypeOf<String>()
}
}
}

context("회원이 존재하지 않는 경우") {

every { authInfoDomainServiceMock.findByEmail(userAuthInfoFixture.email) } returns null

enumValues<SocialLoginProvider>().forEach { socialLoginProvider ->

it("회원 가입 토큰을 응답한다 : ${socialLoginProvider.name}") {
// arrange
val idToken = "idToken"
val command = UserSocialLoginUseCase.Command(
socialLoginProvider = socialLoginProvider,
idToken = idToken,
)

// act
val result: UserSocialLoginUseCase.Result = sut.invoke(command)

// assert
result.shouldBeTypeOf<UserSocialLoginUseCase.Result.NotRegistered>()
result.registerToken.shouldBeTypeOf<String>()
}
}

}

}

})
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.studentcenter.weave.application.service

import com.studentcenter.weave.application.vo.UserTokenClaims
import com.studentcenter.weave.domain.entity.User
import com.studentcenter.weave.domain.entity.UserFixtureFactory
import com.studentcenter.weave.domain.enum.SocialLoginProvider
import com.studentcenter.weave.support.common.vo.Email

class UserTokenServiceStub : UserTokenService {

private val user = UserFixtureFactory.create()

override fun resolveIdToken(
idToken: String,
provider: SocialLoginProvider
): UserTokenClaims.IdToken {
return UserTokenClaims.IdToken(
nickname = user.nickname,
email = user.email,
)
}

override fun generateRegisterToken(
email: Email,
provider: SocialLoginProvider
): String {
return "registerToken"
}

override fun resolveRegisterToken(registerToken: String): UserTokenClaims.RegisterToken {
return UserTokenClaims.RegisterToken(
nickname = user.nickname,
email = user.email,
socialLoginProvider = SocialLoginProvider.KAKAO,
)
}

override fun generateAccessToken(user: User): String {
return "accessToken"
}

override fun resolveAccessToken(accessToken: String): UserTokenClaims.AccessToken {
return UserTokenClaims.AccessToken(
userId = user.id,
nickname = user.nickname,
email = user.email,
avatar = user.avatar,
)
}

override fun generateRefreshToken(user: User): String {
return "refreshToken"
}

override fun resolveRefreshToken(refreshToken: String): UserTokenClaims.RefreshToken {
return UserTokenClaims.RefreshToken(
userId = user.id,
)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.studentcenter.weave.application.service.domain

import com.studentcenter.weave.domain.entity.User
import com.studentcenter.weave.domain.entity.UserFixtureFactory
import java.util.*

class UserDomainServiceStub: UserDomainService {

override fun getById(id: UUID): User {
return UserFixtureFactory.create()
}

}

0 comments on commit 08dd9ba

Please sign in to comment.