Skip to content

Commit

Permalink
Merge pull request #3 from ASAP-Lettering/ASAP-59
Browse files Browse the repository at this point in the history
ASAP-59 feat: 소셜 로그인, 회원 가입 api 추가 및 테스트 코드 추가
  • Loading branch information
tlarbals824 authored Aug 27, 2024
2 parents 4d58a59 + 899d81d commit 602aa99
Show file tree
Hide file tree
Showing 17 changed files with 391 additions and 6 deletions.
2 changes: 1 addition & 1 deletion .deploy/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FROM amazoncorretto:17-alpine-jdk

ARG TARGET_JAR=/api/build/libs/api.jar
ARG TARGET_JAR=/api/build/libs/app.jar

COPY ${TARGET_JAR} /app.jar

Expand Down
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Lettering Backend



## System Architecture


### Overview


```markdown
.
├── app/
│ └── domain/
│ ├── api
│ ├── controller
│ └── dto
└── core
```
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package com.asap.api
package com.asap.app

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication

@SpringBootApplication
class ApiApplication {
class AppApplication {

}

fun main(args: Array<String>) {
SpringApplication.run(ApiApplication::class.java, *args)
SpringApplication.run(AppApplication::class.java, *args)
}
51 changes: 51 additions & 0 deletions app/src/main/kotlin/com/asap/app/auth/api/AuthApi.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.asap.app.auth.api

import com.asap.app.auth.dto.SocialLoginRequest
import com.asap.app.auth.dto.SocialLoginResponse
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.media.Content
import io.swagger.v3.oas.annotations.media.Schema
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.responses.ApiResponses
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping

@Tag(name = "Auth", description = "Auth API")
@RequestMapping("/api/v1/auth")
interface AuthApi {

@Operation(summary = "소셜 로그인")
@PostMapping("/login/{provider}")
@ApiResponses(
value = [
ApiResponse(
responseCode = "200",
description = "기존 회원 로그인 성공",
content = [
Content(
mediaType = "application/json",
schema = Schema(implementation = SocialLoginResponse.Success::class)
)
]
),
ApiResponse(
responseCode = "401",
description = "신규 회원 가입 필요",
content = [
Content(
mediaType = "application/json",
schema = Schema(implementation = SocialLoginResponse.NonRegistered::class)
)
]
)
]
)
fun socialLogin(
@PathVariable provider: String,
@RequestBody request: SocialLoginRequest
): ResponseEntity<SocialLoginResponse>
}
28 changes: 28 additions & 0 deletions app/src/main/kotlin/com/asap/app/auth/controller/AuthController.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.asap.app.auth.controller

import com.asap.app.auth.api.AuthApi
import com.asap.app.auth.dto.SocialLoginRequest
import com.asap.app.auth.dto.SocialLoginResponse
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.RestController

@RestController
class AuthController(
) : AuthApi {

override fun socialLogin(
provider: String,
request: SocialLoginRequest
): ResponseEntity<SocialLoginResponse> {
when (request.accessToken) {
"nonRegistered" -> return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(SocialLoginResponse.NonRegistered("registerToken"))

"registered" -> return ResponseEntity
.ok(SocialLoginResponse.Success("accessToken", "refreshToken"))

else -> return ResponseEntity.badRequest().build()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.asap.app.auth.dto

import io.swagger.v3.oas.annotations.media.Schema

@Schema(description = "소셜 로그인 요청")
data class SocialLoginRequest(
@Schema(description = "oauth access token")
val accessToken: String,
)
28 changes: 28 additions & 0 deletions app/src/main/kotlin/com/asap/app/auth/dto/SocialLoginResponse.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.asap.app.auth.dto

import io.swagger.v3.oas.annotations.media.Schema

@Schema(
description = "소셜 로그인 응답",
oneOf = [
SocialLoginResponse.Success::class,
SocialLoginResponse.NonRegistered::class
]
)
sealed class SocialLoginResponse{

@Schema(description = "기존 회원 로그인 성공")
data class Success(
@Schema(description = "access token")
val accessToken: String,
@Schema(description = "refresh token")
val refreshToken: String
) : SocialLoginResponse()


@Schema(description = "신규 회원 가입 필요")
data class NonRegistered(
@Schema(description = "register token, 회원가입을 위한 토큰")
val registerToken: String
) : SocialLoginResponse()
}
39 changes: 39 additions & 0 deletions app/src/main/kotlin/com/asap/app/user/api/UserApi.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.asap.app.user.api

import com.asap.app.user.dto.RegisterUserRequest
import com.asap.app.user.dto.RegisterUserResponse
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.media.Content
import io.swagger.v3.oas.annotations.media.Schema
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.responses.ApiResponses
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping

@Tag(name = "User", description = "User API")
@RequestMapping("/api/v1/users")
interface UserApi {

@Operation(summary = "회원 가입")
@PostMapping()
@ApiResponses(
value = [
ApiResponse(
responseCode = "200",
description = "회원 가입 성공",
content = [
Content(
mediaType = "application/json",
schema = Schema(implementation = RegisterUserResponse::class)
)
]
)
]
)
fun registerUser(
@RequestBody request: RegisterUserRequest
): ResponseEntity<RegisterUserResponse>
}
20 changes: 20 additions & 0 deletions app/src/main/kotlin/com/asap/app/user/controller/UserController.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.asap.app.user.controller

import com.asap.app.user.api.UserApi
import com.asap.app.user.dto.RegisterUserRequest
import com.asap.app.user.dto.RegisterUserResponse
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.RestController

@RestController
class UserController(

) : UserApi{

override fun registerUser(request: RegisterUserRequest): ResponseEntity<RegisterUserResponse> {
when(request.registerToken){
"register" -> return ResponseEntity.ok(RegisterUserResponse("accessToken", "refreshToken"))
else -> return ResponseEntity.badRequest().build()
}
}
}
19 changes: 19 additions & 0 deletions app/src/main/kotlin/com/asap/app/user/dto/RegisterUserRequest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.asap.app.user.dto

import io.swagger.v3.oas.annotations.media.Schema
import java.time.LocalDate

@Schema(description = "회원 가입 요청")
data class RegisterUserRequest(
@Schema(description = "register_token, 소셜 로그인이로부터 전달받은 토큰")
val registerToken: String,
@Schema(description = "서비스 이용약관 동의")
val servicePermission: Boolean,
@Schema(description = "개인정보 수집 및 이용 동의")
val privatePermission: Boolean,
@Schema(description = "마케팅 정보 수신 동의")
val marketingPermission: Boolean,
@Schema(description = "생년 월일, yyyy-MM-dd, 값이 안넘어올 수 있음")
val birthday: LocalDate?
) {
}
12 changes: 12 additions & 0 deletions app/src/main/kotlin/com/asap/app/user/dto/RegisterUserResponse.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.asap.app.user.dto

import io.swagger.v3.oas.annotations.media.Schema

@Schema(description = "회원 가입 응답")
data class RegisterUserResponse(
@Schema(description = "access token")
val accessToken: String,
@Schema(description = "refresh token")
val refreshToken: String
) {
}
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.asap.app.auth.controller

import com.asap.app.auth.dto.SocialLoginRequest
import com.fasterxml.jackson.databind.ObjectMapper
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.autoconfigure.web.servlet.WebMvcTest
import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.post

@WebMvcTest(AuthController::class)
@AutoConfigureMockMvc
class AuthControllerTest {

@Autowired
private lateinit var mockMvc: MockMvc

private val objectMapper: ObjectMapper = ObjectMapper()


@Test
fun socialLoginSuccessTest(){
// given
val request = SocialLoginRequest("registered")
// when
val response = mockMvc.post("/api/v1/auth/login/{provider}", "kakao") {
contentType = MediaType.APPLICATION_JSON
content = objectMapper.writeValueAsString(request)
}

// then
response.andExpect {
status { isOk() }
jsonPath("$.accessToken") {
exists()
isString()
isNotEmpty()
}
jsonPath("$.refreshToken") {
exists()
isString()
isNotEmpty()
}
}
}

@Test
fun socialLoginNonRegisteredTest(){
// given
val request = SocialLoginRequest("nonRegistered")
// when
val response = mockMvc.post("/api/v1/auth/login/{provider}", "kakao") {
contentType = MediaType.APPLICATION_JSON
content = objectMapper.writeValueAsString(request)
}

// then
response.andExpect {
status { isUnauthorized() }
jsonPath("$.registerToken") {
exists()
isString()
isNotEmpty()
}
}
}


@Test
fun socialLoginBadRequestTest(){
// given
val request = SocialLoginRequest("invalid")
// when
val response = mockMvc.post("/api/v1/auth/login/{provider}", "kakao") {
contentType = MediaType.APPLICATION_JSON
content = objectMapper.writeValueAsString(request)
}

// then
response.andExpect {
status { isBadRequest() }
}
}


}
Loading

0 comments on commit 602aa99

Please sign in to comment.