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

Modifying Contract to support passing all parameters to encoders #1448 #1459

Merged
merged 6 commits into from
Jul 20, 2021

Conversation

fabiocarvalho777
Copy link
Contributor

This PR addresses issue #1448

@velo @kdavisk6 please just let me know if you have any requests for changes, or if this PR should be opened against a different branch and I will do it. Thank you very much.

@fabiocarvalho777
Copy link
Contributor Author

I noticed CircleCI build failed. However, the error is not related to my changes. Here is the error:

[ERROR] Failed to execute goal com.mycila:license-maven-plugin:3.0:check (default) on project feign-core: Some files do not have the expected license header -> [Help 1]

When building from my laptop I skip that license header check by adding -Dlicense.skip=true to my Maven command. I am surprised though the build in CircleCI is failing because of that.

What should be done to avoid this build error in CircleCI?

@velo
Copy link
Member

velo commented Jul 13, 2021

Run license:format locally and you should manage to fix whatever needs fixing.

@fabiocarvalho777
Copy link
Contributor Author

@velo build passed.

@velo
Copy link
Member

velo commented Jul 14, 2021

Sweet, I gave it a very initial pass. I see no tests for it, could you please include some?!

@fabiocarvalho777
Copy link
Contributor Author

@velo added tests. Builds passed. Thanks.

@fabiocarvalho777
Copy link
Contributor Author

@velo hello. Checking if you had a chance to review the PR. Thanks.

@velo
Copy link
Member

velo commented Jul 19, 2021

@velo hello. Checking if you had a chance to review the PR. Thanks.

thanks Fabio, I downloaded a copy of your changes here, and I'm tinkering around, trying to avoid this boolean, in an attempt to create a more generalized solution. Hopefully I will have an answer over the weekend

Copy link
Member

@kdavisk6 kdavisk6 left a comment

Choose a reason for hiding this comment

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

Meets the need and fits as a minimum viable release. I'm going to approve it, pending additional comments from @velo

@kdavisk6 kdavisk6 added the feedback provided Feedback has been provided to the author label Jul 19, 2021
@fabiocarvalho777
Copy link
Contributor Author

@velo hello. Checking if you had a chance to review the PR. Thanks.

thanks Fabio, I downloaded a copy of your changes here, and I'm tinkering around, trying to avoid this boolean, in an attempt to create a more generalized solution. Hopefully I will have an answer over the weekend

Thanks. Let me know if you have any question, or need any change.

@velo
Copy link
Member

velo commented Jul 20, 2021

I still feel this should be a contract-only change.

Contract holds the magic on how to prepare payloads for encoders, so, it alone should be able to "translate" method arguments into an Object[] or Map<> or even a different object assembled method arguments. Just haven't figure HOW yet.

I just don't wanna introduce a "fabio" specific change now, to only figure that we should have done something else later.

@fabiocarvalho777
Copy link
Contributor Author

I still feel this should be a contract-only change.

Sorry, I thought this is the Contact change we have been discussing. Isn't it? As opposed to changing the Encoder API.

it alone should be able to "translate" method arguments into an Object[] or Map<> or even a different object assembled method arguments

But that is exactly what this solution does, it delivers all method arguments into a Object[]. And all of that preserving the current APIs (except for the addition of alwaysEncodeBody).

I just don't wanna introduce a "fabio" specific change now, to only figure that we should have done something else later.

I hear you, no one wants specific solutions, including myself, trust me, otherwise the project becomes unmaintainable and nobody wants their projects to rely on unmainatable libraries. I have maintained 3 different open source projects, so I know what you are talking about. I am just not sure that is the case here, as (please correct me if I am wrong) the PR meets what has been asked in the discussion in this thread, including the points in your last comment.

Anyways, please let me know if you have any other idea, or if there is anything else I can do to help, I would be glad to change anything and retest.

Thanks.

@velo
Copy link
Member

velo commented Jul 20, 2021

