diff --git a/src/main/java/com/google/api/generator/gapic/composer/ServiceClientClassComposer.java b/src/main/java/com/google/api/generator/gapic/composer/ServiceClientClassComposer.java index f9e99d7b18..177fffb162 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/ServiceClientClassComposer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/ServiceClientClassComposer.java @@ -462,12 +462,12 @@ private static List createServiceMethods( Service service, Map messageTypes, Map types) { List javaMethods = new ArrayList<>(); for (Method method : service.methods()) { - if (method.stream().equals(Stream.NONE) && !method.hasLro()) { + if (method.stream().equals(Stream.NONE)) { javaMethods.addAll(createMethodVariants(method, messageTypes, types)); + javaMethods.add(createMethodDefaultMethod(method, types)); } if (method.hasLro()) { - javaMethods.add(createLroAsyncMethod(service.name(), method, types)); - javaMethods.add(createLroCallable(service.name(), method, types)); + javaMethods.add(createLroCallableMethod(service.name(), method, types)); } if (method.isPaged()) { javaMethods.add(createPagedCallableMethod(service.name(), method, types)); @@ -486,6 +486,18 @@ private static List createMethodVariants( method.isPaged() ? types.get(String.format(PAGED_RESPONSE_TYPE_NAME_PATTERN, method.name())) : method.outputType(); + if (method.hasLro()) { + LongrunningOperation lro = method.lro(); + methodOutputType = + TypeNode.withReference( + types + .get("OperationFuture") + .reference() + .copyAndSetGenerics( + Arrays.asList( + lro.responseType().reference(), lro.metadataType().reference()))); + } + String methodInputTypeName = methodInputType.reference().name(); Reference listRef = ConcreteReference.withClazz(List.class); Reference mapRef = ConcreteReference.withClazz(Map.class); @@ -603,7 +615,7 @@ private static List createMethodVariants( // Return expression. MethodInvocationExpr returnExpr = MethodInvocationExpr.builder() - .setMethodName(methodName) + .setMethodName(String.format(method.hasLro() ? "%sAsync" : "%s", methodName)) .setArguments(Arrays.asList(requestVarExpr.toBuilder().setIsDecl(false).build())) .setReturnType(methodOutputType) .build(); @@ -615,14 +627,37 @@ private static List createMethodVariants( .setScope(ScopeNode.PUBLIC) .setIsFinal(true) .setReturnType(methodOutputType) - .setName(methodName) + .setName(String.format(method.hasLro() ? "%sAsync" : "%s", methodName)) .setArguments(arguments) .setBody(statements) .setReturnExpr(returnExpr) .build()); } - // Finally, construct the method that accepts a request proto. + return javaMethods; + } + + private static MethodDefinition createMethodDefaultMethod( + Method method, Map types) { + String methodName = JavaStyle.toLowerCamelCase(method.name()); + TypeNode methodInputType = method.inputType(); + TypeNode methodOutputType = + method.isPaged() + ? types.get(String.format(PAGED_RESPONSE_TYPE_NAME_PATTERN, method.name())) + : method.outputType(); + if (method.hasLro()) { + LongrunningOperation lro = method.lro(); + methodOutputType = + TypeNode.withReference( + types + .get("OperationFuture") + .reference() + .copyAndSetGenerics( + Arrays.asList( + lro.responseType().reference(), lro.metadataType().reference()))); + } + + // Construct the method that accepts a request proto. VariableExpr requestArgVarExpr = VariableExpr.builder() .setVariable(Variable.builder().setName("request").setType(methodInputType).build()) @@ -632,79 +667,32 @@ private static List createMethodVariants( method.isPaged() ? String.format(PAGED_CALLABLE_NAME_PATTERN, methodName) : String.format(CALLABLE_NAME_PATTERN, methodName); + if (method.hasLro()) { + callableMethodName = String.format(OPERATION_CALLABLE_NAME_PATTERN, methodName); + } + MethodInvocationExpr methodReturnExpr = MethodInvocationExpr.builder().setMethodName(callableMethodName).build(); methodReturnExpr = MethodInvocationExpr.builder() - .setMethodName("call") + .setMethodName(method.hasLro() ? "futureCall" : "call") .setArguments(Arrays.asList(requestArgVarExpr.toBuilder().setIsDecl(false).build())) .setExprReferenceExpr(methodReturnExpr) .setReturnType(methodOutputType) .build(); - javaMethods.add( - MethodDefinition.builder() - .setHeaderCommentStatements( - ServiceClientCommentComposer.createRpcMethodHeaderComment(method)) - .setScope(ScopeNode.PUBLIC) - .setIsFinal(true) - .setReturnType(methodOutputType) - .setName(methodName) - .setArguments(Arrays.asList(requestArgVarExpr)) - .setReturnExpr(methodReturnExpr) - .build()); - - return javaMethods; - } - - private static MethodDefinition createLroAsyncMethod( - String serviceName, Method method, Map types) { - // TODO(miraleung): Create variants as well. - Preconditions.checkState( - method.hasLro(), String.format("Method %s does not have an LRO", method.name())); - String methodName = JavaStyle.toLowerCamelCase(method.name()); - TypeNode methodInputType = method.inputType(); - TypeNode methodOutputType = method.outputType(); - String methodInputTypeName = methodInputType.reference().name(); - LongrunningOperation lro = method.lro(); - - VariableExpr argumentExpr = - VariableExpr.builder() - .setVariable(Variable.builder().setName("request").setType(methodInputType).build()) - .setIsDecl(true) - .build(); - - TypeNode returnType = - TypeNode.withReference( - types - .get("OperationFuture") - .reference() - .copyAndSetGenerics( - Arrays.asList(lro.responseType().reference(), lro.metadataType().reference()))); - MethodInvocationExpr returnExpr = - MethodInvocationExpr.builder() - .setMethodName(String.format("%sOperationCallable", methodName)) - .build(); - returnExpr = - MethodInvocationExpr.builder() - .setMethodName("futureCall") - .setArguments(Arrays.asList(argumentExpr.toBuilder().setIsDecl(false).build())) - .setExprReferenceExpr(returnExpr) - .setReturnType(returnType) - .build(); - return MethodDefinition.builder() .setHeaderCommentStatements( ServiceClientCommentComposer.createRpcMethodHeaderComment(method)) .setScope(ScopeNode.PUBLIC) .setIsFinal(true) - .setReturnType(returnType) - .setName(String.format("%sAsync", methodName)) - .setArguments(Arrays.asList(argumentExpr)) - .setReturnExpr(returnExpr) + .setReturnType(methodOutputType) + .setName(String.format(method.hasLro() ? "%sAsync" : "%s", methodName)) + .setArguments(Arrays.asList(requestArgVarExpr)) + .setReturnExpr(methodReturnExpr) .build(); } - private static MethodDefinition createLroCallable( + private static MethodDefinition createLroCallableMethod( String serviceName, Method method, Map types) { return createCallableMethod(serviceName, method, types, CallableMethodKind.LRO); } diff --git a/src/main/java/com/google/api/generator/gapic/composer/ServiceClientTestClassComposer.java b/src/main/java/com/google/api/generator/gapic/composer/ServiceClientTestClassComposer.java index 4fafe9b49b..efd9732e3b 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/ServiceClientTestClassComposer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/ServiceClientTestClassComposer.java @@ -609,9 +609,10 @@ private static MethodDefinition createRpcTestMethod( .build()); } else { for (MethodArgument methodArg : methodSignature) { + String methodArgName = JavaStyle.toLowerCamelCase(methodArg.name()); VariableExpr varExpr = VariableExpr.withVariable( - Variable.builder().setType(methodArg.type()).setName(methodArg.name()).build()); + Variable.builder().setType(methodArg.type()).setName(methodArgName).build()); argExprs.add(varExpr); Expr valExpr = DefaultValueComposer.createDefaultValue(methodArg, resourceNames); methodExprs.add( @@ -1385,9 +1386,10 @@ private static List createRpcExceptionTestStatements( .build()); } else { for (MethodArgument methodArg : methodSignature) { + String methodArgName = JavaStyle.toLowerCamelCase(methodArg.name()); VariableExpr varExpr = VariableExpr.withVariable( - Variable.builder().setType(methodArg.type()).setName(methodArg.name()).build()); + Variable.builder().setType(methodArg.type()).setName(methodArgName).build()); argVarExprs.add(varExpr); Expr valExpr = DefaultValueComposer.createDefaultValue(methodArg, resourceNames); tryBodyExprs.add( diff --git a/src/test/java/com/google/api/generator/gapic/composer/ServiceClientClassComposerTest.java b/src/test/java/com/google/api/generator/gapic/composer/ServiceClientClassComposerTest.java index 371b184823..0f210ac1a5 100644 --- a/src/test/java/com/google/api/generator/gapic/composer/ServiceClientClassComposerTest.java +++ b/src/test/java/com/google/api/generator/gapic/composer/ServiceClientClassComposerTest.java @@ -83,6 +83,8 @@ public void generateServiceClasses() { + "import com.google.common.util.concurrent.MoreExecutors;\n" + "import com.google.longrunning.Operation;\n" + "import com.google.longrunning.OperationsClient;\n" + + "import com.google.protobuf.Duration;\n" + + "import com.google.protobuf.Timestamp;\n" + "import com.google.rpc.Status;\n" + "import com.google.showcase.v1beta1.stub.EchoStub;\n" + "import com.google.showcase.v1beta1.stub.EchoStubSettings;\n" @@ -378,6 +380,32 @@ public void generateServiceClasses() { + " /**\n" + " * Sample code:\n" + " *\n" + + " * @param ttl\n" + + " * @throws com.google.api.gax.rpc.ApiException if the remote call fails\n" + + " */\n" + + " public final OperationFuture waitAsync(Duration ttl)" + + " {\n" + + " WaitRequest request = WaitRequest.newBuilder().setTtl(ttl).build();\n" + + " return waitAsync(request);\n" + + " }\n" + + "\n" + + " // AUTO-GENERATED DOCUMENTATION AND METHOD.\n" + + " /**\n" + + " * Sample code:\n" + + " *\n" + + " * @param end_time\n" + + " * @throws com.google.api.gax.rpc.ApiException if the remote call fails\n" + + " */\n" + + " public final OperationFuture waitAsync(Timestamp" + + " endTime) {\n" + + " WaitRequest request = WaitRequest.newBuilder().setEndTime(endTime).build();\n" + + " return waitAsync(request);\n" + + " }\n" + + "\n" + + " // AUTO-GENERATED DOCUMENTATION AND METHOD.\n" + + " /**\n" + + " * Sample code:\n" + + " *\n" + " * @param request The request object containing all of the parameters for the API" + " call.\n" + " * @throws com.google.api.gax.rpc.ApiException if the remote call fails\n" diff --git a/src/test/java/com/google/api/generator/gapic/composer/ServiceClientTestClassComposerTest.java b/src/test/java/com/google/api/generator/gapic/composer/ServiceClientTestClassComposerTest.java index 59ec4929e1..973ba9393c 100644 --- a/src/test/java/com/google/api/generator/gapic/composer/ServiceClientTestClassComposerTest.java +++ b/src/test/java/com/google/api/generator/gapic/composer/ServiceClientTestClassComposerTest.java @@ -83,6 +83,8 @@ public void generateServiceClasses() { + "import com.google.longrunning.Operation;\n" + "import com.google.protobuf.AbstractMessage;\n" + "import com.google.protobuf.Any;\n" + + "import com.google.protobuf.Duration;\n" + + "import com.google.protobuf.Timestamp;\n" + "import com.google.rpc.Status;\n" + "import io.grpc.StatusRuntimeException;\n" + "import java.io.IOException;\n" @@ -637,16 +639,13 @@ public void generateServiceClasses() { + " .setResponse(Any.pack(expectedResponse))\n" + " .build();\n" + " mockEcho.addResponse(resultOperation);\n" - + " WaitRequest request = WaitRequest.newBuilder().build();\n" - + " WaitResponse actualResponse = client.waitAsync(request).get();\n" + + " Duration ttl = Duration.newBuilder().build();\n" + + " WaitResponse actualResponse = client.waitAsync(ttl).get();\n" + " Assert.assertEquals(expectedResponse, actualResponse);\n" + " List actualRequests = mockEcho.getRequests();\n" + " Assert.assertEquals(1, actualRequests.size());\n" + " WaitRequest actualRequest = ((WaitRequest) actualRequests.get(0));\n" - + " Assert.assertEquals(request.getEndTime(), actualRequest.getEndTime());\n" - + " Assert.assertEquals(request.getTtl(), actualRequest.getTtl());\n" - + " Assert.assertEquals(request.getError(), actualRequest.getError());\n" - + " Assert.assertEquals(request.getSuccess(), actualRequest.getSuccess());\n" + + " Assert.assertEquals(ttl, actualRequest.getTtl());\n" + " Assert.assertTrue(\n" + " channelProvider.isHeaderSent(\n" + " ApiClientHeaderProvider.getDefaultApiClientHeaderKey(),\n" @@ -659,8 +658,50 @@ public void generateServiceClasses() { + " StatusRuntimeException(io.grpc.Status.INVALID_ARGUMENT);\n" + " mockEcho.addException(exception);\n" + " try {\n" - + " WaitRequest request = WaitRequest.newBuilder().build();\n" - + " client.waitAsync(request).get();\n" + + " Duration ttl = Duration.newBuilder().build();\n" + + " client.waitAsync(ttl).get();\n" + + " Assert.fail(\"No exception raised\");\n" + + " } catch (ExecutionException e) {\n" + + " Assert.assertEquals(InvalidArgumentException.class, e.getCause().getClass());\n" + + " InvalidArgumentException apiException = ((InvalidArgumentException)" + + " e.getCause());\n" + + " Assert.assertEquals(StatusCode.Code.INVALID_ARGUMENT," + + " apiException.getStatusCode().getCode());\n" + + " }\n" + + " }\n" + + "\n" + + " @Test\n" + + " public void waitTest2() {\n" + + " WaitResponse expectedResponse =\n" + + " WaitResponse.newBuilder().setContent(\"content951530617\").build();\n" + + " Operation resultOperation =\n" + + " Operation.newBuilder()\n" + + " .setName(\"waitTest\")\n" + + " .setDone(true)\n" + + " .setResponse(Any.pack(expectedResponse))\n" + + " .build();\n" + + " mockEcho.addResponse(resultOperation);\n" + + " Timestamp endTime = Timestamp.newBuilder().build();\n" + + " WaitResponse actualResponse = client.waitAsync(endTime).get();\n" + + " Assert.assertEquals(expectedResponse, actualResponse);\n" + + " List actualRequests = mockEcho.getRequests();\n" + + " Assert.assertEquals(1, actualRequests.size());\n" + + " WaitRequest actualRequest = ((WaitRequest) actualRequests.get(0));\n" + + " Assert.assertEquals(endTime, actualRequest.getEndTime());\n" + + " Assert.assertTrue(\n" + + " channelProvider.isHeaderSent(\n" + + " ApiClientHeaderProvider.getDefaultApiClientHeaderKey(),\n" + + " GaxGrpcProperties.getDefaultApiClientHeaderPattern()));\n" + + " }\n" + + "\n" + + " @Test\n" + + " public void waitExceptionTest2() throws Exception {\n" + + " StatusRuntimeException exception = new" + + " StatusRuntimeException(io.grpc.Status.INVALID_ARGUMENT);\n" + + " mockEcho.addException(exception);\n" + + " try {\n" + + " Timestamp endTime = Timestamp.newBuilder().build();\n" + + " client.waitAsync(endTime).get();\n" + " Assert.fail(\"No exception raised\");\n" + " } catch (ExecutionException e) {\n" + " Assert.assertEquals(InvalidArgumentException.class, e.getCause().getClass());\n" diff --git a/src/test/java/com/google/api/generator/gapic/testdata/echo.proto b/src/test/java/com/google/api/generator/gapic/testdata/echo.proto index e127d7ea40..19058ab680 100644 --- a/src/test/java/com/google/api/generator/gapic/testdata/echo.proto +++ b/src/test/java/com/google/api/generator/gapic/testdata/echo.proto @@ -109,6 +109,8 @@ service Echo { response_type: "WaitResponse" metadata_type: "WaitMetadata" }; + option (google.api.method_signature) = "end_time"; + option (google.api.method_signature) = "ttl"; } // This method will block (wait) for the requested amount of time