-
Notifications
You must be signed in to change notification settings - Fork 0
[Refactor] API 호출 오류 핸들링 개선 #391
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
Changes from 11 commits
9096ecc
a68d0bd
9e2a51d
05159c6
1877369
a3362fe
c09b185
63f4a0e
6cc8dec
dd60769
f587b8c
d140199
ab61862
886200a
019859d
8281771
7701831
d09d4b7
b77cbf5
2c91d5c
7da9ca8
6412fd5
a16114d
31e0c99
e58a3a9
f64d784
ae15754
b221c36
04055fc
cb24faf
855d229
976744f
fb4b49c
55cadf0
1689f2e
f214504
0ec4086
f61979e
1d40b3c
d08ea51
bc9208d
60ffc27
d12a307
be68944
1bdf871
73c652a
dd79dde
4503516
265a86b
3c525bd
f206d82
fcb7647
2c86c4f
7397c53
9ff0cb8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| package com.eatssu.android.data.model | ||
|
|
||
| import java.io.IOException | ||
|
|
||
| sealed class ApiResult<T : Any> { | ||
| fun <R : Any> map(transform: (T) -> R): ApiResult<out R> = when (this) { | ||
| is Success -> Success(transform(data)) | ||
| is Failure -> Failure(responseCode, message) | ||
| is NetworkError -> NetworkError(exception) | ||
| is UnknownError -> UnknownError(exception) | ||
| } | ||
|
|
||
| fun orElse(default: T): T = when (this) { | ||
| is Success -> data | ||
| else -> default | ||
| } | ||
|
|
||
| fun orElse(default: () -> T): T = when (this) { | ||
| is Success -> data | ||
| else -> default() | ||
| } | ||
|
|
||
| fun orNull(): T? = when (this) { | ||
| is Success -> data | ||
| else -> null | ||
| } | ||
|
|
||
| data class Success<T : Any>(val data: T) : ApiResult<T>() | ||
|
|
||
| data class Failure( | ||
PeraSite marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| val responseCode: Int, | ||
| val message: String? | ||
| ) : ApiResult<Nothing>() | ||
|
|
||
| data class NetworkError( | ||
PeraSite marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| val exception: IOException | ||
| ) : ApiResult<Nothing>() | ||
|
|
||
| data class UnknownError( | ||
PeraSite marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| val exception: Throwable | ||
| ) : ApiResult<Nothing>() | ||
|
|
||
| } | ||
|
|
||
| fun <TElement, TList : List<TElement>> ApiResult<TList>.orEmptyList(): List<TElement> = | ||
PeraSite marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| when (this) { | ||
| is ApiResult.Success -> data | ||
| else -> emptyList() | ||
| } | ||
|
|
||
| fun ApiResult<Unit>.isSuccess(): Boolean = this is ApiResult.Success | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,43 +1,24 @@ | ||
| package com.eatssu.android.data.repository | ||
|
|
||
| import com.eatssu.android.data.dto.response.BaseResponse | ||
| import com.eatssu.android.data.dto.response.MenuOfMealResponse | ||
| import com.eatssu.android.data.dto.response.MenusInformation | ||
| import com.eatssu.android.data.dto.response.toDomain | ||
| import com.eatssu.android.data.model.orEmptyList | ||
| import com.eatssu.android.data.service.MealService | ||
| import com.eatssu.android.domain.repository.MealRepository | ||
| import kotlinx.coroutines.flow.Flow | ||
| import kotlinx.coroutines.flow.flow | ||
| import javax.inject.Inject | ||
|
|
||
| class MealRepositoryImpl @Inject constructor( | ||
| private val mealService: MealService, | ||
| ) : MealRepository { | ||
|
|
||
| override suspend fun getTodayMeal( //todo 분기처리 어떻게 할지? | ||
| override suspend fun getTodayMeal( | ||
| date: String, | ||
| restaurant: String, | ||
| time: String | ||
| ): Flow<List<List<String>>> { | ||
| return flow { | ||
| try { | ||
| val response = mealService.getTodayMeal2(date, restaurant, time) | ||
|
|
||
| // 응답이 성공적이라면 Result.success()로 감싸서 Flow로 반환 | ||
| if (response.isSuccess == true) { | ||
| response.result?.let { emit(it.toDomain()) } // 성공시 데이터를 반환 | ||
| } else { | ||
| // 실패한 경우에는 Result.failure()로 실패 정보 반환 | ||
| emit(emptyList()) | ||
| } | ||
| } catch (e: Exception) { | ||
| // 네트워크 오류 또는 예외가 발생한 경우에는 Result.failure()로 반환 | ||
| // emit(ApiResult.Failure(e)) | ||
| } | ||
| } | ||
| ): List<List<String>> { | ||
| return mealService.getTodayMeal(date, restaurant, time).orEmptyList().toDomain() | ||
PeraSite marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| override suspend fun getMenuInfoByMealId(mealId: Long): Flow<BaseResponse<MenuOfMealResponse>> = | ||
| flow { | ||
| emit(mealService.getMenuInfoByMealId(mealId)) | ||
| } | ||
| override suspend fun getMenuInfoByMealId(mealId: Long): List<MenusInformation> = | ||
| mealService.getMenuInfoByMealId(mealId).map { it.briefMenus }.orEmptyList() | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,19 +1,15 @@ | ||
| package com.eatssu.android.data.repository | ||
|
|
||
| import com.eatssu.android.data.dto.request.ReportRequest | ||
| import com.eatssu.android.data.dto.response.BaseResponse | ||
| import com.eatssu.android.data.model.isSuccess | ||
| import com.eatssu.android.data.service.ReportService | ||
| import com.eatssu.android.domain.repository.ReportRepository | ||
| import kotlinx.coroutines.flow.Flow | ||
| import kotlinx.coroutines.flow.flow | ||
| import javax.inject.Inject | ||
|
|
||
| class ReportRepositoryImpl @Inject constructor(private val reportService: ReportService) : | ||
| ReportRepository { | ||
|
|
||
| override suspend fun reportReview(body: ReportRequest): Flow<BaseResponse<Void>> = | ||
| flow { | ||
| emit(reportService.reportReview(body)) | ||
| } | ||
| override suspend fun reportReview(body: ReportRequest): Boolean = | ||
| reportService.reportReview(body).isSuccess() | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,56 +2,49 @@ package com.eatssu.android.data.repository | |
|
|
||
| import com.eatssu.android.data.dto.request.ChangeNicknameRequest | ||
| import com.eatssu.android.data.dto.request.UserDepartmentRequest | ||
| import com.eatssu.android.data.dto.response.BaseResponse | ||
| import com.eatssu.android.data.dto.response.MyReviewResponse | ||
| import com.eatssu.android.data.dto.response.toDomain | ||
| import com.eatssu.android.data.model.isSuccess | ||
| import com.eatssu.android.data.model.orEmptyList | ||
| import com.eatssu.android.data.service.UserService | ||
| import com.eatssu.android.domain.model.College | ||
| import com.eatssu.android.domain.model.Department | ||
| import com.eatssu.android.domain.repository.UserRepository | ||
| import kotlinx.coroutines.flow.Flow | ||
| import kotlinx.coroutines.flow.flow | ||
| import javax.inject.Inject | ||
|
|
||
| class UserRepositoryImpl @Inject constructor(private val userService: UserService) : | ||
| UserRepository { | ||
|
|
||
| override suspend fun updateUserName(body: ChangeNicknameRequest) { | ||
| userService.changeNickname(body) | ||
| } | ||
|
|
||
| override suspend fun updateUserName(body: ChangeNicknameRequest): Boolean = | ||
| userService.changeNickname(body).isSuccess() | ||
|
|
||
| override suspend fun checkUserNameValidation(nickname: String): Boolean = | ||
| userService.checkNickname(nickname).orElse(false) | ||
|
Comment on lines
+22
to
+26
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. isSuccess를 쓰는 상황과 .orElse(false)를 쓰는 상황이 정확히 나뉘는 기준이 있을까요?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. updateUserName는 Response 형태가 Unit이라 BaseResponse에서 성공했는지, HTTP Response Code가 어떻게 되는지만 확인하면 되는데, 실제 서버 코드를 확인해본 것은 아니지만 닉네임이 중복인게(=result.data가 false인게) 오류는 아니니까 정상적으로 반환하는 것 같아요. |
||
|
|
||
| override suspend fun checkUserNameValidation(nickname: String): Flow<BaseResponse<Boolean>> = | ||
| flow { | ||
| emit(userService.checkNickname(nickname)) | ||
| } | ||
| override suspend fun getUserReviews(): MyReviewResponse? = | ||
| userService.getMyReviews().orNull() | ||
|
|
||
| override suspend fun getUserReviews(): Flow<BaseResponse<MyReviewResponse>> = | ||
| flow { | ||
| emit(userService.getMyReviews()) | ||
| } | ||
| override suspend fun getUserNickName(): String = | ||
| userService.getMyInfo().map { it.nickname ?: "" }.orNull() ?: "" | ||
PeraSite marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| override suspend fun getUserNickName() = userService.getMyInfo().result?.nickname ?: "" | ||
|
|
||
| override suspend fun signOut(): Boolean { | ||
| return userService.signOut().result ?: false | ||
| } | ||
| override suspend fun signOut(): Boolean = | ||
| userService.signOut().orElse(false) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기도 .isSuccess() 랑 차이가 궁금합니다
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 위 케이스와 마찬가지로 Success인데 result.data가 false인 케이스가 가능하게 API 명세가 Boolean을 반환해서 이렇게 대응했습니다! |
||
|
|
||
| override suspend fun getTotalColleges(): List<College> = | ||
| userService.getCollegeList().result?.map { it.toDomain() }.orEmpty() | ||
| userService.getCollegeList() | ||
| .map { list -> list.map { it.toDomain() } } | ||
| .orEmptyList() | ||
|
|
||
| override suspend fun getTotalDepartments(collegeId: Int): List<Department> = | ||
| userService.getDepartmentsByCollege(collegeId).result?.map { it.toDomain() }.orEmpty() | ||
| userService.getDepartmentsByCollege(collegeId) | ||
| .map { list -> list.map { it.toDomain() } } | ||
| .orEmptyList() | ||
|
|
||
| override suspend fun getUserCollegeDepartment(): Pair<College, Department> = | ||
| userService.getUserCollegeDepartment().result?.toDomain() | ||
| ?: throw IllegalStateException("유저 학과 정보를 불러올 수 없습니다.") | ||
| override suspend fun getUserCollegeDepartment(): Pair<College, Department>? = | ||
| userService.getUserCollegeDepartment().map { it.toDomain() }.orNull() | ||
|
|
||
| override suspend fun setUserDepartment(departmentId: Int): BaseResponse<Void> { | ||
| return userService.setUserDepartment( | ||
| UserDepartmentRequest(departmentId) | ||
| ) | ||
| override suspend fun setUserDepartment(departmentId: Int): Boolean { | ||
| return userService.setUserDepartment(UserDepartmentRequest(departmentId)).isSuccess() | ||
| } | ||
|
|
||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.