Sorry, I thought this is the Contact change we have been discussing. Isn't it? As opposed to changing the Encoder API

My concern, is this boolean is behaving more like an "body no op contract", and then we need to check the boolean everywhere to ensure correct behavior.

Let's say we have a client like this:

@RequestLine("GET /{id}/something")
@Headers("Authorization: {login}")
public String readSomething(@Param("id") String id, @Param("login") String login, Object body);

Feign behave in very distinct ways with alwaysEncodeBody, and I'm sure this will be generating plenty of support, specially for those using third party encoders that will inherit this "for free"

I was not able to find a different solution for your problem over the weekend, and I won't have the time to get a different approach.

Let's compromise. Instead of having alwaysEncodeBody on BaseContract, that will spread around like wildfire, let's have a new Contract base class with this functionality and let's leave existing feign and third party contracts intact.

I don't exactly have a name suggestion for that class, maybe something like BodyNoOpContract, Body?Contract, AlwaysEncodeBodyBaseContract

If you happy to proceed with that, I would merge and release feign 11.5 today.

@fabiocarvalho777
Copy link
Contributor Author

fabiocarvalho777 commented Jul 20, 2021

@velo Thank you very much. I will implement the changes you mentioned now.

*
* @author fabiocarvalho777@gmail.com
*/
public abstract class AlwaysEncodeBodyContract extends DeclarativeContract {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since the purpose of this class is to allow the body always to be defined by the encoder, there was no need to add alwaysEncodeBody field here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Notice this class extends DeclarativeContract, instead of BaseContract. That is necessary to take advantage of the annotations processors, which is required for extensions of AlwaysEncodeBodyContract.

Copy link
Member

Choose a reason for hiding this comment

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

makes sense.

Copy link
Member

@velo velo left a comment

Choose a reason for hiding this comment

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

Looking good

@@ -111,7 +84,9 @@ protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method me
data.method(method);
data.returnType(Types.resolve(targetType, targetType, method.getGenericReturnType()));
data.configKey(Feign.configKey(targetType, method));
data.alwaysEncodeBody(this.alwaysEncodeBody);
if (AlwaysEncodeBodyContract.class.isAssignableFrom(this.getClass())) {
Copy link
Member

Choose a reason for hiding this comment

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

can't you override this method on the subclass?

Copy link
Member

Choose a reason for hiding this comment

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

na, I don't think it's possible.
ok, nvm

@@ -84,6 +84,9 @@ protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method me
data.method(method);
data.returnType(Types.resolve(targetType, targetType, method.getGenericReturnType()));
data.configKey(Feign.configKey(targetType, method));
if (AlwaysEncodeBodyContract.class.isAssignableFrom(this.getClass())) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This setting is necessary here to avoid having to override this whole method (a copy plus this setting) under AlwaysEncodeBodyContract.

@velo velo merged commit aa96dda into OpenFeign:master Jul 20, 2021
@velo
Copy link
Member

velo commented Jul 21, 2021

@fabiocarvalho777
Copy link
Contributor Author

@velo @kdavisk6 thank you very much, we really appreciate your time and work!!

@velo
Copy link
Member

velo commented Jul 21, 2021

All good, sorry for holding this for so long.

FWIW, the way I would love to see this done (as in the future) is something like this:

@RequestLine("GET /{id}/something")
@Headers("Authorization: {login}")
@Body(expression = "java( new Object[] {id, login, name, age} )")
public String readSomething(@Param("id") String id, @Param("login") String login, String name, int age);

The main advantage of this Body#expression is the flexibility.

Prefer a Map instead, sure:

@Body("java( Map.of("id", id, "name": name, ... ) )")

Do you have your own object, not a problem

@Body("java( new InternalRecord( id, login, new UserIdentifiableInformation(name, age) ) )")

HOW you may ask?! No idea, I just have a dream!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feedback provided Feedback has been provided to the author
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants