Skip to content

Commit

Permalink
Add 413 HTTP error - CONTENT_TOO_LARGE
Browse files Browse the repository at this point in the history
When an image that is too big is uploaded, the server will answer with a 413, which we should handle and show the proper error message.
  • Loading branch information
hamorillo committed Oct 18, 2024
1 parent 61a7e69 commit 8441649
Show file tree
Hide file tree
Showing 9 changed files with 34 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.gravatar.quickeditor.data.repository.EmailAvatars
import com.gravatar.quickeditor.ui.editor.AvatarPickerContentLayout
import com.gravatar.restapi.models.Avatar
import com.gravatar.restapi.models.Profile
import com.gravatar.services.ErrorType
import com.gravatar.types.Email
import com.gravatar.ui.components.ComponentState

Expand Down Expand Up @@ -81,7 +82,7 @@ internal data class AvatarsSectionUiState(

internal data class AvatarUploadFailure(
val uri: Uri,
val error: String?,
val error: ErrorType?,
)

internal sealed class AvatarUi(val avatarId: String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,7 @@ internal class AvatarPickerViewModel(
scrollToIndex = null,
failedUploads = currentState.failedUploads + AvatarUploadFailure(
uri,
error = ((result.error as? QuickEditorError.Request)?.type as? ErrorType.InvalidRequest)
?.error?.error,
error = (result.error as? QuickEditorError.Request)?.type,
),
)
}
Expand Down Expand Up @@ -266,6 +265,7 @@ internal class AvatarPickerViewModel(
ErrorType.Timeout,
is ErrorType.Unknown,
is ErrorType.InvalidRequest,
ErrorType.ContentLengthExceeded,
-> SectionError.Unknown
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.gravatar.quickeditor.R
import com.gravatar.quickeditor.ui.avatarpicker.AvatarUploadFailure
import com.gravatar.services.ErrorType
import com.gravatar.ui.GravatarTheme

@Composable
Expand All @@ -26,7 +27,15 @@ internal fun FailedAvatarUploadAlertDialog(
Text(text = stringResource(id = R.string.avatar_upload_failure_dialog_title))
},
text = {
avatarUploadFailure.error?.let { Text(text = it) }
avatarUploadFailure.error?.let {
when (it) {
is ErrorType.InvalidRequest -> it.error?.error
is ErrorType.ContentLengthExceeded -> stringResource(
id = R.string.gravatar_avatar_upload_failure_image_too_big,
)
else -> null
}?.let { errorString -> Text(text = errorString) }
}
},
confirmButton = {
TextButton(
Expand All @@ -47,7 +56,7 @@ internal fun FailedAvatarUploadAlertDialog(
@Composable
private fun FailedAvatarUploadAlertDialogPreview() {
FailedAvatarUploadAlertDialog(
avatarUploadFailure = AvatarUploadFailure(Uri.EMPTY, "Error message"),
avatarUploadFailure = AvatarUploadFailure(Uri.EMPTY, ErrorType.ContentLengthExceeded),
onRemoveUploadClicked = {},
onRetryClicked = {},
onDismiss = {},
Expand Down
1 change: 1 addition & 0 deletions gravatar-quickeditor/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<string name="failed_to_load_avatar_content_description">Upload failed, tap to see actions.</string>
<string name="avatar_upload_failure_dialog_title">Couldn\'t upload image</string>
<string name="avatar_upload_failure_dialog_remove_upload">Remove upload</string>
<string name="gravatar_avatar_upload_failure_image_too_big">The provided image exceeds the maximum size: 10MB</string>
<string name="oauth_email_associated_error_message">Something went wrong when verifying your email.</string>
<string name="gravatar_qe_permission_required_alert_title">Permission required</string>
<string name="gravatar_qe_camera_permission_rationale_message">To take a picture, you need to grant camera permission. You can grant it in the app settings.</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ class AvatarPickerViewModelTest {
avatarPickerUiState.copy(
uploadingAvatar = null,
scrollToIndex = null,
failedUploads = setOf(AvatarUploadFailure(uri, errorMessage)),
failedUploads = setOf(AvatarUploadFailure(uri, invalidRequest.type)),
),
awaitItem(),
)
Expand Down Expand Up @@ -550,7 +550,7 @@ class AvatarPickerViewModelTest {
scrollToIndex = 0,
avatarPickerContentLayout = avatarPickerContentLayout,
failedUploads = setOf(
AvatarUploadFailure(uriOne, errorMessage),
AvatarUploadFailure(uriOne, invalidRequest.type),
),
)
assertEquals(
Expand Down Expand Up @@ -597,7 +597,7 @@ class AvatarPickerViewModelTest {
expectMostRecentItem()
viewModel.onEvent(AvatarPickerEvent.FailedAvatarTapped(uri))

assertEquals(AvatarUploadFailure(uri, errorMessage), awaitItem().failedUploadDialog)
assertEquals(AvatarUploadFailure(uri, invalidRequest.type), awaitItem().failedUploadDialog)
}
}

Expand Down Expand Up @@ -649,7 +649,7 @@ class AvatarPickerViewModelTest {

val awaitItem = awaitItem()
assertEquals(null, awaitItem.failedUploadDialog)
assertEquals(setOf(AvatarUploadFailure(uri, errorMessage)), awaitItem.failedUploads)
assertEquals(setOf(AvatarUploadFailure(uri, invalidRequest.type)), awaitItem.failedUploads)
}
}

Expand Down
7 changes: 7 additions & 0 deletions gravatar/api/gravatar.api
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,13 @@ public final class com/gravatar/services/AvatarService {
public abstract class com/gravatar/services/ErrorType {
}

public final class com/gravatar/services/ErrorType$ContentLengthExceeded : com/gravatar/services/ErrorType {
public static final field INSTANCE Lcom/gravatar/services/ErrorType$ContentLengthExceeded;
public fun equals (Ljava/lang/Object;)Z
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class com/gravatar/services/ErrorType$InvalidRequest : com/gravatar/services/ErrorType {
public fun <init> (Lcom/gravatar/restapi/models/Error;)V
public fun equals (Ljava/lang/Object;)Z
Expand Down
1 change: 1 addition & 0 deletions gravatar/src/main/java/com/gravatar/HttpResponseCode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ internal object HttpResponseCode {
const val HTTP_TOO_MANY_REQUESTS = 429
const val UNAUTHORIZED = 401
const val INVALID_REQUEST = 400
const val CONTENT_TOO_LARGE = 413

private const val HTTP_INTERNAL_ERROR = 500
private const val NETWORK_CONNECT_TIMEOUT_ERROR = 599
Expand Down
5 changes: 4 additions & 1 deletion gravatar/src/main/java/com/gravatar/services/ErrorType.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ internal fun HttpException.errorTypeFromHttpCode(moshi: Moshi): ErrorType = when
}.getOrNull()
ErrorType.InvalidRequest(error)
}

HttpResponseCode.CONTENT_TOO_LARGE -> ErrorType.ContentLengthExceeded
in HttpResponseCode.SERVER_ERRORS -> ErrorType.Server
else -> ErrorType.Unknown("HTTP Code $code - ErrorBody $rawErrorBody")
}
Expand Down Expand Up @@ -54,6 +54,9 @@ public sealed class ErrorType {
/** User not authorized to perform given action **/
public data object Unauthorized : ErrorType()

/** Content length exceeded **/
public data object ContentLengthExceeded : ErrorType()

/**
* An unknown error occurred
*
Expand Down
2 changes: 2 additions & 0 deletions gravatar/src/test/java/com/gravatar/services/ErrorTypeTest.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.gravatar.services

import com.gravatar.HttpResponseCode.CONTENT_TOO_LARGE
import com.gravatar.HttpResponseCode.HTTP_CLIENT_TIMEOUT
import com.gravatar.HttpResponseCode.HTTP_NOT_FOUND
import com.gravatar.HttpResponseCode.HTTP_TOO_MANY_REQUESTS
Expand Down Expand Up @@ -39,6 +40,7 @@ class ErrorTypeTest {
error = "Only square images are accepted"
},
),
CONTENT_TOO_LARGE to ErrorType.ContentLengthExceeded,
).apply {
SERVER_ERRORS.forEach { code ->
add(code to ErrorType.Server)
Expand Down

0 comments on commit 8441649

Please sign in to comment.