Skip to content
This repository was archived by the owner on May 14, 2026. It is now read-only.

Commit 2c20b41

Browse files
committed
[samplecode][1/3]Implement Pure Unary RPC sample code (#573)
1 parent 0d3c377 commit 2c20b41

13 files changed

Lines changed: 1334 additions & 30 deletions

File tree

src/main/java/com/google/api/generator/gapic/composer/Composer.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ public static List<GapicClass> generateServiceClasses(
6262
@Nonnull Map<String, ResourceName> resourceNames,
6363
@Nonnull Map<String, Message> messageTypes) {
6464
List<GapicClass> clazzes = new ArrayList<>();
65-
clazzes.addAll(generateStubClasses(service, serviceConfig, messageTypes));
66-
clazzes.addAll(generateClientSettingsClasses(service, messageTypes));
65+
clazzes.addAll(generateStubClasses(service, serviceConfig, messageTypes, resourceNames));
66+
clazzes.addAll(generateClientSettingsClasses(service, messageTypes, resourceNames));
6767
clazzes.addAll(generateMocksAndTestClasses(service, resourceNames, messageTypes));
6868
// TODO(miraleung): Generate test classes.
6969
return clazzes;
@@ -78,7 +78,10 @@ public static List<GapicClass> generateResourceNameHelperClasses(
7878
}
7979

8080
public static List<GapicClass> generateStubClasses(
81-
Service service, GapicServiceConfig serviceConfig, Map<String, Message> messageTypes) {
81+
Service service,
82+
GapicServiceConfig serviceConfig,
83+
Map<String, Message> messageTypes,
84+
Map<String, ResourceName> resourceNames) {
8285
List<GapicClass> clazzes = new ArrayList<>();
8386
clazzes.add(ServiceStubClassComposer.instance().generate(service, messageTypes));
8487
clazzes.add(
@@ -89,9 +92,10 @@ public static List<GapicClass> generateStubClasses(
8992
}
9093

9194
public static List<GapicClass> generateClientSettingsClasses(
92-
Service service, Map<String, Message> messageTypes) {
95+
Service service, Map<String, Message> messageTypes, Map<String, ResourceName> resourceNames) {
9396
List<GapicClass> clazzes = new ArrayList<>();
94-
clazzes.add(ServiceClientClassComposer.instance().generate(service, messageTypes));
97+
clazzes.add(
98+
ServiceClientClassComposer.instance().generate(service, messageTypes, resourceNames));
9599
clazzes.add(ServiceSettingsClassComposer.instance().generate(service, messageTypes));
96100
return clazzes;
97101
}

src/main/java/com/google/api/generator/gapic/composer/ServiceClientClassComposer.java

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
import com.google.api.generator.gapic.model.Method;
6767
import com.google.api.generator.gapic.model.Method.Stream;
6868
import com.google.api.generator.gapic.model.MethodArgument;
69+
import com.google.api.generator.gapic.model.ResourceName;
6970
import com.google.api.generator.gapic.model.Service;
7071
import com.google.api.generator.gapic.utils.JavaStyle;
7172
import com.google.api.generator.util.TriFunction;
@@ -85,13 +86,14 @@
8586
import java.util.List;
8687
import java.util.Map;
8788
import java.util.Objects;
89+
import java.util.Optional;
8890
import java.util.concurrent.TimeUnit;
8991
import java.util.function.BiFunction;
9092
import java.util.function.Function;
9193
import java.util.stream.Collectors;
9294
import javax.annotation.Generated;
9395

94-
public class ServiceClientClassComposer implements ClassComposer {
96+
public class ServiceClientClassComposer {
9597
private static final ServiceClientClassComposer INSTANCE = new ServiceClientClassComposer();
9698
private static final String PAGED_RESPONSE_TYPE_NAME_PATTERN = "%sPagedResponse";
9799
private static final String CALLABLE_NAME_PATTERN = "%sCallable";
@@ -116,8 +118,8 @@ public static ServiceClientClassComposer instance() {
116118
return INSTANCE;
117119
}
118120

119-
@Override
120-
public GapicClass generate(Service service, Map<String, Message> messageTypes) {
121+
public GapicClass generate(
122+
Service service, Map<String, Message> messageTypes, Map<String, ResourceName> resourceNames) {
121123
TypeStore typeStore = createTypes(service, messageTypes);
122124
String className = ClassNames.getServiceClientClassName(service);
123125
GapicClass.Kind kind = Kind.MAIN;
@@ -133,7 +135,8 @@ public GapicClass generate(Service service, Map<String, Message> messageTypes) {
133135
.setName(className)
134136
.setImplementsTypes(createClassImplements(typeStore))
135137
.setStatements(createFieldDeclarations(service, typeStore, hasLroClient))
136-
.setMethods(createClassMethods(service, messageTypes, typeStore, hasLroClient))
138+
.setMethods(
139+
createClassMethods(service, messageTypes, typeStore, resourceNames, hasLroClient))
137140
.setNestedClasses(createNestedPagingClasses(service, messageTypes, typeStore))
138141
.build();
139142
return GapicClass.create(kind, classDef);
@@ -170,12 +173,13 @@ private static List<MethodDefinition> createClassMethods(
170173
Service service,
171174
Map<String, Message> messageTypes,
172175
TypeStore typeStore,
176+
Map<String, ResourceName> resourceNames,
173177
boolean hasLroClient) {
174178
List<MethodDefinition> methods = new ArrayList<>();
175179
methods.addAll(createStaticCreatorMethods(service, typeStore));
176180
methods.addAll(createConstructorMethods(service, typeStore, hasLroClient));
177181
methods.addAll(createGetterMethods(service, typeStore, hasLroClient));
178-
methods.addAll(createServiceMethods(service, messageTypes, typeStore));
182+
methods.addAll(createServiceMethods(service, messageTypes, typeStore, resourceNames));
179183
methods.addAll(createBackgroundResourceMethods(service, typeStore));
180184
return methods;
181185
}
@@ -225,7 +229,7 @@ private static List<MethodDefinition> createStaticCreatorMethods(
225229
TypeNode thisClassType = typeStore.get(thisClientName);
226230
TypeNode exceptionType = typeStore.get("IOException");
227231

228-
TypeNode settingsType = typeStore.get(settingsName);
232+
TypeNode settingsType = types.get(settingsName);
229233
Preconditions.checkNotNull(settingsType, String.format("Type %s not found", settingsName));
230234

231235
MethodInvocationExpr newBuilderExpr =
@@ -492,11 +496,16 @@ private static List<MethodDefinition> createGetterMethods(
492496
}
493497

494498
private static List<MethodDefinition> createServiceMethods(
495-
Service service, Map<String, Message> messageTypes, TypeStore typeStore) {
499+
Service service,
500+
Map<String, Message> messageTypes,
501+
TypeStore typeStore,
502+
Map<String, ResourceName> resourceNames) {
496503
List<MethodDefinition> javaMethods = new ArrayList<>();
497504
for (Method method : service.methods()) {
498505
if (method.stream().equals(Stream.NONE)) {
499-
javaMethods.addAll(createMethodVariants(method, messageTypes, typeStore));
506+
javaMethods.addAll(
507+
createMethodVariants(
508+
method, ClassNames.getServiceClientClassName(service), messageTypes, typeStore, resourceNames));
500509
javaMethods.add(createMethodDefaultMethod(method, typeStore));
501510
}
502511
if (method.hasLro()) {
@@ -511,7 +520,11 @@ private static List<MethodDefinition> createServiceMethods(
511520
}
512521

513522
private static List<MethodDefinition> createMethodVariants(
514-
Method method, Map<String, Message> messageTypes, TypeStore typeStore) {
523+
Method method,
524+
String clientName,
525+
Map<String, Message> messageTypes,
526+
TypeStore typeStore,
527+
Map<String, ResourceName> resourceNames) {
515528
List<MethodDefinition> javaMethods = new ArrayList<>();
516529
String methodName = JavaStyle.toLowerCamelCase(method.name());
517530
TypeNode methodInputType = method.inputType();
@@ -573,10 +586,20 @@ private static List<MethodDefinition> createMethodVariants(
573586
.setReturnType(methodOutputType)
574587
.build();
575588

589+
Optional<String> methodSampleCode = Optional.empty();
590+
if (!method.isPaged() && !method.hasLro()) {
591+
// TODO(summerji): Remove the condition check once finished the implementation on paged
592+
// sample code and lro sample code.
593+
methodSampleCode =
594+
Optional.of(
595+
ServiceClientSampleCodeComposer.composeRpcMethodHeaderSampleCode(
596+
method, typeStore.get(clientName), signature, resourceNames));
597+
}
576598
MethodDefinition.Builder methodVariantBuilder =
577599
MethodDefinition.builder()
578600
.setHeaderCommentStatements(
579-
ServiceClientCommentComposer.createRpcMethodHeaderComment(method, signature))
601+
ServiceClientCommentComposer.createRpcMethodHeaderComment(
602+
method, signature, methodSampleCode))
580603
.setScope(ScopeNode.PUBLIC)
581604
.setIsFinal(true)
582605
.setName(String.format(method.hasLro() ? "%sAsync" : "%s", methodName))

src/main/java/com/google/api/generator/gapic/composer/comment/ServiceClientCommentComposer.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.Arrays;
2828
import java.util.Collections;
2929
import java.util.List;
30+
import java.util.Optional;
3031
import java.util.stream.Collectors;
3132
import java.util.stream.Stream;
3233

@@ -158,17 +159,19 @@ public static CommentStatement createCreateMethodStubArgComment(
158159
CREATE_METHOD_STUB_ARG_PATTERN, serviceName, settingsType.reference().name()));
159160
}
160161

161-
public static List<CommentStatement> createRpcMethodHeaderComment(
162-
Method method, List<MethodArgument> methodArguments) {
162+
public static List<CommentStatement> createRpcMethodHeaderComment(
163+
Method method, List<MethodArgument> methodArguments, Optional<String> sampleCode) {
163164
JavaDocComment.Builder methodJavadocBuilder = JavaDocComment.builder();
164165

165166
if (method.hasDescription()) {
166167
methodJavadocBuilder =
167168
processProtobufComment(method.description(), methodJavadocBuilder, null);
168169
}
169170

170-
// methodJavadocBuilder.addParagraph(METHOD_DESCRIPTION_SAMPLE_CODE_SUMMARY_STRING);
171-
// TODO(summerji): Add sample code here and uncomment the above.
171+
if (sampleCode.isPresent()) {
172+
methodJavadocBuilder.addParagraph(METHOD_DESCRIPTION_SAMPLE_CODE_SUMMARY_STRING);
173+
methodJavadocBuilder.addSampleCode(sampleCode.get());
174+
}
172175

173176
if (methodArguments.isEmpty()) {
174177
methodJavadocBuilder.addParam(
@@ -192,8 +195,9 @@ public static List<CommentStatement> createRpcMethodHeaderComment(
192195
return comments;
193196
}
194197

195-
public static List<CommentStatement> createRpcMethodHeaderComment(Method method) {
196-
return createRpcMethodHeaderComment(method, Collections.emptyList());
198+
public static List<CommentStatement> createRpcMethodHeaderComment(Method method) {
199+
// TODO(summerji): Refactor this method when implement default method sample code.
200+
return createRpcMethodHeaderComment(method, Collections.emptyList(), Optional.empty());
197201
}
198202

199203
public static CommentStatement createMethodNoArgComment(String serviceName) {

src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientSampleCodeComposer.java

Lines changed: 151 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,29 @@
2020
import com.google.api.generator.engine.ast.Expr;
2121
import com.google.api.generator.engine.ast.ExprStatement;
2222
import com.google.api.generator.engine.ast.MethodInvocationExpr;
23+
import com.google.api.generator.engine.ast.TryCatchStatement;
2324
import com.google.api.generator.engine.ast.TypeNode;
2425
import com.google.api.generator.engine.ast.VaporReference;
2526
import com.google.api.generator.engine.ast.Variable;
2627
import com.google.api.generator.engine.ast.VariableExpr;
28+
import com.google.api.generator.gapic.composer.samplecode.SampleCodeWriter;
29+
import com.google.api.generator.gapic.model.Method;
30+
import com.google.api.generator.gapic.model.MethodArgument;
31+
import com.google.api.generator.gapic.model.ResourceName;
2732
import com.google.api.generator.gapic.utils.JavaStyle;
33+
import com.google.common.annotations.VisibleForTesting;
34+
import com.google.common.base.Preconditions;
35+
import java.util.ArrayList;
2836
import java.util.Arrays;
37+
import java.util.List;
38+
import java.util.Map;
39+
import java.util.stream.Collectors;
40+
import java.util.stream.IntStream;
2941

3042
public class ServiceClientSampleCodeComposer {
31-
// TODO(summerji): Add unit tests for ServiceClientSampleCodeComposer.
3243

3344
public static String composeClassHeaderCredentialsSampleCode(
45+
// TODO(summerji): Add unit tests for composeClassHeaderCredentialsSampleCode.
3446
TypeNode clientType, TypeNode settingsType) {
3547
// Initialize clientSettings with builder() method.
3648
// e.g. EchoSettings echoSettings =
@@ -103,6 +115,7 @@ public static String composeClassHeaderCredentialsSampleCode(
103115
}
104116

105117
public static String composeClassHeaderEndpointSampleCode(
118+
// TODO(summerji): Add unit tests for composeClassHeaderEndpointSampleCode.
106119
TypeNode clientType, TypeNode settingsType) {
107120
// Initialize client settings with builder() method.
108121
// e.g. EchoSettings echoSettings = EchoSettings.newBuilder().setEndpoint("myEndpoint").build();
@@ -166,4 +179,141 @@ public static String composeClassHeaderEndpointSampleCode(
166179
ExprStatement.withExpr(initSettingsVarExpr),
167180
ExprStatement.withExpr(initClientVarExpr)));
168181
}
182+
183+
public static String composeRpcMethodHeaderSampleCode(
184+
Method method,
185+
TypeNode clientType,
186+
List<MethodArgument> arguments,
187+
Map<String, ResourceName> resourceNames) {
188+
// TODO(summerji): Add other types RPC methods' sample code.
189+
return SampleCodeWriter.write(
190+
composeUnaryRpcMethodSampleCode(method, clientType, arguments, resourceNames));
191+
}
192+
193+
@VisibleForTesting
194+
static TryCatchStatement composeUnaryRpcMethodSampleCode(
195+
Method method,
196+
TypeNode clientType,
197+
List<MethodArgument> arguments,
198+
Map<String, ResourceName> resourceNames) {
199+
VariableExpr clientVarExpr =
200+
VariableExpr.withVariable(
201+
Variable.builder()
202+
.setName(JavaStyle.toLowerCamelCase(clientType.reference().name()))
203+
.setType(clientType)
204+
.build());
205+
// List of rpc method arguments' variable expressions.
206+
List<VariableExpr> rpcMethodArgVarExprs =
207+
arguments.stream()
208+
.map(
209+
arg ->
210+
VariableExpr.withVariable(
211+
Variable.builder()
212+
.setName(JavaStyle.toLowerCamelCase(arg.name()))
213+
.setType(arg.type())
214+
.build()))
215+
.collect(Collectors.toList());
216+
// List of rpc method arguments' default value expression.
217+
List<ResourceName> resourceNameList =
218+
resourceNames.values().stream().collect(Collectors.toList());
219+
List<Expr> rpcMethodArgDefaultValueExprs =
220+
arguments.stream()
221+
.map(
222+
arg ->
223+
!isStringTypedResourceName(arg, resourceNames)
224+
? DefaultValueComposer.createDefaultValue(arg, resourceNames)
225+
: MethodInvocationExpr.builder()
226+
.setExprReferenceExpr(
227+
DefaultValueComposer.createDefaultValue(
228+
resourceNames.get(
229+
arg.field().resourceReference().resourceTypeString()),
230+
resourceNameList,
231+
arg.field().name()))
232+
.setMethodName("toString")
233+
.setReturnType(TypeNode.STRING)
234+
.build())
235+
.collect(Collectors.toList());
236+
237+
List<Expr> bodyExprs = new ArrayList<>();
238+
Preconditions.checkState(
239+
rpcMethodArgVarExprs.size() == rpcMethodArgDefaultValueExprs.size(),
240+
"Expected the number of method arguments to match the number of default values.");
241+
bodyExprs.addAll(
242+
IntStream.range(0, rpcMethodArgVarExprs.size())
243+
.mapToObj(
244+
i ->
245+
AssignmentExpr.builder()
246+
.setVariableExpr(
247+
(rpcMethodArgVarExprs.get(i)).toBuilder().setIsDecl(true).build())
248+
.setValueExpr(rpcMethodArgDefaultValueExprs.get(i))
249+
.build())
250+
.collect(Collectors.toList()));
251+
// Invoke current method based on return type.
252+
// e.g. if return void, echoClient.echo(..); or,
253+
// e.g. if return other type, EchoResponse response = echoClient.echo(...);
254+
boolean returnsVoid = isProtoEmptyType(method.outputType());
255+
if (returnsVoid) {
256+
bodyExprs.add(
257+
MethodInvocationExpr.builder()
258+
.setExprReferenceExpr(clientVarExpr)
259+
.setMethodName(JavaStyle.toLowerCamelCase(method.name()))
260+
.setArguments(
261+
rpcMethodArgVarExprs.stream().map(e -> (Expr) e).collect(Collectors.toList()))
262+
.setReturnType(clientType)
263+
.build());
264+
} else {
265+
VariableExpr responseVarExpr =
266+
VariableExpr.withVariable(
267+
Variable.builder().setName("response").setType(method.outputType()).build());
268+
MethodInvocationExpr clientMethodInvocationExpr =
269+
MethodInvocationExpr.builder()
270+
.setExprReferenceExpr(clientVarExpr)
271+
.setMethodName(JavaStyle.toLowerCamelCase(method.name()))
272+
.setArguments(
273+
rpcMethodArgVarExprs.stream().map(e -> (Expr) e).collect(Collectors.toList()))
274+
.setReturnType(responseVarExpr.variable().type())
275+
.build();
276+
bodyExprs.add(
277+
AssignmentExpr.builder()
278+
.setVariableExpr(responseVarExpr.toBuilder().setIsDecl(true).build())
279+
.setValueExpr(clientMethodInvocationExpr)
280+
.build());
281+
}
282+
283+
return TryCatchStatement.builder()
284+
.setTryResourceExpr(assignClientVariableWithCreateMethodExpr(clientVarExpr))
285+
.setTryBody(
286+
bodyExprs.stream().map(e -> ExprStatement.withExpr(e)).collect(Collectors.toList()))
287+
.setIsSampleCode(true)
288+
.build();
289+
}
290+
291+
// ==================================Helpers===================================================//
292+
293+
// Assign client variable expr with create client.
294+
// e.g EchoClient echoClient = EchoClient.create()
295+
private static AssignmentExpr assignClientVariableWithCreateMethodExpr(
296+
VariableExpr clientVarExpr) {
297+
return AssignmentExpr.builder()
298+
.setVariableExpr(clientVarExpr.toBuilder().setIsDecl(true).build())
299+
.setValueExpr(
300+
MethodInvocationExpr.builder()
301+
.setStaticReferenceType(clientVarExpr.variable().type())
302+
.setReturnType(clientVarExpr.variable().type())
303+
.setMethodName("create")
304+
.build())
305+
.build();
306+
}
307+
308+
private static boolean isStringTypedResourceName(
309+
MethodArgument arg, Map<String, ResourceName> resourceNames) {
310+
return arg.type().equals(TypeNode.STRING)
311+
&& arg.field().hasResourceReference()
312+
&& resourceNames.containsKey(arg.field().resourceReference().resourceTypeString());
313+
}
314+
315+
private static boolean isProtoEmptyType(TypeNode type) {
316+
return type.reference().pakkage().equals("com.google.protobuf")
317+
&& type.reference().name().equals("Empty");
318+
}
169319
}

src/test/java/com/google/api/generator/gapic/composer/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ UPDATE_GOLDENS_TESTS = [
1919

2020
TESTS = UPDATE_GOLDENS_TESTS + [
2121
"RetrySettingsComposerTest",
22+
"ServiceClientSampleCodeComposerTest",
2223
]
2324

2425
TEST_DEPS = [
@@ -37,6 +38,7 @@ TEST_DEPS = [
3738
"//src/test/java/com/google/api/generator/gapic/testdata:showcase_java_proto",
3839
"//src/test/java/com/google/api/generator/gapic/testdata:testgapic_java_proto",
3940
"//src/test/java/com/google/api/generator/gapic/composer/constants",
41+
"@com_google_api_api_common//jar",
4042
"@com_google_api_gax_java//gax",
4143
"@com_google_googleapis//google/logging/v2:logging_java_proto",
4244
"@com_google_googleapis//google/pubsub/v1:pubsub_java_proto",

0 commit comments

Comments
 (0)