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 07dee2a7b2..f350f0c4a2 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 @@ -806,7 +806,7 @@ private static MethodDefinition createCallableMethod( } // TODO (summerji): Implement sample code for stream method. // Replace by if (method.stream()). - if (method.stream().equals(Stream.SERVER)) { + if (method.stream().equals(Stream.SERVER) || method.stream().equals(Stream.BIDI)) { sampleCodeOpt = Optional.of( ServiceClientSampleCodeComposer.composeStreamCallableMethodHeaderSampleCode( diff --git a/src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientSampleCodeComposer.java b/src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientSampleCodeComposer.java index 12af74b8dc..fd190baca2 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientSampleCodeComposer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientSampleCodeComposer.java @@ -17,6 +17,7 @@ import com.google.api.core.ApiFuture; import com.google.api.gax.core.FixedCredentialsProvider; import com.google.api.gax.longrunning.OperationFuture; +import com.google.api.gax.rpc.BidiStream; import com.google.api.gax.rpc.ServerStream; import com.google.api.generator.engine.ast.AssignmentExpr; import com.google.api.generator.engine.ast.CommentStatement; @@ -36,6 +37,7 @@ import com.google.api.generator.gapic.model.Field; import com.google.api.generator.gapic.model.Message; import com.google.api.generator.gapic.model.Method; +import com.google.api.generator.gapic.model.Method.Stream; import com.google.api.generator.gapic.model.MethodArgument; import com.google.api.generator.gapic.model.ResourceName; import com.google.api.generator.gapic.utils.JavaStyle; @@ -552,8 +554,15 @@ public static String composeStreamCallableMethodHeaderSampleCode( .build(); // TODO (summerji) : Implement Stream.Client and Stream.Bidi sample code body statements. - List bodyStatements = - composeStreamServerSampleCodeBodyStatements(method, clientVarExpr, requestAssignmentExpr); + List bodyStatements = new ArrayList<>(); + if (method.stream().equals(Stream.SERVER)) { + bodyStatements.addAll( + composeStreamServerSampleCodeBodyStatements( + method, clientVarExpr, requestAssignmentExpr)); + } else if (method.stream().equals(Stream.BIDI)) { + bodyStatements.addAll( + composeStreamBidiSampleCodeBodyStatements(method, clientVarExpr, requestAssignmentExpr)); + } return SampleCodeWriter.write( TryCatchStatement.builder() @@ -761,6 +770,79 @@ private static List composeStreamServerSampleCodeBodyStatements( return bodyStatements; } + private static List composeStreamBidiSampleCodeBodyStatements( + Method method, VariableExpr clientVarExpr, AssignmentExpr requestAssignmentExpr) { + List bodyExprs = new ArrayList<>(); + + // Create bidi stream variable expression and assign it with invoking client's bidi stream + // method. + // e.g. BidiStream bidiStream = echoClient.chatCallable().call(); + TypeNode bidiStreamType = + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(BidiStream.class) + .setGenerics(method.inputType().reference(), method.outputType().reference()) + .build()); + VariableExpr bidiStreamVarExpr = + VariableExpr.withVariable( + Variable.builder().setName("bidiStream").setType(bidiStreamType).build()); + MethodInvocationExpr clientBidiStreamCallMethodInvoationExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(clientVarExpr) + .setMethodName(JavaStyle.toLowerCamelCase(String.format("%sCallable", method.name()))) + .build(); + clientBidiStreamCallMethodInvoationExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(clientBidiStreamCallMethodInvoationExpr) + .setMethodName("call") + .setReturnType(bidiStreamType) + .build(); + AssignmentExpr bidiStreamAssignmentExpr = + AssignmentExpr.builder() + .setVariableExpr(bidiStreamVarExpr.toBuilder().setIsDecl(true).build()) + .setValueExpr(clientBidiStreamCallMethodInvoationExpr) + .build(); + bodyExprs.add(bidiStreamAssignmentExpr); + + // Add request with default value expression. + bodyExprs.add(requestAssignmentExpr); + + // Invoke send method with argument request. + // e.g. bidiStream.send(request); + MethodInvocationExpr sendMethodInvocationExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(bidiStreamVarExpr) + .setArguments(requestAssignmentExpr.variableExpr().toBuilder().setIsDecl(false).build()) + .setMethodName("send") + .build(); + bodyExprs.add(sendMethodInvocationExpr); + + List bodyStatements = + bodyExprs.stream().map(e -> ExprStatement.withExpr(e)).collect(Collectors.toList()); + + // For-loop on bidi stream variable. + // e.g. for (EchoResponse response : bidiStream) { + // // Do something when reveive a response. + // } + ForStatement forStatement = + ForStatement.builder() + .setLocalVariableExpr( + VariableExpr.builder() + .setVariable( + Variable.builder().setType(method.outputType()).setName("response").build()) + .setIsDecl(true) + .build()) + .setCollectionExpr(bidiStreamVarExpr) + .setBody( + Arrays.asList( + CommentStatement.withComment( + LineComment.withComment("Do something when a response is received.")))) + .build(); + bodyStatements.add(forStatement); + + return bodyStatements; + } + // ==================================Helpers===================================================// // Create a list of RPC method arguments' variable expressions. diff --git a/src/test/java/com/google/api/generator/gapic/composer/goldens/EchoClient.golden b/src/test/java/com/google/api/generator/gapic/composer/goldens/EchoClient.golden index de9cf3ea35..94806914c8 100644 --- a/src/test/java/com/google/api/generator/gapic/composer/goldens/EchoClient.golden +++ b/src/test/java/com/google/api/generator/gapic/composer/goldens/EchoClient.golden @@ -353,13 +353,49 @@ public class EchoClient implements BackgroundResource { } // AUTO-GENERATED DOCUMENTATION AND METHOD. - /** Sample code: */ + /** + * Sample code: + * + *
{@code
+   * try (EchoClient echoClient = EchoClient.create()) {
+   *   BidiStream bidiStream = echoClient.chatCallable().call();
+   *   EchoRequest request =
+   *       EchoRequest.newBuilder()
+   *           .setName(FoobarName.ofProjectFoobarName("[PROJECT]", "[FOOBAR]").toString())
+   *           .setParent(FoobarName.ofProjectFoobarName("[PROJECT]", "[FOOBAR]").toString())
+   *           .setFoobar(Foobar.newBuilder().build())
+   *           .build();
+   *   bidiStream.send(request);
+   *   for (EchoResponse response : bidiStream) {
+   *     // Do something when a response is received.
+   *   }
+   * }
+   * }
+ */ public final BidiStreamingCallable chatCallable() { return stub.chatCallable(); } // AUTO-GENERATED DOCUMENTATION AND METHOD. - /** Sample code: */ + /** + * Sample code: + * + *
{@code
+   * try (EchoClient echoClient = EchoClient.create()) {
+   *   BidiStream bidiStream = echoClient.chatAgainCallable().call();
+   *   EchoRequest request =
+   *       EchoRequest.newBuilder()
+   *           .setName(FoobarName.ofProjectFoobarName("[PROJECT]", "[FOOBAR]").toString())
+   *           .setParent(FoobarName.ofProjectFoobarName("[PROJECT]", "[FOOBAR]").toString())
+   *           .setFoobar(Foobar.newBuilder().build())
+   *           .build();
+   *   bidiStream.send(request);
+   *   for (EchoResponse response : bidiStream) {
+   *     // Do something when a response is received.
+   *   }
+   * }
+   * }
+ */ public final BidiStreamingCallable chatAgainCallable() { return stub.chatAgainCallable(); } diff --git a/src/test/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientSampleCodeComposerTest.java b/src/test/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientSampleCodeComposerTest.java index 08d91bf03d..1b221d3f6e 100644 --- a/src/test/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientSampleCodeComposerTest.java +++ b/src/test/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientSampleCodeComposerTest.java @@ -1767,5 +1767,93 @@ public void invalidComposeStreamCallableMethodHeaderSampleCode_serverStreamNotEx ServiceClientSampleCodeComposer.composeStreamCallableMethodHeaderSampleCode( method, clientType, resourceNames, messageTypes)); } + + @Test + public void validComposeStreamCallableMethodHeaderSampleCode_bidiStream() { + FileDescriptor echoFileDescriptor = EchoOuterClass.getDescriptor(); + Map resourceNames = Parser.parseResourceNames(echoFileDescriptor); + Map messageTypes = Parser.parseMessages(echoFileDescriptor); + TypeNode clientType = + TypeNode.withReference( + VaporReference.builder() + .setName("EchoClient") + .setPakkage(SHOWCASE_PACKAGE_NAME) + .build()); + TypeNode inputType = + TypeNode.withReference( + VaporReference.builder() + .setName("EchoRequest") + .setPakkage(SHOWCASE_PACKAGE_NAME) + .build()); + TypeNode outputType = + TypeNode.withReference( + VaporReference.builder() + .setName("EchoResponse") + .setPakkage(SHOWCASE_PACKAGE_NAME) + .build()); + Method method = + Method.builder() + .setName("chat") + .setInputType(inputType) + .setOutputType(outputType) + .setStream(Stream.BIDI) + .build(); + String results = + ServiceClientSampleCodeComposer.composeStreamCallableMethodHeaderSampleCode( + method, clientType, resourceNames, messageTypes); + String expected = + LineFormatter.lines( + "try (EchoClient echoClient = EchoClient.create()) {\n", + " BidiStream bidiStream = echoClient.chatCallable().call();\n", + " EchoRequest request =\n", + " EchoRequest.newBuilder()\n", + " .setName(FoobarName.ofProjectFoobarName(\"[PROJECT]\", \"[FOOBAR]\").toString())\n", + " .setParent(FoobarName.ofProjectFoobarName(\"[PROJECT]\", \"[FOOBAR]\").toString())\n", + " .setFoobar(Foobar.newBuilder().build())\n", + " .build();\n", + " bidiStream.send(request);\n", + " for (EchoResponse response : bidiStream) {\n", + " // Do something when a response is received.\n", + " }\n", + "}"); + assertEquals(results, expected); + } + + @Test + public void invalidComposeStreamCallableMethodHeaderSampleCode_bidiStreamNotExistRequest() { + FileDescriptor echoFileDescriptor = EchoOuterClass.getDescriptor(); + Map resourceNames = Parser.parseResourceNames(echoFileDescriptor); + Map messageTypes = Parser.parseMessages(echoFileDescriptor); + TypeNode clientType = + TypeNode.withReference( + VaporReference.builder() + .setName("EchoClient") + .setPakkage(SHOWCASE_PACKAGE_NAME) + .build()); + TypeNode inputType = + TypeNode.withReference( + VaporReference.builder() + .setName("NotExistRequest") + .setPakkage(SHOWCASE_PACKAGE_NAME) + .build()); + TypeNode outputType = + TypeNode.withReference( + VaporReference.builder() + .setName("EchoResponse") + .setPakkage(SHOWCASE_PACKAGE_NAME) + .build()); + Method method = + Method.builder() + .setName("chat") + .setInputType(inputType) + .setOutputType(outputType) + .setStream(Stream.BIDI) + .build(); + assertThrows( + NullPointerException.class, + () -> + ServiceClientSampleCodeComposer.composeStreamCallableMethodHeaderSampleCode( + method, clientType, resourceNames, messageTypes)); + } }