-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
chore(client): use application errors instead of PlatformErrors #2337
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.
We included arbitrary details in the errors, we should forward them to the strongly typed errors as well.
* ipcToAppError converts an Error returned from the MethodChannel IPC to an application error. | ||
* MethodChannel errors encode its information as a JSON object in the Error message. | ||
*/ | ||
export function ipcToAppError(ipcError: Error): CustomError { |
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.
parseAppErrorFromIPC
would be better?
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.
I'm not sure about using "parse" here, since the input is an object, not serialized text.
@@ -330,10 +330,6 @@ export class App { | |||
buttonHandler = () => { | |||
this.showErrorDetailsDialog(error.details); | |||
}; | |||
} else if (error instanceof PlatformError) { |
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.
I would keep this. The default handler (showErrorCauseDialog
) will print weird outputs (for example, duplicated causes). PlatformError
's toString
already handles the format and prints a cause chain.
Also the default handler doesn't print out error details
Map
.
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.
Fixed in showErrorCauseDialog
.
We shouldn't be using localizeErrorCode
anymore, it's not needed.
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.
We shouldn't be using
localizeErrorCode
anymore, it's not needed.
Where is localization of errors happening now? Are we no longer localizing these errors and just throwing a generic localized "unknown error" for them? That's a regression and is a worse user experience. We should localize the top-level user-facing error that can tell the user what went wrong (like the one suggesting to contact their service provider with details).
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.
We localize the errors in App.showLocalizedError
:
outline-apps/client/src/www/app/app.ts
Line 246 in 2e3bd3d
showLocalizedError(error?: Error, toastDuration = 10000) { |
I'm actually removing the duplicate logic in this PR.
* Returns a JSON string of this error with all details and causes. | ||
* @returns {string} The JSON string representing this error. | ||
*/ | ||
toJSON(): string { |
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 is used when initiating a PlatformError
from electron main process (not from Go) to renderer process: https://github.com/Jigsaw-Code/outline-apps/blob/master/client/electron/routing_service.ts#L141
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.
Fixed.
Ugh... Perhaps we can move the error serialization to Go at some point.
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, we should move the logic to Go in the future. All errors should be triggered from Go.
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.
Another look?
* ipcToAppError converts an Error returned from the MethodChannel IPC to an application error. | ||
* MethodChannel errors encode its information as a JSON object in the Error message. | ||
*/ | ||
export function ipcToAppError(ipcError: Error): CustomError { |
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.
I'm not sure about using "parse" here, since the input is an object, not serialized text.
* Returns a JSON string of this error with all details and causes. | ||
* @returns {string} The JSON string representing this error. | ||
*/ | ||
toJSON(): string { |
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.
Fixed.
Ugh... Perhaps we can move the error serialization to Go at some point.
@@ -330,10 +330,6 @@ export class App { | |||
buttonHandler = () => { | |||
this.showErrorDetailsDialog(error.details); | |||
}; | |||
} else if (error instanceof PlatformError) { |
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.
Fixed in showErrorCauseDialog
.
We shouldn't be using localizeErrorCode
anymore, it's not needed.
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.
Thanks for the update. Added some comments.
// FetchConfigFailed means we failed to fetch a config from a remote location. | ||
FetchConfigFailed ErrorCode = "ERR_FETCH_CONFIG_FAILURE" | ||
// GenericErr indicates a generic error that is not directly surfaced to the user. | ||
GenericErr ErrorCode = "ERR_GENERIC" |
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.
Can we use the InternalError ErrorCode = "ERR_INTERNAL_ERROR"
defined above, which has already been widely used across the code base?
client/src/www/app/app.ts
Outdated
@@ -756,7 +750,7 @@ export class App { | |||
private showErrorCauseDialog(error: Error) { | |||
let message = error.toString(); | |||
|
|||
if (error.cause) { | |||
if (!(error instanceof PlatformError && error.cause)) { |
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.
Correct me if I was wrong: if (!(error instanceof PlatformError && error.cause))
<=> if ((error not-instanceof PlatformError) or (!error.cause))
, then we show the cause. Is this correct?
client/src/www/model/errors.ts
Outdated
constructor(message: string, options?: {cause?: Error}) { | ||
super(message, options); | ||
constructor(cause?: Error) { | ||
super(undefined, {cause}); |
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.
The undefined
message
seems to be off from the best practice of JavaScript's Error
object:
The message data property of an Error instance is a human-readable description of the error.
Maybe we can have a default human-readable message
?
|
||
// TODO(fortuna): Remove PlatformError from the model. | ||
// PlatformError is an implementation detail. It does not belong in the model. | ||
// It's also about serialization, we should not use it in application code. |
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.
👍 As we have a unified MethodChannel
now, we can convert the errors there, without exporting any internal implementations.
@@ -45,7 +45,7 @@ func fetchResource(url string) (string, error) { | |||
resp.Body.Close() | |||
if resp.StatusCode > 299 { | |||
return "", platerrors.PlatformError{ | |||
Code: platerrors.FetchConfigFailed, | |||
Code: platerrors.GenericErr, |
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 might be useful to expose this error details? Especially the HTTP response code?
Closing in favor of the simpler #2346 |
convertRawErrorObjectToPlatformError
is now a place where we can convert from thePlatformError
serialized errors to application errors.Note how it deletes a significant amount of code. We were representing the same errors multiple times.
Before this PR, deserialization had to happen at different places in the code, complicating application code and making it impractical to change serialization. Error deserialization is now centralized, and we need to move away from using PlatformError in application code.
The motivation for this PR was propagating the Provider error from the dynamic key. It has a message and a detailed message, and our application error models exactly that. With PlatformError I will have to create my own schema to encode that in the error, but then the application code had to be aware of that schema as well in order to use the messages correctly, which was problematic.