-
Notifications
You must be signed in to change notification settings - Fork 62
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
Fix crashes on unmapped error codes #951
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, only minor comments + we also need a CHANGELOG update.
public constructor(message: String) : super(message) | ||
public constructor(message: String, cause: Throwable) : super(message, cause) | ||
public constructor(cause: Throwable) : super(cause) | ||
public constructor(message: String?) : super(message) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was actually on purpose I made these non-null as we should always strive to have some kind of message. I can see you use null
in the safeErrorCodeMapping
method, but I would consider if we could just return ""
there instead since it is kind of an edge case.
return "[$categoryDesc][$errorDesc] ${error.message}" | ||
} | ||
|
||
// Prevents exceptions caused by unmapped sync errors from propagating and causing a crash. The exception | ||
// message describes the missing error code and its category. | ||
internal fun <T> safeErrorCodeMapping(onException: (message: String?) -> T, block: () -> T): T = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice way doing this 👍
ProtocolConnectionErrorCode.RLM_SYNC_ERR_CONNECTION_SWITCH_TO_PBS -> { // Connected with wrong wire protocol - should switch to PBS | ||
WrongSyncTypeException(message) | ||
return safeErrorCodeMapping(onException = { SyncException(it) }) { | ||
when (error.category) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably not worth fixing in this PR, but we would still crash if we suddenly see a category we haven't seen before. It would crash here though, but inside RealmInterop. Maybe just create an issue for it for now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I just don't get the overall layers of mapping, but the tests looks more like a unittest of the AppErrorCode/SyncErrorCode
-constructor than a test verifying that we actually can handle unmapped error codes. Don't know if it possible for Sync errors, but for App-errors we could probably inject a mock HTTP-client that issued unknown error codes. Don't know if it is worth considering for this PR compared to getting the fix out though 🤔 ... has it been verified manually?
3ddf15b
to
a92ca9c
Compare
# Conflicts: # CHANGELOG.md
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only tiny things, otherwise looks good 💯
@@ -63,7 +61,7 @@ internal fun convertSyncErrorCode(error: SyncErrorCode): SyncException { | |||
// See https://github.com/realm/realm-core/blob/master/src/realm/sync/protocol.hpp#L200 | |||
// Use https://docs.google.com/spreadsheets/d/1SmiRxhFpD1XojqCKC-xAjjV-LKa9azeeWHg-zgr07lE/edit | |||
// as guide for how to categorize Connection type errors. | |||
when (ProtocolConnectionErrorCode.fromInt(error.value)) { | |||
when (error.error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Redundant naming?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really, but we can rename the variable error:
SyncErrorCode into syncErrorCode: SyncErrorCode
for readability.
SyncErrorCodeCategory.RLM_SYNC_ERROR_CATEGORY_UNKNOWN, | ||
-> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Weird indenting?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Helps add new categories.
if (errorCodeDesc == null) | ||
error.errorCode.toString() | ||
else | ||
"$errorCodeDesc(${error.errorCode})" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Leave it as a one liner or write braces for the if
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only minor comments 🚀
@@ -16,18 +16,55 @@ | |||
|
|||
package io.realm.kotlin.internal.interop.sync | |||
|
|||
import kotlin.jvm.JvmStatic | |||
|
|||
interface ErrorCode { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a description of what this interface is for? Especially since we also have an errorCode: Int
in AppError
val category: AppErrorCategory, | ||
data class AppError internal constructor( | ||
val category: AppErrorCategory?, | ||
val error: ErrorCode?, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is a bit confusing that we have both error: ErrorCode
and errorCode: Int
. Should we either combine them or rename one of them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I got the same feeling, but they map core structures, I will try to get better naming.
return value | ||
} | ||
internal actual fun of(nativeValue: Int): AppErrorCategory? = | ||
values().first { value -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be firstOrNull
otherwise I think it will throw?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😅 yes
return value | ||
} | ||
internal actual fun of(nativeValue: Int): ClientErrorCode? = | ||
values().first { value -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same
return value | ||
} | ||
internal actual fun of(nativeValue: Int): SyncErrorCodeCategory? = | ||
values().first { value -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same
return value | ||
} | ||
internal actual fun of(nativeValue: Int): ProtocolConnectionErrorCode? = | ||
values().first { value -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same
return value | ||
} | ||
internal actual fun of(nativeValue: Int): ProtocolSessionErrorCode? = | ||
values().first { value -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same
return value | ||
} | ||
internal actual fun of(nativeValue: Int): ServiceErrorCode? = | ||
values().first { value -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same
return value | ||
} | ||
internal actual fun of(nativeValue: Int): SyncErrorCodeCategory? = | ||
values().first { value -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same
// server log. | ||
val msg = error.message?.let { message: String -> | ||
if (message.endsWith(".")) { | ||
message |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't add a space before it so I suspect this will make the error a little more unreadable, e.g [Category][ErrorCode]Message.
rather than [Category][ErrorCode] Message.
The SDK would crash if core throws an exception that is not mapped by the C-API.
This PR fixes this situation by handling these unmapped error codes into proper sync or app exceptions.