-
Notifications
You must be signed in to change notification settings - Fork 46
Add tests for #2608 #2623
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
base: mpritham/exceptions-with-rich-params
Are you sure you want to change the base?
Add tests for #2608 #2623
Conversation
eaab496 to
6e392f4
Compare
| public static final class DifferentPackageErrorException extends RemoteException { | ||
| private DifferentPackageErrorSerializableError error; |
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.
RemoteException is serializable, due to being a subclass of Throwable (and contains a serialVersionUid). I'm wondering whether this means we need to make all these exceptions and the AbstractSerializableError also serializable (which would raise the question of how to maintain compatibility upon changes)
I don't think we would be relying on this anywhere, but this might be worth to explicitly acknowledge this?
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.
yeah, I'm not sure of the history here, but we use jackson to deserialize some subclass of AbstractSerializableError, and construct this class - it's never deserialized to directly off the wire. I'm not sure that RemoteException is either.
I can acknowledge this in a comment.
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.
All Throwables are Serializable, so it's really just a matter of whether we care. It sounds like RemoteException actually has a serialVersionUid. It sounds like it was added form the get-go though palantir/conjure-java-runtime-api#9 and I can't imagine this being used as serializable anywhere.
With that said, it might be worth just adding a default serialVersionUid on the subclasses nonetheless? 🤔 (adding it after the fact is a bit annoying after all)
| .exception( | ||
| ConjureErrors.CONFLICTING_CAUSE_SAFE_ARG.name(), | ||
| new TypeMarker<ConjureErrors.ConflictingCauseSafeArgSerializableError>() {}, | ||
| new TypeMarker<ConjureErrors.ConflictingCauseSafeArgException>() {}) | ||
| .exception( | ||
| ConjureErrors.CONFLICTING_CAUSE_UNSAFE_ARG.name(), | ||
| new TypeMarker<ConjureErrors.ConflictingCauseUnsafeArgSerializableError>() {}, | ||
| new TypeMarker<ConjureErrors.ConflictingCauseUnsafeArgException>() {}) | ||
| .exception( | ||
| ConjureErrors.ERROR_WITH_COMPLEX_ARGS.name(), | ||
| new TypeMarker<ConjureErrors.ErrorWithComplexArgsSerializableError>() {}, | ||
| new TypeMarker<ConjureErrors.ErrorWithComplexArgsException>() {}) | ||
| .exception( | ||
| ConjureErrors.INVALID_SERVICE_DEFINITION.name(), | ||
| new TypeMarker<ConjureErrors.InvalidServiceDefinitionSerializableError>() {}, | ||
| new TypeMarker<ConjureErrors.InvalidServiceDefinitionException>() {}) | ||
| .exception( | ||
| ConjureErrors.INVALID_TYPE_DEFINITION.name(), | ||
| new TypeMarker<ConjureErrors.InvalidTypeDefinitionSerializableError>() {}, | ||
| new TypeMarker<ConjureErrors.InvalidTypeDefinitionException>() {}) | ||
| .exception( | ||
| ConjureJavaErrors.JAVA_COMPILATION_FAILED.name(), | ||
| new TypeMarker<ConjureJavaErrors.JavaCompilationFailedSerializableError>() {}, | ||
| new TypeMarker<ConjureJavaErrors.JavaCompilationFailedException>() {}) |
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 not contain the error from the different package too? It can be thrown by the service that defined it, right?
| private <T> ExceptionDeserializerArgs<T> createExceptionDeserializerArgs(TypeMarker<T> returnType) { | ||
| return ExceptionDeserializerArgs.<T>builder() | ||
| .returnType(returnType) | ||
| .exception( | ||
| ConjureErrors.CONFLICTING_CAUSE_SAFE_ARG.name(), | ||
| new TypeMarker<ConjureErrors.ConflictingCauseSafeArgSerializableError>() {}, | ||
| new TypeMarker<ConjureErrors.ConflictingCauseSafeArgException>() {}) | ||
| .exception( | ||
| ConjureErrors.CONFLICTING_CAUSE_UNSAFE_ARG.name(), | ||
| new TypeMarker<ConjureErrors.ConflictingCauseUnsafeArgSerializableError>() {}, | ||
| new TypeMarker<ConjureErrors.ConflictingCauseUnsafeArgException>() {}) | ||
| .exception( | ||
| ConjureErrors.ERROR_WITH_COMPLEX_ARGS.name(), | ||
| new TypeMarker<ConjureErrors.ErrorWithComplexArgsSerializableError>() {}, | ||
| new TypeMarker<ConjureErrors.ErrorWithComplexArgsException>() {}) | ||
| .exception( | ||
| ConjureErrors.INVALID_SERVICE_DEFINITION.name(), | ||
| new TypeMarker<ConjureErrors.InvalidServiceDefinitionSerializableError>() {}, | ||
| new TypeMarker<ConjureErrors.InvalidServiceDefinitionException>() {}) | ||
| .exception( | ||
| ConjureErrors.INVALID_TYPE_DEFINITION.name(), | ||
| new TypeMarker<ConjureErrors.InvalidTypeDefinitionSerializableError>() {}, | ||
| new TypeMarker<ConjureErrors.InvalidTypeDefinitionException>() {}) | ||
| .exception( | ||
| ConjureJavaErrors.JAVA_COMPILATION_FAILED.name(), | ||
| new TypeMarker<ConjureJavaErrors.JavaCompilationFailedSerializableError>() {}, | ||
| new TypeMarker<ConjureJavaErrors.JavaCompilationFailedException>() {}) | ||
| .build(); | ||
| } |
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's bugging me a bit that we're defining the exact same method in each service, and then calling it to create new deserializer args for each endpoint (which will be the same if they share a return type).
Do you envision this being some kind of problem or maybe instead a benefit down the line? (e.g. whether on memory allocation, since we're copying the same map of possible hundreds of errors on each endpoint, which might be many as well, or our ability to change the code-gen later on)
Maybe it is better this way, because if we need to later have different exceptions deserializable for different services, we don't need to break the ABI by removing a method that creates the generic deserializer. With that said, I'm not sure when we'd want to not support all errors, but only a subset.
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.
One solution to this could be to create a new file that just has this method which all generated services can use. From the comment above regarding adding all errors in the Conjure definitions, we'd have only one helper method definition in this file.
So concretely, I'm thinking of a file ServiceErrorUtils that is generated which has one method createExceptionDeserializerArgs.
If we end up changing how the deserializers are created, I think we shouldn't have an ABI break?
With that said, I'm not sure when we'd want to not support all errors, but only a subset.
I think this is related to the comment above: whether we filter errors on defined in the same package, or just include every error in the Conjure definition provided. I implemented the former, but I'm sort of learning toward the latter to make migration easier. Some folks end up creating errors in a separate errors package.
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.
So the changes I want to make here:
- take out the filtering on package - consider every error defined for every service
- add a separate utility file for this helper method
With the separate utility file, we will still end up creating loads of maps. One idea is to introduce a cache from returnType -> exceptionDeserializerArgs.
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 chatted about this yesterday. There unfortunately isn't really a great place to put such a file, since it can't be in a predefined package, to avoid conflicts with other dialogue client libraries. I think it's fine to leave it as one method per client for now, and get rid of it if we realize this causes problems. This is also why we have various safe rollout mechanisms after all.
This (and the fact that we can't deserialize exceptions that aren't known at codegen time) makes me wonder however whether we could instead try to discover exceptions at runtime (e.g. through some autoservice or other mechanism, making it possible to build a centralized map of all exceptions, instead of having each client and deserializer to know about them).
| getOptionalBinaryPresentChannel, | ||
| _request.build(), | ||
| _runtime.bodySerDe().optionalInputStreamDeserializer()); | ||
| .call(getOptionalBinaryPresentChannel, _request.build(), getOptionalBinaryPresentDeserializer); |
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.
side-note: the name of this endpoint is so confusing, because I though getOptionalBinaryPresentChannel and getOptionalBinaryPresentDeserializer were methods (e.g. suppliers), when they were in fact fields
| return ExceptionDeserializerArgs.<T>builder() | ||
| .returnType(returnType) | ||
| .build(); |
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.
Is CookieServiceAsync considered as part of a different conjure project? If not, should it also support deserializing all errors?
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 related to the comments above regarding the filtering around what errors are considered. In the implementation, I added filters so services would be able to handle all errors defined in the same package. After looking at some usages, it's probably safest to include all errors defined - some folks put errors in a separate package com.palantir.x.errors.
Even if they didn't, it's possible, like you said, for a service to throw errors defined in a different package.
| // .contains("optional-value"); | ||
| // }); | ||
| // e should be an instance of ErrorWithComplexArgsException, which has rich parameters as well | ||
| assertThat(e.getCause()).isInstanceOfSatisfying(ErrorWithComplexArgsException.class, exception -> { |
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.
Shouldn't this be
| assertThat(e.getCause()).isInstanceOfSatisfying(ErrorWithComplexArgsException.class, exception -> { | |
| assertThat(e).isInstanceOfSatisfying(ErrorWithComplexArgsException.class, exception -> { |
rather than e.getCause()?
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.
Discussed about this yesterday. This is because of the pattern from https://github.com/palantir/dialogue/blob/c17a28c5673cd3988056a4181d38f5f1eae43f59/dialogue-serde/src/main/java/com/palantir/conjure/java/dialogue/serde/DefaultClients.java#L148.
We ought to either replicate this pattern with the new remote exceptions, or instead get rid of it entirely (and do what is done for other types of exceptions)
05f862d to
76484ec
Compare
6e392f4 to
ed1a75d
Compare
76484ec to
b13b56d
Compare
ed1a75d to
1e4f594
Compare
[skip ci]
58ed0e4 to
9394834
Compare
1e4f594 to
948b05d
Compare
aldexis
left a comment
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.
Didn't do a very deep review of the code changes (i.e. I didn't inspect each line, because they seem mostly duplicates of each other), so I could have missed one specific change that is problematic among all the others, but this seems fine to me 👍
63549f0 to
a2135bf
Compare
|
This PR has been automatically marked as stale because it has not been touched in the last 14 days. If you'd like to keep it open, please leave a comment or add the 'long-lived' label, otherwise it'll be closed in 7 days. |
Separating #2608 to make it easier to review. This PR should be updated to create separate test files instead of using the existing
dialoguetest case, but using the existing test case makes it easy to see what concretely changes when the new functionality isused.