Skip to content

Commit add42d2

Browse files
committed
Throwing GravatarException on noncatching service methods
We want to wrap all exceptions that can occur inside the Gravatar service methods in our own "GravatarException." With this approach, we'll provide detailed information about the error, but at the same time, third-party developers know what exceptions can be expected from the Gravatar Services.
1 parent f2a573c commit add42d2

File tree

16 files changed

+213
-125
lines changed

16 files changed

+213
-125
lines changed

gravatar-quickeditor/src/main/java/com/gravatar/quickeditor/ui/avatarpicker/AvatarPickerViewModel.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ internal class AvatarPickerViewModel(
264264
ErrorType.NotFound,
265265
ErrorType.RateLimitExceeded,
266266
ErrorType.Timeout,
267-
ErrorType.Unknown,
267+
is ErrorType.Unknown,
268268
is ErrorType.InvalidRequest,
269269
-> SectionError.Unknown
270270
}

gravatar-quickeditor/src/test/java/com/gravatar/quickeditor/data/repository/AvatarRepositoryTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,12 @@ class AvatarRepositoryTest {
9797
coEvery { tokenStorage.getToken(any()) } returns "token"
9898
coEvery {
9999
avatarService.setAvatarCatching(any(), any(), any())
100-
} returns GravatarResult.Failure(ErrorType.Unknown)
100+
} returns GravatarResult.Failure(ErrorType.Unknown())
101101

102102
val result = avatarRepository.selectAvatar(email, "avatarId")
103103

104104
assertEquals(
105-
GravatarResult.Failure<String, QuickEditorError>(QuickEditorError.Request(ErrorType.Unknown)),
105+
GravatarResult.Failure<String, QuickEditorError>(QuickEditorError.Request(ErrorType.Unknown())),
106106
result,
107107
)
108108
}

gravatar-quickeditor/src/test/java/com/gravatar/quickeditor/ui/avatarpicker/AvatarPickerViewModelTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class AvatarPickerViewModelTest {
6262

6363
@Before
6464
fun setup() {
65-
coEvery { profileService.retrieveCatching(email) } returns GravatarResult.Failure(ErrorType.Unknown)
65+
coEvery { profileService.retrieveCatching(email) } returns GravatarResult.Failure(ErrorType.Unknown())
6666
coEvery { avatarRepository.getAvatars(email) } returns GravatarResult.Success(emailAvatars)
6767
}
6868

gravatar-quickeditor/src/test/java/com/gravatar/quickeditor/ui/oauth/OAuthViewModelTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ class OAuthViewModelTest {
9191
fun `given token when association check failed then UiState_Status updated`() = runTest {
9292
coEvery {
9393
profileService.checkAssociatedEmailCatching(token, email)
94-
} returns GravatarResult.Failure(ErrorType.Unknown)
94+
} returns GravatarResult.Failure(ErrorType.Unknown())
9595

9696
viewModel.uiState.test {
9797
expectMostRecentItem()
@@ -108,7 +108,7 @@ class OAuthViewModelTest {
108108
fun `given token when association check failed then token stored`() = runTest {
109109
coEvery {
110110
profileService.checkAssociatedEmailCatching(token, email)
111-
} returns GravatarResult.Failure(ErrorType.Unknown)
111+
} returns GravatarResult.Failure(ErrorType.Unknown())
112112

113113
viewModel.tokenReceived(
114114
email,

gravatar/api/gravatar.api

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -705,12 +705,20 @@ public final class com/gravatar/services/ErrorType$Unauthorized : com/gravatar/s
705705
}
706706

707707
public final class com/gravatar/services/ErrorType$Unknown : com/gravatar/services/ErrorType {
708-
public static final field INSTANCE Lcom/gravatar/services/ErrorType$Unknown;
708+
public fun <init> ()V
709+
public fun <init> (Ljava/lang/String;)V
710+
public synthetic fun <init> (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
709711
public fun equals (Ljava/lang/Object;)Z
712+
public final fun getErrorMsg ()Ljava/lang/String;
710713
public fun hashCode ()I
711714
public fun toString ()Ljava/lang/String;
712715
}
713716

717+
public final class com/gravatar/services/GravatarException : java/lang/RuntimeException {
718+
public final fun getErrorType ()Lcom/gravatar/services/ErrorType;
719+
public final fun getOriginalException ()Ljava/lang/Exception;
720+
}
721+
714722
public abstract interface class com/gravatar/services/GravatarListener {
715723
public abstract fun onError (Ljava/lang/Object;)V
716724
public abstract fun onSuccess (Ljava/lang/Object;)V
@@ -742,11 +750,6 @@ public final class com/gravatar/services/GravatarResult$Success : com/gravatar/s
742750
public fun toString ()Ljava/lang/String;
743751
}
744752

745-
public final class com/gravatar/services/HttpException : java/lang/RuntimeException {
746-
public final fun getCode ()I
747-
public fun getMessage ()Ljava/lang/String;
748-
}
749-
750753
public final class com/gravatar/services/ProfileService {
751754
public static final field LOG_TAG Ljava/lang/String;
752755
public fun <init> ()V

gravatar/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ dependencies {
8484
testImplementation(libs.mockk.android)
8585
testImplementation(libs.mockk.agent)
8686
testImplementation(libs.kotlinx.coroutines.test)
87+
testImplementation(kotlin("test"))
8788
}
8889

8990
project.afterEvaluate {

gravatar/src/main/java/com/gravatar/services/AvatarService.kt

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -29,23 +29,25 @@ public class AvatarService(private val okHttpClient: OkHttpClient? = null) {
2929
* @param file The image file to upload
3030
* @param oauthToken The OAuth token to use for authentication
3131
*/
32-
public suspend fun upload(file: File, oauthToken: String): Avatar = withContext(GravatarSdkDI.dispatcherIO) {
33-
val service = instance.getGravatarV3Service(okHttpClient, oauthToken)
32+
public suspend fun upload(file: File, oauthToken: String): Avatar = runThrowingExceptionRequest {
33+
withContext(GravatarSdkDI.dispatcherIO) {
34+
val service = instance.getGravatarV3Service(okHttpClient, oauthToken)
3435

35-
val filePart =
36-
MultipartBody.Part.createFormData("image", file.name, file.asRequestBody())
36+
val filePart =
37+
MultipartBody.Part.createFormData("image", file.name, file.asRequestBody())
3738

38-
val response = service.uploadAvatar(filePart)
39+
val response = service.uploadAvatar(filePart)
3940

40-
if (response.isSuccessful && response.body() != null) {
41-
response.body()!!
42-
} else {
43-
// Log the response body for debugging purposes if the response is not successful
44-
Logger.w(
45-
LOG_TAG,
46-
"Network call unsuccessful trying to upload Gravatar: $response.body",
47-
)
48-
throw HttpException(response)
41+
if (response.isSuccessful && response.body() != null) {
42+
response.body()!!
43+
} else {
44+
// Log the response body for debugging purposes if the response is not successful
45+
Logger.w(
46+
LOG_TAG,
47+
"Network call unsuccessful trying to upload Gravatar: $response.body",
48+
)
49+
throw HttpException(response)
50+
}
4951
}
5052
}
5153

@@ -70,7 +72,7 @@ public class AvatarService(private val okHttpClient: OkHttpClient? = null) {
7072
* @param hash The hash of the email to associate the avatars with
7173
* @return The list of avatars
7274
*/
73-
public suspend fun retrieve(oauthToken: String, hash: Hash): List<Avatar> =
75+
public suspend fun retrieve(oauthToken: String, hash: Hash): List<Avatar> = runThrowingExceptionRequest {
7476
withContext(GravatarSdkDI.dispatcherIO) {
7577
val service = instance.getGravatarV3Service(okHttpClient, oauthToken)
7678

@@ -87,6 +89,7 @@ public class AvatarService(private val okHttpClient: OkHttpClient? = null) {
8789
throw HttpException(response)
8890
}
8991
}
92+
}
9093

9194
/**
9295
* Retrieves a list of available avatars for the authenticated user.
@@ -110,20 +113,22 @@ public class AvatarService(private val okHttpClient: OkHttpClient? = null) {
110113
* @param oauthToken The OAuth token to use for authentication
111114
*/
112115
public suspend fun setAvatar(hash: String, avatarId: String, oauthToken: String): Unit =
113-
withContext(GravatarSdkDI.dispatcherIO) {
114-
val service = GravatarSdkDI.getGravatarV3Service(okHttpClient, oauthToken)
116+
runThrowingExceptionRequest {
117+
withContext(GravatarSdkDI.dispatcherIO) {
118+
val service = GravatarSdkDI.getGravatarV3Service(okHttpClient, oauthToken)
115119

116-
val response = service.setEmailAvatar(avatarId, SetEmailAvatarRequest { emailHash = hash })
120+
val response = service.setEmailAvatar(avatarId, SetEmailAvatarRequest { emailHash = hash })
117121

118-
if (response.isSuccessful) {
119-
Unit
120-
} else {
121-
// Log the response body for debugging purposes if the response is not successful
122-
Logger.w(
123-
LOG_TAG,
124-
"Network call unsuccessful trying to set Gravatar avatar: $response.body",
125-
)
126-
throw HttpException(response)
122+
if (response.isSuccessful) {
123+
Unit
124+
} else {
125+
// Log the response body for debugging purposes if the response is not successful
126+
Logger.w(
127+
LOG_TAG,
128+
"Network call unsuccessful trying to set Gravatar avatar: $response.body",
129+
)
130+
throw HttpException(response)
131+
}
127132
}
128133
}
129134

gravatar/src/main/java/com/gravatar/services/ErrorType.kt

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,15 @@ internal fun HttpException.errorTypeFromHttpCode(moshi: Moshi): ErrorType = when
2020
}
2121

2222
in HttpResponseCode.SERVER_ERRORS -> ErrorType.Server
23-
else -> ErrorType.Unknown
23+
else -> ErrorType.Unknown("HTTP Code $code - ErrorBody $rawErrorBody")
2424
}
2525

2626
internal fun Throwable.errorType(moshi: Moshi): ErrorType {
2727
return when (this) {
2828
is SocketTimeoutException -> ErrorType.Timeout
2929
is UnknownHostException -> ErrorType.Network
3030
is HttpException -> this.errorTypeFromHttpCode(moshi)
31-
else -> ErrorType.Unknown
31+
else -> ErrorType.Unknown(message)
3232
}
3333
}
3434

@@ -54,8 +54,18 @@ public sealed class ErrorType {
5454
/** User not authorized to perform given action **/
5555
public data object Unauthorized : ErrorType()
5656

57-
/** An unknown error occurred */
58-
public data object Unknown : ErrorType()
57+
/**
58+
* An unknown error occurred
59+
*
60+
* @property errorMsg The error message, if available.
61+
*/
62+
public class Unknown(public val errorMsg: String? = null) : ErrorType() {
63+
override fun toString(): String = "Unknown(errorMsg=$errorMsg)"
64+
65+
override fun equals(other: Any?): Boolean = other is Unknown && errorMsg == other.errorMsg
66+
67+
override fun hashCode(): Int = Objects.hash(errorMsg)
68+
}
5969

6070
/**
6171
* An error occurred while processing the request.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.gravatar.services
2+
3+
/**
4+
* Exception that will be thrown when an error occurs during Gravatar operations.
5+
*
6+
* @property errorType The type of error that occurred.
7+
* @property originalException The original exception that caused this exception.
8+
*/
9+
public class GravatarException internal constructor(
10+
public val errorType: ErrorType,
11+
public val originalException: Exception? = null,
12+
) : RuntimeException()

gravatar/src/main/java/com/gravatar/services/HttpException.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@ import retrofit2.Response
55
/**
66
* Exception thrown when an HTTP error occurs in a non-cathing method.
77
*
8+
* [rawErrorBody] The raw error body of the response.
89
* [code] The HTTP status code.
9-
* [message] The HTTP status message.
1010
*/
11-
public class HttpException internal constructor(response: Response<*>) : RuntimeException() {
12-
internal val rawErrorBody: String? = response.errorBody()?.string()
13-
public val code: Int = response.code()
14-
public override val message: String = "HTTP ${response.code()} $rawErrorBody"
11+
internal class HttpException internal constructor(response: Response<*>) : RuntimeException() {
12+
val rawErrorBody: String? = response.errorBody()?.string()
13+
val code: Int = response.code()
1514
}

0 commit comments

Comments
 (0)