Skip to content
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

feat(robot-server): Present error codes in error responses #12969

Merged
merged 8 commits into from
Jun 28, 2023

Conversation

sfoster1
Copy link
Member

@sfoster1 sfoster1 commented Jun 23, 2023

This PR does the equivalent of #12936 for the non-protocol-engine robot server error responses - both the LegacyErrorResponse (which now has an errorCode entry as well as a message, but otherwise not a whole lot else) and the ErrorResponse and MultiErrorResponse, which now have a top level errorCode and also properly intern the exception chain in their multiple-error-body responses.

We also go through and add a couple new useful errors, which is a process that will continue.

Review Requests

  • Does the way we treat MultipleErrorResponse and ErrorResponse make sense? What's the point of the multi error response anyway, given that error response can hold multiple errors?

Testing

  • Make sure that in practice, errors from both legacy and non-legacy routes that don't have anything to do with protocol engine send back errors properly

Next steps

Similarly to #12936 , now that the catchalls and structure is in place we need to go through and make sure that the exceptions that we raise to start any given error response off have error codes and tuned messages.

@sfoster1 sfoster1 requested review from a team as code owners June 23, 2023 16:31
@codecov
Copy link

codecov bot commented Jun 23, 2023

Codecov Report

Merging #12969 (469046e) into edge (f87e3a4) will decrease coverage by 0.05%.
The diff coverage is 55.55%.

Impacted file tree graph

@@            Coverage Diff             @@
##             edge   #12969      +/-   ##
==========================================
- Coverage   72.87%   72.82%   -0.05%     
==========================================
  Files        2351     2362      +11     
  Lines       64445    64470      +25     
  Branches     7190     7199       +9     
==========================================
- Hits        46963    46950      -13     
- Misses      15799    15837      +38     
  Partials     1683     1683              
Flag Coverage Δ
app 71.43% <ø> (-0.14%) ⬇️
protocol-designer 44.44% <ø> (ø)
shared-data 76.38% <55.55%> (-0.25%) ⬇️
step-generation 85.91% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
...server/robot_server/commands/get_default_engine.py 100.00% <ø> (ø)
robot-server/robot_server/commands/router.py 100.00% <ø> (ø)
...obot-server/robot_server/errors/error_responses.py 100.00% <ø> (ø)
...t-server/robot_server/errors/exception_handlers.py 93.47% <ø> (-0.97%) ⬇️
robot-server/robot_server/errors/global_errors.py 100.00% <ø> (ø)
...ot-server/robot_server/robot/calibration/errors.py 100.00% <ø> (ø)
...-server/robot_server/runs/router/actions_router.py 100.00% <ø> (ø)
...bot-server/robot_server/runs/router/base_router.py 96.33% <ø> (ø)
...server/robot_server/runs/router/commands_router.py 100.00% <ø> (ø)
robot-server/robot_server/runs/run_models.py 100.00% <ø> (ø)
... and 9 more

... and 61 files with indirect coverage changes

sfoster1 added 4 commits June 23, 2023 13:39
By adding codes to the response models we can make sure they're always
expressed by all the random overriding models
We can now express error codes in top level error responses and handle
and format them from the eneumerated errors that the rest of the system
will soon throw.

There's some interesting detail there around the MultiErrorResponse and
the ErrorResponse but with multiple errors captured inside of it... I'm
not sure what the right play is, but this is working out for me.

There are some response format changes for some of the messages; we can
fix these by making the exceptions that are being turned into the error
responses EnumeratedErrors that are properly formatted.
@sfoster1 sfoster1 force-pushed the robot-server-error-codes branch from 4a0a575 to ab7290a Compare June 23, 2023 17:49
@sfoster1
Copy link
Member Author

Openapi docs and spec now show the error code in error response schemas with a default of 4000!

@sfoster1 sfoster1 requested review from a team and fsinapi June 23, 2023 18:13
Copy link
Contributor

@fsinapi fsinapi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had one question on pydantic model typing and a couple little doc typos. LGTM but I think I lack context on the impact on model changes so I'll hold off approving.

detail: Optional[Dict[str, Any]] = None,
wrapping: Optional[Sequence[EnumeratedError]] = None,
) -> None:
"""Build a CanbusCommunicationError."""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lil typo

detail: Optional[Dict[str, Any]] = None,
wrapping: Optional[Sequence[EnumeratedError]] = None,
) -> None:
"""Build an GripperNotPresentError."""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo

@@ -105,6 +106,48 @@ def get_some_model():
"occurrence of the error"
),
)
errorCode: str = Field(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the type be Optional[str] since there's a default of None?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's weird that it allows that. i should change that to default to 4000 anyway

Copy link
Contributor

@SyntaxColoring SyntaxColoring left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the way we treat MultipleErrorResponse and ErrorResponse make sense? What's the point of the multi error response anyway, given that error response can hold multiple errors?

Hmm. It looks like this was introduced in #7783. I don't know if this was the intent, but theoretically, it lets us provide a stricter model when we know we're only returning a single error. (Which is almost always.) Like, we can override the OpenAPI spec to include "minItems": 1, "maxItems": 1 or something.

checked_exc = exc

return cls(
message=checked_exc.message.strip(), errorCode=checked_exc.code.value.code
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to .strip() the message?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for a reason I can't be bothered with it comes with a trailing newline

Comment on lines +48 to +61
@classmethod
def from_exc(
cls: Type["FirmwareUpdateRequired"],
exc: BaseException,
**supplemental_kwargs: Any
) -> "FirmwareUpdateRequired":
"""Build a FirmwareUpdateRequired from a specific exception. Preserves metadata."""
parent_inst = ErrorDetails.from_exc(exc, **supplemental_kwargs)
inst = FirmwareUpdateRequired(**parent_inst.dict())
if not inst.meta:
inst.meta = {"update_url": "/subsystems/update"}
else:
inst.meta["update_url"] = "/subsystems/update"
return inst
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me make sure I understand what's going on here:

meta normally includes attributes automatically taken from the Python exception. But when we're returning a FirmwareUpdateRequired model, we also want to include a helpful update_url pointer. That won't be present in the original Python exception, so we need to add it here.

Is that right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah pretty much. It's pretty equally metadata, so it doesn't really do any harm to put it in there next to all the other stuff

Comment on lines -124 to +122
raise RunNotFound(detail=str(e)).as_error(status.HTTP_404_NOT_FOUND) from e
raise RunNotFound.from_exc(e).as_error(status.HTTP_404_NOT_FOUND) from e
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very very very happy that we're making this broken detail=str(e) pattern less pervasive.

This raise SomeModel.from_exc(..).as_error(...) pattern is the confusing back-and-forth thing between models and exceptions that you were talking about before, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, and it's worse in some other places like the exception handlers, where we

  1. catch an exception
  2. build a model from it
  3. create an exception from the model with as_error
  4. pass that somewhere else
  5. ultimately put the exception instance's .content in the response

What I'd really prefer is that exceptions get turned into models and models get sent, without going back into ApiErrors.

@sfoster1 sfoster1 merged commit fc667aa into edge Jun 28, 2023
@sfoster1 sfoster1 deleted the robot-server-error-codes branch June 28, 2023 15:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants