Skip to content

Instantly share code, notes, and snippets.

@MaxwellBo
Created December 17, 2019 22:36
Show Gist options
  • Save MaxwellBo/7a1a3a1d1d0a00d7a37484c2e376518c to your computer and use it in GitHub Desktop.
Save MaxwellBo/7a1a3a1d1d0a00d7a37484c2e376518c to your computer and use it in GitHub Desktop.
import io.circe.JsonObject
object BaseExError {
def generateErrorId(): String = UUID.randomUUID().toString.take(8)
def errorDataFromJsonObj(jsonObj: JsonObject): Map[String, String] =
jsonObj.toMap.mapValues(_.noSpaces)
/**
* We sometimes need to generate a message to show to the user given a Throwable.
* e.g. we make several network calls and want to report every error to the user, hiding unexpected exceptions
* under a generic error message.
* We need to ensure we're not leaking errors that shouldn't be leaked
*/
def generateErrorMessage(e: Throwable, errorId: Option[String] = None): String = {
e match {
case err: ExceptionError =>
s"There was an error while processing your request. Please contact customer support. Error ID: ${err.errorId}"
case err@(_: DisplayableExceptionError | _: UserError) => err.getMessage
case _: Throwable => s"There was an error while processing your request. Please contact customer support. Error ID: ${errorId.getOrElse(generateErrorId(): String)}"
}
}
}
sealed trait BaseExError extends BaseError { self: Throwable =>
override val errorMsg: String = this.getMessage
// Each error that's instantiated will have an Error ID. This is used
// to reference a particular error without needing to let the end user
// see the details of the error
val errorId: String = BaseExError.generateErrorId()
/** Any extra data relevant for this error */
def errorData: Map[String, String] = Map.empty
final def asThrowable: Throwable = self
}
/** An error which wraps an Exception. No information will be rendered to the user.
* If you have an internal errors which we want to track through error reporting (e.g. impossible state observerd)
* then you should extend this error
*/
@SuppressWarnings(Array("org.wartremover.warts.Null"))
abstract class ExceptionError(errorMsg: String, cause: Option[Throwable]) extends Exception(errorMsg, cause.orNull) with BaseExError
/**
* An Error with a message that is "user-friendly" (can be shown to the user).
* Extend this error if you want to wrap an exception which can have a user-friendly message
*/
@SuppressWarnings(Array("org.wartremover.warts.Null"))
abstract class DisplayableExceptionError(errorMsg: String, cause: Option[Throwable]) extends Exception(errorMsg, cause.orNull) with BaseExError
/**
* An Exception which the user can fix. This Exception is not reported when caught because it's a user error.
* We also enforce that a subclass of UserError should have a Http code associated with it.
* If you have an error deep down the stack which is caused by user error (and can provide infomation for them to fix it),
* then you can extend this class and throw it.
*/
@SuppressWarnings(Array("org.wartremover.warts.Null"))
abstract class UserError(errorMsg: String, cause: Option[Throwable] = None) extends Exception(errorMsg, cause.orNull) with BaseExError with HttpError
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment