-
Notifications
You must be signed in to change notification settings - Fork 70
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
Error handling improvements #570
Error handling improvements #570
Conversation
Ahh I see, I didn't even look at the github diff (my bad) but yeah I will address that. Thanks! |
Marking as draft until we get 0.17 CI passing in general, then will update this branch and re-mark as ready for review |
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.
Great work, loving the docs and the tests 👍
// If Error trait is present (must be 'client' or 'server') and | ||
// the status code is in the client/server error range, then use that alt | ||
val isClientError = | ||
e.value.toLowerCase == "client" && inputStatus >= 400 && inputStatus < 500 |
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.
nitpick : if e == Error.Client
} | ||
.collect { | ||
case (Some(key), values) if values.size == 1 => key -> values.head | ||
private val byStatusCode: Int => Option[SchemaAlt[E, _]] = { inputStatus => |
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.
semi nitpick : hotpath
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 thought about this and even checkout the branch to give it a shot, it got hairy pretty quickly
Will give it another shot, I'm probably missing something
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.
second attempt:
diff --git a/modules/core/src/smithy4s/http/ErrorAltPicker.scala b/modules/core/src/smithy4s/http/ErrorAltPicker.scala
index 91c297df..cb57cdad 100644
--- a/modules/core/src/smithy4s/http/ErrorAltPicker.scala
+++ b/modules/core/src/smithy4s/http/ErrorAltPicker.scala
@@ -41,29 +41,51 @@ final class ErrorAltPicker[E](alts: Vector[SchemaAlt[E, _]]) {
.map(alt => alt.instance.shapeId.name -> alt)
.toMap[String, SchemaAlt[E, _]]
- private val byStatusCode: Int => Option[SchemaAlt[E, _]] = { inputStatus =>
- val errorForStatus: Option[SchemaAlt[E, _]] = {
- val results =
- alts.filter(_.hints.get(HttpError).exists(_.value == inputStatus))
- if (results.length == 1) results.headOption else None
- }
- lazy val fallbackError: Option[SchemaAlt[E, _]] = {
- val matchingAlts = alts.filter { alt =>
- alt.hints.get(Error) match {
- case Some(e) if alt.hints.get(HttpError).isEmpty =>
- // If Error trait is present (must be 'client' or 'server') and
- // the status code is in the client/server error range, then use that alt
- val isClientError =
- e.value.toLowerCase == "client" && inputStatus >= 400 && inputStatus < 500
- val isServerError =
- e.value.toLowerCase == "server" && inputStatus >= 500 && inputStatus < 600
- isClientError || isServerError
- case _ => false
- }
+ // build a map: status code to alternative
+ // exclude all status code that are used on multiple alternative
+ // in essence, it gives a `Map[Int, SchemaAlt[E, _]]` that's used
+ // for the lookup
+ private val byStatusCode: Int => Option[SchemaAlt[E, _]] = {
+ val perStatusCode: Map[Int, SchemaAlt[E, _]] = alts
+ .flatMap { alt =>
+ alt.hints.get(HttpError).map { he => he.value -> alt }
+ }
+ .groupMap(_._1)(_._2)
+ .collect {
+ // Discard alternative where another alternative has the same http status code
+ case (status, allAlts) if allAlts.size == 1 => status -> allAlts.head
+ }
+ val errorForStatus: Int => Option[SchemaAlt[E, _]] = perStatusCode.get
+
+ lazy val fallbackError: Int => Option[SchemaAlt[E, _]] = {
+ // grab the alt that's annotated with the expected `Error` hint
+ // only if there is only one
+ def forErrorType(expected: Error): Option[SchemaAlt[E, _]] = {
+ val matchingAlts = alts
+ .flatMap { alt =>
+ alt.hints
+ .get(HttpError)
+ .fold(
+ alt.hints.get(Error).collect {
+ case e if e == expected => alt
+ }
+ )(_ => None)
+
+ }
+ if (matchingAlts.size == 1) matchingAlts.headOption else None
+ }
+ val clientAlt: Option[SchemaAlt[E, _]] = forErrorType(Error.CLIENT)
+ val serverAlt: Option[SchemaAlt[E, _]] = forErrorType(Error.SERVER)
+
+ { intStatus =>
+ if (intStatus >= 400 && intStatus < 500) clientAlt
+ else if (intStatus >= 500 && intStatus < 600) serverAlt
+ else None
}
- if (matchingAlts.size == 1) matchingAlts.headOption else None
}
- errorForStatus.orElse(fallbackError)
+
+ inputStatus =>
+ errorForStatus(inputStatus).orElse(fallbackError(inputStatus))
}
def getPreciseAlternative(
side note: I'm not sure we should consider error paths as hot path
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 can push it if you like it @lewisjkl
Updates error handling to close #413.
See attached docs in markdown file in PR for more information.