[APIv2/Librus] Implement error handling. Catch exceptions in ApiService.

This commit is contained in:
Kuba Szczodrzyński 2019-11-05 11:27:29 +01:00
parent 399ae7e3dc
commit 385fe21d16
6 changed files with 101 additions and 27 deletions

View File

@ -90,6 +90,7 @@ class ApiService : Service() {
errorList.add(apiError) errorList.add(apiError)
apiError.throwable?.printStackTrace() apiError.throwable?.printStackTrace()
if (apiError.isCritical) { if (apiError.isCritical) {
taskRunning?.cancel()
notification.setCriticalError().post() notification.setCriticalError().post()
taskRunning = null taskRunning = null
taskIsRunning = false taskIsRunning = false
@ -154,10 +155,14 @@ class ApiService : Service() {
// post an event // post an event
EventBus.getDefault().post(ApiTaskStartedEvent(taskProfileId, task.profile)) EventBus.getDefault().post(ApiTaskStartedEvent(taskProfileId, task.profile))
when (task) { try {
is EdziennikTask -> task.run(app, taskCallback) when (task) {
is NotifyTask -> task.run(app, taskCallback) is EdziennikTask -> task.run(app, taskCallback)
is ErrorReportTask -> task.run(app, taskCallback, notification, errorList) is NotifyTask -> task.run(app, taskCallback)
is ErrorReportTask -> task.run(app, taskCallback, notification, errorList)
}
} catch (e: Exception) {
taskCallback.onError(ApiError(TAG, EXCEPTION_API_TASK).withThrowable(e))
} }
} }

View File

@ -150,6 +150,7 @@ const val ERROR_IDZIENNIK_API_OTHER = 451
const val ERROR_TEMPLATE_WEB_OTHER = 801 const val ERROR_TEMPLATE_WEB_OTHER = 801
const val EXCEPTION_API_TASK = 900
const val EXCEPTION_LOGIN_LIBRUS_API_TOKEN = 901 const val EXCEPTION_LOGIN_LIBRUS_API_TOKEN = 901
const val EXCEPTION_LOGIN_LIBRUS_PORTAL_TOKEN = 902 const val EXCEPTION_LOGIN_LIBRUS_PORTAL_TOKEN = 902
const val EXCEPTION_LIBRUS_PORTAL_SYNERGIA_TOKEN = 903 const val EXCEPTION_LIBRUS_PORTAL_SYNERGIA_TOKEN = 903

View File

@ -5,7 +5,7 @@
package pl.szczodrzynski.edziennik.api.v2.librus package pl.szczodrzynski.edziennik.api.v2.librus
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 import pl.szczodrzynski.edziennik.api.v2.*
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusData import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusData
@ -14,9 +14,7 @@ import pl.szczodrzynski.edziennik.api.v2.librus.firstlogin.LibrusFirstLogin
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLogin import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLogin
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginApi import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginApi
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginSynergia import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginSynergia
import pl.szczodrzynski.edziennik.api.v2.librusLoginMethods
import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.api.v2.prepare
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
@ -53,12 +51,28 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|__*/ |__*/
override fun sync(featureIds: List<Int>, viewId: Int?) { override fun sync(featureIds: List<Int>, viewId: Int?) {
data.prepare(librusLoginMethods, LibrusFeatures, featureIds, viewId) data.prepare(librusLoginMethods, LibrusFeatures, featureIds, viewId)
d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}") login()
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") }
private fun login() {
d(TAG, "Trying to login with ${data.targetLoginMethodIds}")
if (internalErrorList.isNotEmpty()) {
d(TAG, " - Internal errors:")
internalErrorList.forEach { d(TAG, " - code $it") }
}
LibrusLogin(data) { LibrusLogin(data) {
LibrusData(data) { data()
completed() }
} }
private fun data() {
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
if (internalErrorList.isNotEmpty()) {
d(TAG, " - Internal errors:")
internalErrorList.forEach { d(TAG, " - code $it") }
}
LibrusData(data) {
completed()
} }
} }
@ -102,15 +116,67 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
} }
override fun onError(apiError: ApiError) { override fun onError(apiError: ApiError) {
if (apiError.errorCode in internalErrorList) {
// finish immediately if the same error occurs twice during the same sync
callback.onError(apiError)
return
}
internalErrorList.add(apiError.errorCode)
when (apiError.errorCode) { when (apiError.errorCode) {
in internalErrorList -> { ERROR_LIBRUS_PORTAL_ACCESS_DENIED -> {
// finish immediately if the same error occurs twice during the same sync data.loginMethods.remove(LOGIN_METHOD_LIBRUS_PORTAL)
callback.onError(apiError) data.targetLoginMethodIds.add(LOGIN_METHOD_LIBRUS_PORTAL)
data.targetLoginMethodIds.sort()
data.portalTokenExpiryTime = 0
login()
} }
CODE_INTERNAL_LIBRUS_ACCOUNT_410 -> { ERROR_LIBRUS_API_ACCESS_DENIED,
internalErrorList.add(apiError.errorCode) ERROR_LIBRUS_API_TOKEN_EXPIRED -> {
loginStore.removeLoginData("refreshToken") // force a clean login data.loginMethods.remove(LOGIN_METHOD_LIBRUS_API)
//loginLibrus() data.targetLoginMethodIds.add(LOGIN_METHOD_LIBRUS_API)
data.targetLoginMethodIds.sort()
data.apiTokenExpiryTime = 0
login()
}
ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED -> {
data.loginMethods.remove(LOGIN_METHOD_LIBRUS_SYNERGIA)
data.targetLoginMethodIds.add(LOGIN_METHOD_LIBRUS_SYNERGIA)
data.targetLoginMethodIds.sort()
data.synergiaSessionIdExpiryTime = 0
login()
}
ERROR_LIBRUS_MESSAGES_ACCESS_DENIED -> {
data.loginMethods.remove(LOGIN_METHOD_LIBRUS_MESSAGES)
data.targetLoginMethodIds.add(LOGIN_METHOD_LIBRUS_MESSAGES)
data.targetLoginMethodIds.sort()
data.messagesSessionIdExpiryTime = 0
login()
}
ERROR_LOGIN_LIBRUS_PORTAL_NO_CODE,
ERROR_LOGIN_LIBRUS_PORTAL_CSRF_MISSING,
ERROR_LOGIN_LIBRUS_PORTAL_CODE_REVOKED,
ERROR_LOGIN_LIBRUS_PORTAL_CODE_EXPIRED -> {
login()
}
ERROR_LOGIN_LIBRUS_PORTAL_NO_REFRESH,
ERROR_LOGIN_LIBRUS_PORTAL_REFRESH_REVOKED,
ERROR_LOGIN_LIBRUS_PORTAL_REFRESH_INVALID -> {
data.portalRefreshToken = null
login()
}
ERROR_LOGIN_LIBRUS_SYNERGIA_TOKEN_INVALID,
ERROR_LOGIN_LIBRUS_SYNERGIA_NO_TOKEN,
ERROR_LOGIN_LIBRUS_SYNERGIA_NO_SESSION_ID -> {
login()
}
ERROR_LOGIN_LIBRUS_MESSAGES_NO_SESSION_ID -> {
login()
}
// TODO PORTAL CAPTCHA
ERROR_LIBRUS_API_TIMETABLE_NOT_PUBLIC,
ERROR_LIBRUS_API_LUCKY_NUMBER_NOT_ACTIVE,
ERROR_LIBRUS_API_NOTES_NOT_ACTIVE -> {
data()
} }
else -> callback.onError(apiError) else -> callback.onError(apiError)
} }

View File

@ -44,7 +44,10 @@ open class LibrusPortal(open val data: DataLibrus) {
"Access token is invalid" -> ERROR_LIBRUS_PORTAL_ACCESS_DENIED "Access token is invalid" -> ERROR_LIBRUS_PORTAL_ACCESS_DENIED
"ApiDisabled" -> ERROR_LIBRUS_PORTAL_API_DISABLED "ApiDisabled" -> ERROR_LIBRUS_PORTAL_API_DISABLED
"Account not found" -> ERROR_LIBRUS_PORTAL_SYNERGIA_NOT_FOUND "Account not found" -> ERROR_LIBRUS_PORTAL_SYNERGIA_NOT_FOUND
else -> ERROR_LIBRUS_PORTAL_OTHER else -> when (json.getString("hint")) {
"Error while decoding to JSON" -> ERROR_LIBRUS_PORTAL_ACCESS_DENIED
else -> ERROR_LIBRUS_PORTAL_OTHER
}
}.let { errorCode -> }.let { errorCode ->
data.error(ApiError(tag, errorCode) data.error(ApiError(tag, errorCode)
.withApiResponse(json) .withApiResponse(json)

View File

@ -390,8 +390,6 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
} }
fun error(apiError: ApiError) { fun error(apiError: ApiError) {
if (apiError.isCritical)
cancel()
callback.onError(apiError) callback.onError(apiError)
} }

View File

@ -8,6 +8,7 @@ import androidx.room.Entity;
import androidx.room.Ignore; import androidx.room.Ignore;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull; import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull;
@ -73,7 +74,7 @@ public class LoginStore {
if (data == null) if (data == null)
return defaultValue; return defaultValue;
JsonElement element = data.get(key); JsonElement element = data.get(key);
if (element != null) { if (element != null && !(element instanceof JsonNull)) {
return element.getAsString(); return element.getAsString();
} }
return defaultValue; return defaultValue;
@ -83,7 +84,7 @@ public class LoginStore {
if (data == null) if (data == null)
return defaultValue; return defaultValue;
JsonElement element = data.get(key); JsonElement element = data.get(key);
if (element != null) { if (element != null && !(element instanceof JsonNull)) {
return element.getAsInt(); return element.getAsInt();
} }
return defaultValue; return defaultValue;
@ -93,7 +94,7 @@ public class LoginStore {
if (data == null) if (data == null)
return defaultValue; return defaultValue;
JsonElement element = data.get(key); JsonElement element = data.get(key);
if (element != null) { if (element != null && !(element instanceof JsonNull)) {
return element.getAsLong(); return element.getAsLong();
} }
return defaultValue; return defaultValue;
@ -103,7 +104,7 @@ public class LoginStore {
if (data == null) if (data == null)
return defaultValue; return defaultValue;
JsonElement element = data.get(key); JsonElement element = data.get(key);
if (element != null) { if (element != null && !(element instanceof JsonNull)) {
return element.getAsFloat(); return element.getAsFloat();
} }
return defaultValue; return defaultValue;
@ -112,7 +113,7 @@ public class LoginStore {
if (data == null) if (data == null)
return defaultValue; return defaultValue;
JsonElement element = data.get(key); JsonElement element = data.get(key);
if (element != null) { if (element != null && !(element instanceof JsonNull)) {
return element.getAsBoolean(); return element.getAsBoolean();
} }
return defaultValue; return defaultValue;