Created
October 26, 2022 10:28
-
-
Save shibbirweb/ebb313f0c0e2a38cbf1dfff78f7a0448 to your computer and use it in GitHub Desktop.
Android - Kotlin: Laravel Validation Error Parser (Retrofit with Moshi)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.data.repositories.authentication | |
import com.data.apis.Api | |
import com.data.apis.endpoints.authentication.AuthenticationApi | |
import com.data.apis.models.generic.LaravelValidationErrors | |
import com.data.apis.models.requests.auth.LoginRequest | |
import com.data.apis.models.requests.auth.LoginRequestErrors | |
import com.data.apis.models.responses.auth.LoginResponse | |
import com.data.helpers.Errors.* | |
import com.data.helpers.Resource | |
import com.utilities.parsers.LaravelErrorParser | |
class AuthenticationRepository { | |
private val TAG by lazy { this::class.simpleName } | |
private val apiServiceWithoutAuthentication by lazy { | |
Api.serviceWithoutAuthorization( | |
AuthenticationApi::class.java | |
) | |
} | |
// login | |
suspend fun login(loginRequest: LoginRequest): Resource<LoginResponse, LaravelValidationErrors<LoginRequestErrors>> = | |
try { | |
val response = apiServiceWithoutAuthentication.login(loginRequest) | |
when (val responseCode = response.code()) { | |
in 200..299 -> { | |
Resource.Success(response.body()!!) | |
} | |
422 -> { | |
val validationError = | |
LaravelErrorParser(response.errorBody()!!).laravelValidationErrors<LoginRequestErrors>() | |
Resource.Error(ValidationError(validationError!!, responseCode)) | |
} | |
else -> { | |
Resource.Error( | |
ServerError( | |
"Something went wrong. Try again later.", | |
responseCode | |
) | |
) | |
} | |
} | |
} catch (e: Exception) { | |
e.printStackTrace() | |
Resource.Error( | |
error = ClientError( | |
"Something went wrong. Try again later." | |
) | |
) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.utilities.parsers | |
import com.data.apis.models.generic.BaseLaravelErrorsObject | |
import com.data.apis.models.generic.LaravelValidationErrors | |
import com.squareup.moshi.Moshi | |
import com.squareup.moshi.Types | |
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory | |
import okhttp3.ResponseBody | |
class LaravelErrorParser(private val errorResponse: ResponseBody) { | |
private val TAG by lazy { this::class.simpleName } | |
val _moshi: Moshi by lazy { | |
Moshi | |
.Builder() | |
.addLast(KotlinJsonAdapterFactory()) | |
.build() | |
} | |
val _errorSource by lazy { errorResponse.source() } | |
// parsed response | |
val parsedResponse: Map<*, *> by lazy { | |
val jsonAdapter = _moshi.adapter(Map::class.java).lenient() | |
jsonAdapter.fromJson(errorResponse.source()) as Map<*, *> | |
} | |
/** | |
* Regular error message | |
*/ | |
val message by lazy { | |
parsedResponse["message"].toString() | |
} | |
/** | |
* Validation errors | |
*/ | |
val validationErrors by lazy { | |
try { | |
parsedResponse["errors"] as Map<String, List<String>> | |
} catch (e: Exception) { | |
null | |
} | |
} | |
/** | |
* Specific validation errors | |
* | |
* @param key: String Key name | |
* @return List of string errors or null | |
*/ | |
fun validationErrorsOf(key: String): List<String>? { | |
return try { | |
validationErrors?.get(key) as List<String> | |
} catch (e: Exception) { | |
null | |
} | |
} | |
override fun toString(): String { | |
return validationErrors.toString() | |
} | |
/** | |
* Validation errors | |
*/ | |
inline fun <reified T> validationErrors(): T? { | |
return try { | |
val jsonAdapter = _moshi.adapter(T::class.java).lenient() | |
jsonAdapter.fromJson(_errorSource) | |
} catch (e: Exception) { | |
e.printStackTrace() | |
null | |
} | |
} | |
/** | |
* Laravel validation errors | |
*/ | |
inline fun <reified T : BaseLaravelErrorsObject> laravelValidationErrors(): LaravelValidationErrors<T>? { | |
return try { | |
val type = | |
Types.newParameterizedType(LaravelValidationErrors::class.java, T::class.java) | |
val jsonAdapter = _moshi.adapter<LaravelValidationErrors<T>>(type).lenient() | |
jsonAdapter.fromJson(_errorSource) | |
} catch (e: Exception) { | |
e.printStackTrace() | |
null | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.data.apis.models.generic | |
import com.squareup.moshi.Json | |
import com.squareup.moshi.JsonClass | |
import kotlin.reflect.full.memberProperties | |
@JsonClass(generateAdapter = true) | |
data class LaravelValidationErrors<out T : BaseLaravelErrorsObject>( | |
@Json(name = "message") | |
val message: String? = null, | |
@Json(name = "errors") | |
val errors: T? = null, | |
) | |
// base laravel error object | |
abstract class BaseLaravelErrorsObject { | |
fun errorMessages(): String { | |
var errorMessage = "" | |
this::class.memberProperties.forEach { | |
val errorProperty = it.getter.call(this) as List<String>? | |
if (errorProperty != null) { | |
if (errorMessage.isNotEmpty()) { | |
errorMessage = errorMessage.plus("\n") | |
} | |
errorMessage = errorMessage.plus(errorProperty.joinToString("\n")) | |
} | |
} | |
return errorMessage | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.activities.authenticationActivity.fragments.loginPasswordFragment | |
import androidx.lifecycle.ViewModel | |
import androidx.lifecycle.viewModelScope | |
import com.data.apis.models.requests.auth.LoginRequest | |
import com.data.helpers.Resource | |
import com.data.repositories.authentication.AuthenticationRepository | |
import kotlinx.coroutines.flow.MutableStateFlow | |
import kotlinx.coroutines.flow.StateFlow | |
import kotlinx.coroutines.launch | |
class LoginPasswordFragmentViewModel : ViewModel() { | |
private val TAG by lazy { this::class.simpleName } | |
private val authenticationRepository by lazy { AuthenticationRepository() } | |
private val _uiState = MutableStateFlow(UiState()) | |
val uiState = _uiState as StateFlow<UiState> | |
fun login(loginRequest: LoginRequest, onSuccess: () -> Unit) { | |
viewModelScope.launch { | |
_uiState.value = UiState(true) | |
when (val resource = authenticationRepository.login(loginRequest)) { | |
is Resource.Error -> { | |
_uiState.value = | |
UiState( | |
errorMessage = resource.error?.validationError?.errors?.errorMessages() | |
) | |
} | |
is Resource.Success -> { | |
// TODO save token | |
onSuccess.invoke() | |
} | |
} | |
// _uiState.value = UiState(false) | |
} | |
} | |
// state | |
data class UiState( | |
val loading: Boolean = false, | |
val errorMessage: String? = null | |
) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.data.apis.models.requests.auth | |
import com.data.apis.models.generic.BaseLaravelErrorsObject | |
import com.squareup.moshi.Json | |
import com.squareup.moshi.JsonClass | |
@JsonClass(generateAdapter = true) | |
data class LoginRequest( | |
@Json(name = "username") | |
val username: String, // shibbirweb@gmail.com | |
@Json(name = "password") | |
val password: String, // YOUR_PASSWORD | |
@Json(name = "device_name") | |
val deviceName: String, // Postman | |
) | |
@JsonClass(generateAdapter = true) | |
data class LoginRequestErrors( | |
@Json(name = "username") | |
val username: List<String>?, // shibbirweb@gmail.com | |
@Json(name = "password") | |
val password: List<String>?, // YOUR_PASSWORD | |
@Json(name = "device_name") | |
val deviceName: List<String>?, // Postman | |
) : BaseLaravelErrorsObject() | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.data.helpers | |
sealed class Resource<T, Y>( | |
val data: T? = null, | |
val error: Errors<Y>? = null | |
) { | |
class Success<T, Y>(data: T) : Resource<T, Y>(data) | |
class Error<T, Y>(error: Errors<Y>) : Resource<T, Y>(error = error) | |
} | |
sealed class Errors<K>( | |
val validationError: K? = null, | |
val message: String? = null, | |
val code: Int? = null, | |
) { | |
class ValidationError<K>(validationError: K, code: Int = 422) : | |
Errors<K>(validationError = validationError, code = code) | |
class ServerError<K>(message: String, code: Int = 500) : | |
Errors<K>(message = message, code = code) | |
class ClientError<K>(message: String, code: Int = 400) : Errors<K>(message = message) | |
// more errors as necessary | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment