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

Add @ClientFormParam to Reactive REST Client #34535

Merged
merged 1 commit into from
Jul 25, 2023
Merged

Conversation

Eng-Fouad
Copy link
Contributor

Resolves #34477

@quarkus-bot quarkus-bot bot added the area/rest label Jul 5, 2023
@Eng-Fouad Eng-Fouad force-pushed the #34477 branch 2 times, most recently from 06800ad to 61c2ea8 Compare July 5, 2023 03:03
@geoand
Copy link
Contributor

geoand commented Jul 5, 2023

Seems reasonable, but I would like @Sgitario to take a look

Copy link
Contributor

@Sgitario Sgitario left a comment

Choose a reason for hiding this comment

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

The changes look amazing to me. Many thanks!
The only missing part is to update the documentation similar to what is mentioned to @ClientQueryParam: https://quarkus.io/guides/rest-client-reactive#using-clientqueryparam

I'm also running the CI.

@quarkus-bot

This comment has been minimized.

@Sgitario
Copy link
Contributor

Sgitario commented Jul 5, 2023

The test failures do seem related.

@Eng-Fouad
Copy link
Contributor Author

@geoand @Sgitario Do you know why tests fail? I cloned ClientQueryParamFromMethodTest to ClientFormParamFromMethodTest, but it fails.

[INFO] Results:
[INFO] 
Error:  Errors: 
Error:    ClientFormParamFromMethodTest.shouldUseValuesFromClassAndMethod:44 » IllegalArgument Not a REST client interface: interface io.quarkus.rest.client.reactive.form.ClientFormParamFromMethodTest$Client. No @Path annotation found on the class or any methods of the interface and no HTTP method annotations (@POST, @PUT, @GET, @HEAD, @DELETE, etc) found on any of the methods
Error:    ClientFormParamFromMethodTest.shouldUseValuesFromFormParam:60 » IllegalArgument Not a REST client interface: interface io.quarkus.rest.client.reactive.form.ClientFormParamFromMethodTest$Client. No @Path annotation found on the class or any methods of the interface and no HTTP method annotations (@POST, @PUT, @GET, @HEAD, @DELETE, etc) found on any of the methods
Error:    ClientFormParamFromMethodTest.shouldUseValuesFromFormParams:68 » IllegalArgument Not a REST client interface: interface io.quarkus.rest.client.reactive.form.ClientFormParamFromMethodTest$Client. No @Path annotation found on the class or any methods of the interface and no HTTP method annotations (@POST, @PUT, @GET, @HEAD, @DELETE, etc) found on any of the methods
Error:    ClientFormParamFromMethodTest.shouldUseValuesFromMethodWithParam:52 » IllegalArgument Not a REST client interface: interface io.quarkus.rest.client.reactive.form.ClientFormParamFromMethodTest$Client. No @Path annotation found on the class or any methods of the interface and no HTTP method annotations (@POST, @PUT, @GET, @HEAD, @DELETE, etc) found on any of the methods
Error:    ClientFormParamFromMethodTest.shouldUseValuesFromSubclientAnnotations:76 » IllegalArgument Not a REST client interface: interface io.quarkus.rest.client.reactive.form.ClientFormParamFromMethodTest$Client. No @Path annotation found on the class or any methods of the interface and no HTTP method annotations (@POST, @PUT, @GET, @HEAD, @DELETE, etc) found on any of the methods
Error:    ClientFormParamFromMethodTest.shouldUseValuesOnlyFromClass:36 » IllegalArgument Not a REST client interface: interface io.quarkus.rest.client.reactive.form.ClientFormParamFromMethodTest$Client. No @Path annotation found on the class or any methods of the interface and no HTTP method annotations (@POST, @PUT, @GET, @HEAD, @DELETE, etc) found on any of the methods
Error:    ClientFormParamFromPropertyTest.shouldFailOnMissingRequiredProperty:49 » IllegalArgument Not a REST client interface: interface io.quarkus.rest.client.reactive.form.ClientFormParamFromPropertyTest$Client. No @Path annotation found on the class or any methods of the interface and no HTTP method annotations (@POST, @PUT, @GET, @HEAD, @DELETE, etc) found on any of the methods
Error:    ClientFormParamFromPropertyTest.shouldSetFromProperties:41 » IllegalArgument Not a REST client interface: interface io.quarkus.rest.client.reactive.form.ClientFormParamFromPropertyTest$Client. No @Path annotation found on the class or any methods of the interface and no HTTP method annotations (@POST, @PUT, @GET, @HEAD, @DELETE, etc) found on any of the methods
Error:    ClientFormParamFromPropertyTest.shouldSucceedOnMissingNonRequiredProperty:58 » IllegalArgument Not a REST client interface: interface io.quarkus.rest.client.reactive.form.ClientFormParamFromPropertyTest$Client. No @Path annotation found on the class or any methods of the interface and no HTTP method annotations (@POST, @PUT, @GET, @HEAD, @DELETE, etc) found on any of the methods
Error:    ClientFormParamFromPropertyTest.shouldSucceedOnMissingNonRequiredPropertyAndUseOverriddenValue:66 » IllegalArgument Not a REST client interface: interface io.quarkus.rest.client.reactive.form.ClientFormParamFromPropertyTest$Client. No @Path annotation found on the class or any methods of the interface and no HTTP method annotations (@POST, @PUT, @GET, @HEAD, @DELETE, etc) found on any of the methods
[INFO] 
Error:  Tests run: 263, Failures: 0, Errors: 10, Skipped: 1

https://github.com/quarkusio/quarkus/actions/runs/5460063979/jobs/9940583562?pr=34535#step:11:4485

@Eng-Fouad
Copy link
Contributor Author

Eng-Fouad commented Jul 5, 2023

Added documentation, but still I was not able to fix test failures.

@github-actions
Copy link

github-actions bot commented Jul 5, 2023

🙈 The PR is closed and the preview is expired.

@quarkus-bot

This comment has been minimized.

@Eng-Fouad Eng-Fouad requested a review from Sgitario July 5, 2023 20:34
@Sgitario
Copy link
Contributor

Sgitario commented Jul 6, 2023

I'm having a look into the test failures

@Sgitario
Copy link
Contributor

Sgitario commented Jul 6, 2023

I'm having a look into the test failures

The message was indeed misleading, though when running the test on debug mode, you add a breakpoint here, you will see the actual errors.

The problem is that the issue is found in the SubClient and that's why it could not identify the errors.

Anyway, the actual errors are:

  • there are a couple of null pointers because formValues is null here and here.
  • the method definition in here is not correct, I think it should be:
private static final MethodDescriptor MULTI_VALUED_MAP_ADD_ALL_METHOD = MethodDescriptor.ofMethod(MultivaluedMap.class,
            "addAll", void.class, Object.class, List.class);

After making these changes, still didn't work for me because the following exception at runtime:

java.lang.VerifyError: Bad local variable type
Exception Details:
  Location:
    io/quarkus/rest/client/reactive/form/ClientFormParamFromMethodTest$Client$$QuarkusRestClientInterface.setFromFormParam(Ljava/lang/String;)Ljava/lang/String; @116: aload
  Reason:
    Type top (current frame, locals[6]) is not assignable to reference type
  Current Frame:
    bci: @116
    flags: { }
    locals: { 'io/quarkus/rest/client/reactive/form/ClientFormParamFromMethodTest$Client$$QuarkusRestClientInterface', 'java/lang/String', 'java/lang/reflect/Type', '[Ljava/lang/annotation/Annotation;', 'jakarta/ws/rs/core/MultivaluedMap', top, top, top, top, top, top, top, 'jakarta/ws/rs/client/WebTarget' }
    stack: { }
  Bytecode:

But I hope you can continue investigating after my findings.

Copy link
Contributor

@Sgitario Sgitario left a comment

Choose a reason for hiding this comment

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

See my inline comments and last comment to fix the test failures.

@Eng-Fouad
Copy link
Contributor Author

Merged with latest commit from main branch, and now I am getting new error:

[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] /mnt/c/Users/fouad/IdeaProjects/quarkus-fouad/extensions/resteasy-reactive/rest-client-reactive/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/RestClientReactiveProcessor.java:[531,24] method isImplementorOf in class org.jboss.resteasy.reactive.common.processor.JandexUtil cannot be applied to given types;
  required: org.jboss.jandex.IndexView,org.jboss.jandex.ClassInfo,org.jboss.jandex.DotName
  found:    org.jboss.jandex.CompositeIndex,org.jboss.jandex.ClassInfo,org.jboss.jandex.DotName,java.util.Set<org.jboss.jandex.DotName>
  reason: actual and formal argument lists differ in length

I think it is caused by #34570 cc @geoand

@geoand
Copy link
Contributor

geoand commented Jul 6, 2023

You need to rebased on main and update the code

@Eng-Fouad
Copy link
Contributor Author

I'm having a look into the test failures

The message was indeed misleading, though when running the test on debug mode, you add a breakpoint here, you will see the actual errors.

The problem is that the issue is found in the SubClient and that's why it could not identify the errors.

Anyway, the actual errors are:

  • there are a couple of null pointers because formValues is null here and here.
  • the method definition in here is not correct, I think it should be:
private static final MethodDescriptor MULTI_VALUED_MAP_ADD_ALL_METHOD = MethodDescriptor.ofMethod(MultivaluedMap.class,
            "addAll", void.class, Object.class, List.class);

After making these changes, still didn't work for me because the following exception at runtime:

java.lang.VerifyError: Bad local variable type
Exception Details:
  Location:
    io/quarkus/rest/client/reactive/form/ClientFormParamFromMethodTest$Client$$QuarkusRestClientInterface.setFromFormParam(Ljava/lang/String;)Ljava/lang/String; @116: aload
  Reason:
    Type top (current frame, locals[6]) is not assignable to reference type
  Current Frame:
    bci: @116
    flags: { }
    locals: { 'io/quarkus/rest/client/reactive/form/ClientFormParamFromMethodTest$Client$$QuarkusRestClientInterface', 'java/lang/String', 'java/lang/reflect/Type', '[Ljava/lang/annotation/Annotation;', 'jakarta/ws/rs/core/MultivaluedMap', top, top, top, top, top, top, top, 'jakarta/ws/rs/client/WebTarget' }
    stack: { }
  Bytecode:

But I hope you can continue investigating after my findings.

Thanks for your help. I applied all fixes you mentioned. I will try to figure out how to fix java.lang.VerifyError: Bad local variable type.

@Eng-Fouad
Copy link
Contributor Author

You need to rebased on main and update the code

My bad. I had to recompile the project again.

@quarkus-bot

This comment has been minimized.

@Eng-Fouad
Copy link
Contributor Author

Ok, I fixed java.lang.VerifyError: Bad local variable type. However, the tests still fail:

[ERROR] Failures:
[ERROR]   ClientFormParamFromMethodTest.shouldUseValuesOnlyFromClass:39
expected: "1/"
 but was: "/"
[INFO]
[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0

It seems that the form params are not passed in the request.

@Eng-Fouad
Copy link
Contributor Author

Fixed. All tests should pass now.

@Eng-Fouad Eng-Fouad requested a review from Sgitario July 6, 2023 23:45
@quarkus-bot

This comment has been minimized.

Copy link
Contributor

@Sgitario Sgitario left a comment

Choose a reason for hiding this comment

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

There are several concerns about the implementation:

  • Generally speaking, we should be very careful when changing the signature of interfaces like JaxrsClientReactiveEnricher. In this case, we can avoid it (see the following points)
  • JaxrsClientReactiveEnricher.forWebTarget and JaxrsClientReactiveEnricher.forSubResourceWebTarget is meant to enrich the webTarget and when handling the form params, we are not updating the web target.
  • You're using an array to hold a single instance of formParams (as if you were passing a reference parameter).

To avoid these three points, I would create a new method in JaxrsClientReactiveEnricher like handleFormParams that returns the assignable result handle (to avoid the usage of arrays for doing this).

Other comments:

  • The method MicroProfileRestClientEnricher.addFormParam is too long and I think it can be refactored (eg reusing variables, etc).
  • There are several conditions like if (!multipart), I don't fully understand why you ended up with this condition, but form params are not incompatible with multipart. I guess your intention here is not to parse the annotations that are used in Multipart classes? If this scenario is not supported, we should throw an exception to let users that this is not a valid use case. If this was not your intention, I would need further clarification about this to better understand the condition.

@Eng-Fouad
Copy link
Contributor Author

Eng-Fouad commented Jul 7, 2023

There are several concerns about the implementation:

  • Generally speaking, we should be very careful when changing the signature of interfaces like JaxrsClientReactiveEnricher. In this case, we can avoid it (see the following points)
  • JaxrsClientReactiveEnricher.forWebTarget and JaxrsClientReactiveEnricher.forSubResourceWebTarget is meant to enrich the webTarget and when handling the form params, we are not updating the web target.
  • You're using an array to hold a single instance of formParams (as if you were passing a reference parameter).

To avoid these three points, I would create a new method in JaxrsClientReactiveEnricher like handleFormParams that returns the assignable result handle (to avoid the usage of arrays for doing this).

I agree with you, using separate method handleFormParams is much better. I will do that.

Other comments:

  • The method MicroProfileRestClientEnricher.addFormParam is too long and I think it can be refactored (eg reusing variables, etc).

Do you mean like merging addFormParam with addQueryParam (e.g. new method addParam) and then pass method argument that specifies param type?

  • There are several conditions like if (!multipart), I don't fully understand why you ended up with this condition, but form params are not incompatible with multipart. I guess your intention here is not to parse the annotations that are used in Multipart classes? If this scenario is not supported, we should throw an exception to let users that this is not a valid use case. If this was not your intention, I would need further clarification about this to better understand the condition.

In case of multipart, forParams is QuarkusMultipartForm not MultivaluedHashMap. With MultivaluedHashMap, we can simply add it as follows:

forParams.addAll(annotation.name(), Arrays.asList(annotation.value()));

However, with QuarkusMultipartForm, should I use attribute(String name, String value, String filename)? How to construct filename? or should I pass null all the time?

Edit:

I think I can get filename as follows:

String filename = null;
AnnotationInstance partFileName = method.annotation(ResteasyReactiveDotNames.PART_FILE_NAME);
if (partFileName != null && partFileName.value() != null) {
    filename = filepartFileName.value().asString();
}

Is this correct?

@quarkus-bot

This comment has been minimized.

@quarkus-bot

This comment has been minimized.

@Eng-Fouad Eng-Fouad requested a review from Sgitario July 8, 2023 02:56
@Eng-Fouad
Copy link
Contributor Author

@geoand @Sgitario Any updates on this PR? Thanks.

@geoand
Copy link
Contributor

geoand commented Jul 20, 2023

@Sgitario is on PTO, so let's wait for him to have the final say

Copy link
Contributor

@Sgitario Sgitario left a comment

Choose a reason for hiding this comment

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

Back from my PTO. Only two comments to address.
The rest of the changes look good to me. Thanks!

@Eng-Fouad
Copy link
Contributor Author

Back from my PTO. Only two comments to address. The rest of the changes look good to me. Thanks!

Welcome back. I updated the code according to your comments. Thanks.

@quarkus-bot
Copy link

quarkus-bot bot commented Jul 24, 2023

✔️ The latest workflow run for the pull request has completed successfully.

It should be safe to merge provided you have a look at the other checks in the summary.

@Eng-Fouad Eng-Fouad requested a review from Sgitario July 24, 2023 22:01
@Sgitario Sgitario merged commit aef8aba into quarkusio:main Jul 25, 2023
@quarkus-bot quarkus-bot bot added the kind/enhancement New feature or request label Jul 25, 2023
@quarkus-bot quarkus-bot bot added this to the 3.3 - main milestone Jul 25, 2023
@Eng-Fouad Eng-Fouad deleted the #34477 branch July 25, 2023 05:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add @ClientFormParam to Reactive REST Client
3 participants