Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: API 응답에 status 추가 (#84) #85

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions src/main/kotlin/kr/galaxyhub/sc/api/common/ApiResponse.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,27 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include

@JsonInclude(Include.NON_NULL)
class ApiResponse<T>(
val status: Int,
val message: String? = null,
val data: T,
val data: T?,
) {

companion object {

fun error(message: String): ApiResponse<Unit> {
return ApiResponse(message, Unit)
fun error(statusCode: Int, message: String): ApiResponse<Unit> {
return ApiResponse(status = statusCode, message = message, data = Unit)
}

fun <T> success(data: T): ApiResponse<T> {
return ApiResponse(data = data)
fun <T> ok(data: T): ApiResponse<T> {
return ApiResponse(status = 200, message = null, data = data)
}

fun <T> created(data: T): ApiResponse<T> {
return ApiResponse(status = 201, message = null, data = data)
}

fun <T> noContent(data: T): ApiResponse<T> {
return ApiResponse(status = 204, message = null, data = data)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ class ExceptionHandler : ResponseEntityExceptionHandler() {
status: HttpStatusCode,
request: WebRequest,
): ResponseEntity<Any> {
// 누락된 쿼리 파라미터 값 때문에 기본 생성자 사용
return ResponseEntity(
ApiResponse("쿼리 파라미터에 누락된 값이 있습니다.", ex.missingParameters()),
ApiResponse(400, "쿼리 파라미터에 누락된 값이 있습니다.", ex.missingParameters()),
HttpStatus.BAD_REQUEST
)
}
Expand All @@ -42,7 +43,7 @@ class ExceptionHandler : ResponseEntityExceptionHandler() {
request: WebRequest,
): ResponseEntity<Any> {
return ResponseEntity(
ApiResponse("${ex.propertyName}에 잘못된 값이 입력 되었습니다.", "${ex.propertyName},${ex.value}"),
ApiResponse(400, "${ex.propertyName}에 잘못된 값이 입력 되었습니다.", "${ex.propertyName},${ex.value}"),
HttpStatus.BAD_REQUEST
)
}
Expand All @@ -53,7 +54,7 @@ class ExceptionHandler : ResponseEntityExceptionHandler() {
request: HttpServletRequest,
): ResponseEntity<ApiResponse<Unit>> {
log.debug(e) { "[🟢DEBUG] - (${request.method} ${request.requestURI})" }
return ResponseEntity(ApiResponse.error(e.message!!), e.httpStatus)
return ResponseEntity(ApiResponse.error(e.httpStatus.value(), e.message!!), e.httpStatus)
}

@ExceptionHandler(Exception::class)
Expand All @@ -64,7 +65,7 @@ class ExceptionHandler : ResponseEntityExceptionHandler() {

companion object {

private val DEFAULT_ERROR = ApiResponse.error("서버 내부에 알 수 없는 문제가 발생했습니다.")
private val DEFAULT_ERROR = ApiResponse.error(500, "서버 내부에 알 수 없는 문제가 발생했습니다.")
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ class OAuth2ControllerV1(
): ResponseEntity<ApiResponse<LoginResponse>> {
val response = oAuth2FacadeService.login(code, socialType)
return ResponseEntity.ok()
.body(ApiResponse.success(response))
.body(ApiResponse.ok(response))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ class CrawlerControllerV1(
): ResponseEntity<ApiResponse<UUID>> {
val newsId = crawlerCommandService.crawling(request.url)
return ResponseEntity.created("/api/v1/news/${newsId}".toUri())
.body(ApiResponse.success(newsId))
.body(ApiResponse.created(newsId))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class NewsControllerV1(
fun findAll(): ResponseEntity<ApiResponse<List<NewsResponse>>> {
val response = newsQueryService.findAll()
return ResponseEntity.ok()
.body(ApiResponse.success(response))
.body(ApiResponse.ok(response))
}

@GetMapping("/{newsId}")
Expand All @@ -40,7 +40,7 @@ class NewsControllerV1(
): ResponseEntity<ApiResponse<NewsDetailResponse>> {
val response = newsQueryService.getDetailByIdAndLanguage(newsId, language)
return ResponseEntity.ok()
.body(ApiResponse.success(response))
.body(ApiResponse.ok(response))
}

@PostMapping
Expand All @@ -49,7 +49,7 @@ class NewsControllerV1(
): ResponseEntity<ApiResponse<UUID>> {
val newsId = newsCommandService.create(request.toCommand())
return ResponseEntity.created("/api/v1/news/${newsId}".toUri())
.body(ApiResponse.success(newsId))
.body(ApiResponse.created(newsId))
}

@PostMapping("/{newsId}/content")
Expand All @@ -59,6 +59,6 @@ class NewsControllerV1(
): ResponseEntity<ApiResponse<Unit>> {
newsCommandService.updateContent(newsId, request.toCommand())
return ResponseEntity.ok()
.body(ApiResponse.success(Unit))
.body(ApiResponse.ok(Unit))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class TranslationControllerV1(
val command = request.toCommand(newsId)
val translateProgressionId = translationCommandService.translate(command)
return ResponseEntity.created("/api/v1/translation/${translateProgressionId}".toUri())
.body(ApiResponse.success(translateProgressionId))
.body(ApiResponse.created(translateProgressionId))
}

@GetMapping("/{translateProgressionId}")
Expand All @@ -39,6 +39,6 @@ class TranslationControllerV1(
): ResponseEntity<ApiResponse<TranslationResponse>> {
val response = translationQueryService.findById(translateProgressionId)
return ResponseEntity.ok()
.body(ApiResponse.success(response))
.body(ApiResponse.ok(response))
}
}
6 changes: 6 additions & 0 deletions src/test/kotlin/kr/galaxyhub/sc/api/support/RestDocDsl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ class RestDocDsl {
responseFieldsSnippet = PayloadDocumentation.responseFields(fields.map { it.descriptor })
}

fun responseBodyWithStatus(vararg fields: DocField) {
val docField = "status" type NUMBER means "HTTP 상태 코드"
responseFieldsSnippet =
PayloadDocumentation.responseFields(docField.descriptor).and(fields.map { it.descriptor })
}

fun queryParameters(vararg params: DocParam) {
queryParametersSnippet = RequestDocumentation.queryParameters(params.map { it.descriptor })
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class OAuth2ControllerV1Test(
"code" pathMeans "Authorization 코드",
"socialType" pathMeans "OAuth2 소셜 타입" formattedAs ENUM(SocialType.entries.filter { it != SocialType.LOCAL }),
)
responseBody(
responseBodyWithStatus(
"data.accessToken" type STRING means "Bearer JWT Access Token",
"data.nickname" type STRING means "사용자의 닉네임",
"data.imageUrl" type STRING means "사용자의 프로필 사진 링크" isOptional true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class CrawlerControllerV1Test(
requestBody(
"url" type STRING means "크롤링할 뉴스의 URL",
)
responseBody(
responseBodyWithStatus(
"data" type STRING means "크롤링되서 생성된 뉴스의 식별자"
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class NewsControllerV1Test(
queryParameters(
"language" pathMeans "뉴스 언어" formattedAs ENUM(Language::class)
)
responseBody(
responseBodyWithStatus(
"data" type OBJECT means "뉴스 상세 정보",
"data.id" type STRING means "뉴스 식별자",
"data.newsType" type ENUM(NewsType::class) means "뉴스 타입",
Expand Down Expand Up @@ -86,7 +86,7 @@ class NewsControllerV1Test(
}.andExpect {
status { isOk() }
}.andDocument("news/find-all") {
responseBody(
responseBodyWithStatus(
"data" type ARRAY means "뉴스 목록",
"data[0].id" type STRING means "뉴스 식별자",
"data[0].newsType" type ENUM(NewsType::class) means "뉴스 타입",
Expand Down Expand Up @@ -124,7 +124,7 @@ class NewsControllerV1Test(
"language" type ENUM(Language::class) means "뉴스 언어",
"content" type STRING means "뉴스 내용"
)
responseBody(
responseBodyWithStatus(
"data" type STRING means "생성한 뉴스의 식별자"
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import kr.galaxyhub.sc.translation.domain.TranslatorProvider
import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc

class TranslationControllerV1Test(
class TranslationControllerV1Test(
private val mockMvc: MockMvc,
private val objectMapper: ObjectMapper,
private val translationCommandService: TranslationCommandService,
Expand Down Expand Up @@ -50,7 +50,7 @@ class TranslationControllerV1Test(
"translatorProvider" type ENUM(TranslatorProvider.entries
.filter { it != TranslatorProvider.LOCAL }) means "번역 서비스 제공자"
)
responseBody(
responseBodyWithStatus(
"data" type STRING means "번역 진행 상황의 식별자"
)
}
Expand All @@ -73,7 +73,7 @@ class TranslationControllerV1Test(
pathParameters(
"translateProgressionId" pathMeans "번역 진행 상황의 식별자"
)
responseBody(
responseBodyWithStatus(
"data.translateProgressionId" type STRING means "번역 진행 상황의 식별자",
"data.targetNewsId" type STRING means "번역할 뉴스의 식별자",
"data.translationStatus" type ENUM(TranslationStatus::class) means "번역 상태",
Expand Down
Loading