-
Notifications
You must be signed in to change notification settings - Fork 96
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
Add injection point for handling exceptions #742
Conversation
ae5b873
to
279a162
Compare
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.
Change log needed
|
||
/** | ||
* Allows a customer to inject custom code for handling Exceptions in Fili's DataServlet. | ||
* |
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.
Need a <p>
tag to start the next paragraph
* For example, customers may want to return 500's for some custom exceptions, or return | ||
* an empty set when dimension rows are not found. | ||
*/ | ||
public interface ExceptionHandler { |
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.
Where is this interface used?
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 vestigial, and should be removed.
* For example, customers may want to return 500's for some custom exceptions, or return | ||
* an empty set when dimension rows are not found. | ||
*/ | ||
public interface DataExceptionHandler { |
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.
Why do we make DataExceptionHandler
and MetadataExceptionHandler
separate?
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.
Because of how the two servlets work. The DataServlet uses asynchronous responses, so we need to pass in the asyncResponse for this method to resume.
The metadata servlets meanwhile use synchronous responses (if you look at their get*
methods, you'll see that they return a Response
), so we need to do the same thing in the Metadata interface.
Kinda hard to put both of those behaviors into one interface.
* For example, customers may want to return 500's for some custom exceptions, or return | ||
* an empty set when dimension rows are not found. | ||
*/ | ||
public interface ExceptionHandler { |
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 orphaned?
*/ | ||
Response handleThrowable( | ||
Throwable e, | ||
Optional<ApiRequest> request, |
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.
Should this be ? extends ApiRequest allowing the subclasses to be typesafer?
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 should've commented instead of requesting changes. Please let @michael-mclawhorn take a look as well
* a result, there is not a clean way to unify them into a single exception handler. So they each | ||
* get their own | ||
*/ | ||
public interface MetadataExceptionHandler { |
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 we can have a marker interface ExceptionHandler
whose child interfaces are MetadataExceptionHandler
and DataExceptionHandler
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.
That sounds like unnecessary infrastructure to me. Since both interfaces
have such different signatures, we'll still need to either use the
(Meta)DataExceptionHandler
in the appropriate Servlet, or we'll have to cast.
So I don't know what such a marker interface buys us.
|
||
/** | ||
* The default implementation of {@link DataExceptionHandler}. | ||
* |
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.
<p>
String msg = ErrorMessageFormat.REQUEST_PROCESSING_EXCEPTION.format(e.getMessage()); | ||
LOG.error(msg, e); | ||
responseSender = () -> Response.status(Status.BAD_REQUEST).entity(msg).build(); | ||
return response; |
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 used to have no idea what responseSender is for
@@ -41,10 +41,9 @@ public FiliJobsExceptionHandler(ObjectMappersSuite objectMappers) { | |||
@Override | |||
public Response handleThrowable( | |||
Throwable e, | |||
Optional<ApiRequest> request, | |||
Optional<? extends ApiRequest> request, |
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 was thinking that these would become more type specific. e.g. Optional etc.
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 what you're asking. So far as I know, you can't make a type more specific when implementing an interface method. Are you suggesting providing a separate interface for each metadata endpoint?
); | ||
// Generally, it's expected that implementations of `ExceptionHandler` will resume | ||
// the response in every case. This exists so that if someone writes a buggy handler | ||
// that fails to resume the response, they at least get *something* back. |
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 'just in case' asynch response resume should check if the response is already done and if it /isn't/ send a message indicating that an error was mishandled. (A 500 error, probably, also passing along the error that was badly handled)
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.
Restated below.
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 thing may want for a quick test, maybe directly on dataservlet.getData(), initialize with a bunch of Mocks, throw an NPE early in processing the request, and then have an error handler for NPE catch and respond to it correctly.
Also, now that the error handlers exist externally, it would be very easy to write regression tests instantiating them and stimulating their various exception handling scenarios. It would also be a hint to people building their own to make tests on them.
@michael-mclawhorn I'm hesitant to do a direct test on The only thing the I'm really hesitant to add a brittle test to test something that doesn't happen in the default implementation. That being said, I have no problem adding unit tests for the exception handlers. |
-- Before we hard-coded how we handled exceptions in the Servlets. For example, by default we return a 400 - Bad Request if we get an Exception. However, this is very inflexible. For example, suppose customers have some customizations (like their own SearchProviders) that may generate errors the DataServlet doesn't know to look for, but aren't the fault of the user. Then, Fili currently returns a 400, even though it should probably be some sort of 5xx. So we add an injection point to all servlets that allows customers to provide their own logic for handling DataServlet errors.
-- Also, removes the unnecessary `metadataEntityName` parameter from the `MetadataExceptionHandler`. That was used to pass along the dimension name for one of the error messages in the `DimensionsServlet`, but the `ApiRequest` contains the dimension name already.
-- Use `DataApiRequest` rather than `? extends ApiRequest` in `DataExceptionHandler`. -- Improves some Javadoc.
-- Tests have been added for unit tests, but not the direct test on `DataServlet` that Michael recommended because of how brittle such a test would be.
f6e0efd
to
cf978a4
Compare
Added some tests, as requested by @michael-mclawhorn. Note that I very explicitly did not try to merge the tests under a shared specification, despite them having some shared tests. The implementations are all completely separate, and I think the shared implementation are a coincidence, and not because of any inherent relationship. Also, these days I like keeping my tests self-contained (navigating Specification trees is annoying) if I can, and the shared tests are simple enough that I don't think we got a whole lot out of trying to share them anyway. |
-- Before we hard-coded how we handled exceptions in the Servlets.
For example, by default we return a 400 - Bad Request if we get an
Exception. However, this is very inflexible. For example, suppose
customers have some customizations (like their own SearchProviders) that
may generate errors the DataServlet doesn't know to look for, but aren't
the fault of the user. Then, Fili currently returns a 400, even though
it should probably be some sort of 5xx.
So we add an injection point to all servlets that allows customers to
provide their own logic for handling DataServlet errors.