From 2c7a295e097a72a8b5b2d478533c6cb29c780dfb Mon Sep 17 00:00:00 2001 From: Jose Date: Tue, 13 Dec 2022 06:38:17 +0100 Subject: [PATCH] Reactive Rest Client: Process paths before sub resources Fix https://github.com/quarkusio/quarkus/issues/29712 --- .../JaxrsClientReactiveProcessor.java | 77 ++++++++++++------- .../reactive/subresource/SubResourceTest.java | 15 ++++ 2 files changed, 64 insertions(+), 28 deletions(-) diff --git a/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java b/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java index 39cfc80d38003..86edc4adff49d 100644 --- a/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java +++ b/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java @@ -1161,8 +1161,31 @@ private void handleSubResourceMethod(List ownerContext.constructor.writeInstanceField(forMethodTargetDesc, ownerContext.constructor.getThis(), constructorTarget); - ResultHandle subInstance = ownerMethod.newInstance(subConstructorDescriptor, - ownerMethod.readInstanceField(forMethodTargetDesc, ownerMethod.getThis())); + Supplier methodParamAnnotationsField = ownerContext.getLazyJavaMethodParamAnnotationsField( + methodIndex); + Supplier methodGenericParametersField = ownerContext.getLazyJavaMethodGenericParametersField( + methodIndex); + + AssignableResultHandle client = createRestClientField(name, ownerContext.classCreator, ownerMethod); + AssignableResultHandle webTarget = ownerMethod.createVariable(WebTarget.class); + ownerMethod.assign(webTarget, ownerMethod.readInstanceField(forMethodTargetDesc, ownerMethod.getThis())); + // Setup Path param from current method + for (int i = 0; i < method.getParameters().length; i++) { + MethodParameter param = method.getParameters()[i]; + if (param.parameterType == ParameterType.PATH) { + ResultHandle paramValue = ownerMethod.getMethodParam(i); + // methodTarget = methodTarget.resolveTemplate(paramname, paramvalue); + addPathParam(ownerMethod, webTarget, param.name, paramValue, + param.type, + client, + ownerMethod.readStaticField(methodGenericParametersField.get()), + ownerMethod.readStaticField(methodParamAnnotationsField.get()), + i); + } + } + + // Continue creating the subresource instance with the web target updated + ResultHandle subInstance = ownerMethod.newInstance(subConstructorDescriptor, webTarget); List subParamFields = new ArrayList<>(); @@ -1179,23 +1202,24 @@ private void handleSubResourceMethod(List ownerParameter.paramIndex)); } - FieldDescriptor clientField = createRestClientField(name, ownerContext.classCreator, ownerMethod, - subContext.classCreator, subInstance); - - Supplier methodParamAnnotationsField = ownerContext.getLazyJavaMethodParamAnnotationsField( - methodIndex); - Supplier methodGenericParametersField = ownerContext.getLazyJavaMethodGenericParametersField( - methodIndex); - // method parameters are rewritten to sub client fields (directly, public fields): + FieldDescriptor clientField = subContext.classCreator.getFieldCreator("client", RestClientBase.class) + .setModifiers(Modifier.PUBLIC) + .getFieldDescriptor(); + ownerMethod.writeInstanceField(clientField, subInstance, client); + // method parameters (except path parameters) are rewritten to sub client fields (directly, public fields): for (int i = 0; i < method.getParameters().length; i++) { - FieldDescriptor paramField = subContext.classCreator.getFieldCreator("param" + i, - method.getParameters()[i].type) - .setModifiers(Modifier.PUBLIC) - .getFieldDescriptor(); - ownerMethod.writeInstanceField(paramField, subInstance, ownerMethod.getMethodParam(i)); - subParamFields.add(new SubResourceParameter(method.getParameters()[i], method.getParameters()[i].type, - jandexMethod.parameterType(i), paramField, methodParamAnnotationsField, methodGenericParametersField, - i)); + MethodParameter param = method.getParameters()[i]; + if (param.parameterType != ParameterType.PATH) { + FieldDescriptor paramField = subContext.classCreator.getFieldCreator("param" + i, param.type) + .setModifiers(Modifier.PUBLIC) + .getFieldDescriptor(); + ownerMethod.writeInstanceField(paramField, subInstance, ownerMethod.getMethodParam(i)); + subParamFields.add(new SubResourceParameter(method.getParameters()[i], param.type, + jandexMethod.parameterType(i), paramField, methodParamAnnotationsField, + methodGenericParametersField, + i)); + } + } int subMethodIndex = 0; @@ -1555,22 +1579,19 @@ private void appendPath(MethodCreator constructor, String pathPart, AssignableRe * Create the `client` field into the `c` class that represents a RestClientBase instance. * The RestClientBase instance is coming from either a root client or a sub client (clients generated from root clients). */ - private FieldDescriptor createRestClientField(String name, ClassCreator c, MethodCreator methodCreator, ClassCreator sub, - ResultHandle subInstance) { - FieldDescriptor clientField = sub.getFieldCreator("client", RestClientBase.class) - .setModifiers(Modifier.PUBLIC) - .getFieldDescriptor(); + private AssignableResultHandle createRestClientField(String name, ClassCreator c, MethodCreator methodCreator) { + AssignableResultHandle client = methodCreator.createVariable(RestClientBase.class); if (c.getSuperClass().contains(RestClientBase.class.getSimpleName())) { // We're in a root client, so we can set the client field with: sub.client = (RestClientBase) this - methodCreator.writeInstanceField(clientField, subInstance, methodCreator.getThis()); + methodCreator.assign(client, methodCreator.getThis()); } else { FieldDescriptor subClientField = FieldDescriptor.of(name, "client", RestClientBase.class); - // We're in a sub sub resource, so we need to get the client from the field: subSub.client = sub.client - methodCreator.writeInstanceField(clientField, subInstance, - methodCreator.readInstanceField(subClientField, methodCreator.getThis())); + // We're in a sub-sub resource, so we need to get the client from the field: subSub.client = sub.client + methodCreator.assign(client, methodCreator.readInstanceField(subClientField, methodCreator.getThis())); } - return clientField; + + return client; } private void handleMultipartField(String formParamName, String partType, String partFilename, diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/subresource/SubResourceTest.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/subresource/SubResourceTest.java index ee757d0222937..53e6cc1d39fb1 100644 --- a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/subresource/SubResourceTest.java +++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/subresource/SubResourceTest.java @@ -61,6 +61,14 @@ void shouldPassParamsToSubSubResource() { assertThat(result).isEqualTo("rt/mthd/sub/subSimple"); } + @Test + void shouldPassPathParamsToSubSubResource() { + // should result in sending GET /path/rt/mthd/sub/s/ss + RootClient rootClient = RestClientBuilder.newBuilder().baseUri(baseUri).build(RootClient.class); + String result = rootClient.sub("rt", "mthd").sub("s").get("ss"); + assertThat(result).isEqualTo("rt/mthd/sub/s/ss"); + } + @Test void shouldDoMultiplePosts() { RootClient rootClient = RestClientBuilder.newBuilder().baseUri(baseUri).build(RootClient.class); @@ -129,6 +137,9 @@ interface SubClient { @Path("/sub") SubSubClient sub(); + @Path("/sub/{methodParam}") + SubSubClient sub(@PathParam("methodParam") String methodParam); + @POST @ClientHeaderParam(name = "overridable", value = "SubClient") @ClientHeaderParam(name = "fromSubMethod", value = "{fillingMethod}") @@ -146,6 +157,10 @@ interface SubSubClient { @Path("/subSimple") String simpleSub(); + @GET + @Path("/{methodParam}") + String get(@PathParam("methodParam") String methodParam); + @POST @ClientHeaderParam(name = "overridable", value = "SubSubClient") @ClientHeaderParam(name = "fromSubMethod", value = "{fillingMethod}")