Skip to content

Commit 4a75bfd

Browse files
committed
feat: selective generation based on service config include list.
1 parent cdcc0a2 commit 4a75bfd

File tree

12 files changed

+652
-58
lines changed

12 files changed

+652
-58
lines changed

gapic-generator-java-pom-parent/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
<j2objc-annotations.version>3.0.0</j2objc-annotations.version>
3939
<threetenbp.version>1.7.0</threetenbp.version>
4040
<junit.version>5.11.2</junit.version>
41+
<mockito.version>4.11.0</mockito.version>
4142
</properties>
4243

4344
<developers>

gapic-generator-java/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,5 +488,11 @@
488488
<version>${junit.version}</version>
489489
<scope>test</scope>
490490
</dependency>
491+
<dependency>
492+
<groupId>org.mockito</groupId>
493+
<artifactId>mockito-core</artifactId>
494+
<version>${mockito.version}</version>
495+
<scope>test</scope>
496+
</dependency>
491497
</dependencies>
492498
</project>

gapic-generator-java/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java

Lines changed: 65 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,8 @@
1414

1515
package com.google.api.generator.gapic.protoparser;
1616

17-
import com.google.api.ClientProto;
18-
import com.google.api.DocumentationRule;
19-
import com.google.api.FieldBehavior;
20-
import com.google.api.FieldBehaviorProto;
17+
import com.google.api.*;
2118
import com.google.api.FieldInfo.Format;
22-
import com.google.api.FieldInfoProto;
23-
import com.google.api.HttpRule;
24-
import com.google.api.MethodSettings;
25-
import com.google.api.ResourceDescriptor;
26-
import com.google.api.ResourceProto;
2719
import com.google.api.generator.engine.ast.TypeNode;
2820
import com.google.api.generator.engine.ast.VaporReference;
2921
import com.google.api.generator.gapic.model.Field;
@@ -154,6 +146,14 @@ public static GapicContext parse(CodeGeneratorRequest request) {
154146
// Keep message and resource name parsing separate for cleaner logic.
155147
// While this takes an extra pass through the protobufs, the extra time is relatively trivial
156148
// and is worth the larger reduced maintenance cost.
149+
150+
// Note that if selective api generation is configured via service yaml, resource names
151+
// and messages corresponding to RPC's methods excluded for generation are not removed.
152+
// However, these resource names will not be composed.
153+
// refer to ComposerTest.testComposeSelectively_shouldComposeOnlyOneHelperResource for
154+
// verification.
155+
// TODO: remove messages and resource names that's only used by excluded RPCs configured
156+
// via selective api generation from parsed GapicContext.
157157
Map<String, Message> messages = parseMessages(request, outputResourceReferencesSeen);
158158

159159
Map<String, ResourceName> resourceNames = parseResourceNames(request);
@@ -425,6 +425,45 @@ public static List<Service> parseService(
425425
Transport.GRPC);
426426
}
427427

428+
static boolean shouldIncludeMethodInGeneration(
429+
MethodDescriptor method,
430+
Optional<com.google.api.Service> serviceYamlProtoOpt,
431+
String protoPackage) {
432+
// default to include all when no service yaml or no library setting section.
433+
if (!serviceYamlProtoOpt.isPresent()
434+
|| serviceYamlProtoOpt.get().getPublishing().getLibrarySettingsCount() == 0) {
435+
return true;
436+
}
437+
List<ClientLibrarySettings> librarySettingsList =
438+
serviceYamlProtoOpt.get().getPublishing().getLibrarySettingsList();
439+
// Validate for logging purpose, this should be validated upstream.
440+
// If library_settings.version does not match with proto package name
441+
// Give warnings and disregard this config. default to include all.
442+
if (!librarySettingsList.get(0).getVersion().isEmpty()
443+
&& !protoPackage.equals(librarySettingsList.get(0).getVersion())) {
444+
LOGGER.warning(
445+
String.format(
446+
"Service yaml config is misconfigured. Version in "
447+
+ "publishing.library_settings (%s) does not match proto package (%s)."
448+
+ "Disregarding selective generation settings.",
449+
librarySettingsList.get(0).getVersion(), protoPackage));
450+
return true;
451+
}
452+
List<String> includeMethodsList =
453+
librarySettingsList
454+
.get(0)
455+
.getJavaSettings()
456+
.getCommon()
457+
.getSelectiveGapicGeneration()
458+
.getMethodsList();
459+
// default to include all when nothing specified
460+
if (includeMethodsList.isEmpty()) {
461+
return true;
462+
}
463+
464+
return includeMethodsList.contains(method.getFullName());
465+
}
466+
428467
public static List<Service> parseService(
429468
FileDescriptor fileDescriptor,
430469
Map<String, Message> messageTypes,
@@ -433,18 +472,25 @@ public static List<Service> parseService(
433472
Optional<GapicServiceConfig> serviceConfigOpt,
434473
Set<ResourceName> outputArgResourceNames,
435474
Transport transport) {
436-
475+
String protoPackage = fileDescriptor.getPackage();
437476
return fileDescriptor.getServices().stream()
438477
.filter(
439478
serviceDescriptor -> {
440479
List<MethodDescriptor> methodsList = serviceDescriptor.getMethods();
441-
if (methodsList.isEmpty()) {
480+
List<MethodDescriptor> methodListSelected =
481+
methodsList.stream()
482+
.filter(
483+
method ->
484+
shouldIncludeMethodInGeneration(
485+
method, serviceYamlProtoOpt, protoPackage))
486+
.collect(Collectors.toList());
487+
if (methodListSelected.isEmpty()) {
442488
LOGGER.warning(
443489
String.format(
444490
"Service %s has no RPC methods and will not be generated",
445491
serviceDescriptor.getName()));
446492
}
447-
return !methodsList.isEmpty();
493+
return !methodListSelected.isEmpty();
448494
})
449495
.map(
450496
s -> {
@@ -498,6 +544,8 @@ public static List<Service> parseService(
498544
String pakkage = TypeParser.getPackage(fileDescriptor);
499545
String originalJavaPackage = pakkage;
500546
// Override Java package with that specified in gapic.yaml.
547+
// this override is deprecated and legacy support only
548+
// see go/client-user-guide#configure-long-running-operation-polling-timeouts-optional
501549
if (serviceConfigOpt.isPresent()
502550
&& serviceConfigOpt.get().getLanguageSettingsOpt().isPresent()) {
503551
GapicLanguageSettings languageSettings =
@@ -518,6 +566,7 @@ public static List<Service> parseService(
518566
.setMethods(
519567
parseMethods(
520568
s,
569+
protoPackage,
521570
pakkage,
522571
messageTypes,
523572
resourceNames,
@@ -709,6 +758,7 @@ public static Map<String, ResourceName> parseResourceNames(
709758
@VisibleForTesting
710759
static List<Method> parseMethods(
711760
ServiceDescriptor serviceDescriptor,
761+
String protoPackage,
712762
String servicePackage,
713763
Map<String, Message> messageTypes,
714764
Map<String, ResourceName> resourceNames,
@@ -721,8 +771,10 @@ static List<Method> parseMethods(
721771
// Parse the serviceYaml for autopopulated methods and fields once and put into a map
722772
Map<String, List<String>> autoPopulatedMethodsWithFields =
723773
parseAutoPopulatedMethodsAndFields(serviceYamlProtoOpt);
724-
725774
for (MethodDescriptor protoMethod : serviceDescriptor.getMethods()) {
775+
if (!shouldIncludeMethodInGeneration(protoMethod, serviceYamlProtoOpt, protoPackage)) {
776+
continue;
777+
}
726778
// Parse the method.
727779
TypeNode inputType = TypeParser.parseType(protoMethod.getInputType());
728780
Method.Builder methodBuilder = Method.builder();

gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/GrpcServiceStubClassComposerTest.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,12 @@ void generateGrpcServiceStubClass_autopopulateField() {
9494
}
9595

9696
@Test
97-
void generateGrpcServiceStubClass_callableNameType() {
98-
GapicContext context = GrpcTestProtoLoader.instance().parseCallabeNameType();
97+
void generateGrpcServiceStubClass_selectiveGeneration() {
98+
GapicContext context = GrpcTestProtoLoader.instance().parseSelectiveGenerationTesting();
9999
Service service = context.services().get(0);
100100
GapicClass clazz = GrpcServiceStubClassComposer.instance().generate(context, service);
101-
Assert.assertGoldenClass(this.getClass(), clazz, "GrpcCallableNameTypeStub.golden");
101+
102+
Assert.assertGoldenClass(this.getClass(), clazz, "SelectiveGeneratedStub.golden");
102103
Assert.assertEmptySamples(clazz.samples());
103104
}
104105
}
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
package com.google.selective.generate.v1beta1.stub;
2+
3+
import com.google.api.core.BetaApi;
4+
import com.google.api.gax.core.BackgroundResource;
5+
import com.google.api.gax.core.BackgroundResourceAggregation;
6+
import com.google.api.gax.grpc.GrpcCallSettings;
7+
import com.google.api.gax.grpc.GrpcStubCallableFactory;
8+
import com.google.api.gax.rpc.BidiStreamingCallable;
9+
import com.google.api.gax.rpc.ClientContext;
10+
import com.google.api.gax.rpc.UnaryCallable;
11+
import com.google.longrunning.stub.GrpcOperationsStub;
12+
import com.google.selective.generate.v1beta1.EchoRequest;
13+
import com.google.selective.generate.v1beta1.EchoResponse;
14+
import io.grpc.MethodDescriptor;
15+
import io.grpc.protobuf.ProtoUtils;
16+
import java.io.IOException;
17+
import java.util.concurrent.TimeUnit;
18+
import javax.annotation.Generated;
19+
20+
// AUTO-GENERATED DOCUMENTATION AND CLASS.
21+
/**
22+
* gRPC stub implementation for the EchoServiceShouldGeneratePartial service API.
23+
*
24+
* <p>This class is for advanced usage and reflects the underlying API directly.
25+
*/
26+
@BetaApi
27+
@Generated("by gapic-generator-java")
28+
public class GrpcEchoServiceShouldGeneratePartialStub extends EchoServiceShouldGeneratePartialStub {
29+
private static final MethodDescriptor<EchoRequest, EchoResponse>
30+
echoShouldIncludeMethodDescriptor =
31+
MethodDescriptor.<EchoRequest, EchoResponse>newBuilder()
32+
.setType(MethodDescriptor.MethodType.UNARY)
33+
.setFullMethodName(
34+
"google.selective.generate.v1beta1.EchoServiceShouldGeneratePartial/EchoShouldInclude")
35+
.setRequestMarshaller(ProtoUtils.marshaller(EchoRequest.getDefaultInstance()))
36+
.setResponseMarshaller(ProtoUtils.marshaller(EchoResponse.getDefaultInstance()))
37+
.build();
38+
39+
private static final MethodDescriptor<EchoRequest, EchoResponse>
40+
chatShouldIncludeMethodDescriptor =
41+
MethodDescriptor.<EchoRequest, EchoResponse>newBuilder()
42+
.setType(MethodDescriptor.MethodType.BIDI_STREAMING)
43+
.setFullMethodName(
44+
"google.selective.generate.v1beta1.EchoServiceShouldGeneratePartial/ChatShouldInclude")
45+
.setRequestMarshaller(ProtoUtils.marshaller(EchoRequest.getDefaultInstance()))
46+
.setResponseMarshaller(ProtoUtils.marshaller(EchoResponse.getDefaultInstance()))
47+
.build();
48+
49+
private static final MethodDescriptor<EchoRequest, EchoResponse>
50+
chatAgainShouldIncludeMethodDescriptor =
51+
MethodDescriptor.<EchoRequest, EchoResponse>newBuilder()
52+
.setType(MethodDescriptor.MethodType.BIDI_STREAMING)
53+
.setFullMethodName(
54+
"google.selective.generate.v1beta1.EchoServiceShouldGeneratePartial/ChatAgainShouldInclude")
55+
.setRequestMarshaller(ProtoUtils.marshaller(EchoRequest.getDefaultInstance()))
56+
.setResponseMarshaller(ProtoUtils.marshaller(EchoResponse.getDefaultInstance()))
57+
.build();
58+
59+
private final UnaryCallable<EchoRequest, EchoResponse> echoShouldIncludeCallable;
60+
private final BidiStreamingCallable<EchoRequest, EchoResponse> chatShouldIncludeCallable;
61+
private final BidiStreamingCallable<EchoRequest, EchoResponse> chatAgainShouldIncludeCallable;
62+
63+
private final BackgroundResource backgroundResources;
64+
private final GrpcOperationsStub operationsStub;
65+
private final GrpcStubCallableFactory callableFactory;
66+
67+
public static final GrpcEchoServiceShouldGeneratePartialStub create(
68+
EchoServiceShouldGeneratePartialStubSettings settings) throws IOException {
69+
return new GrpcEchoServiceShouldGeneratePartialStub(settings, ClientContext.create(settings));
70+
}
71+
72+
public static final GrpcEchoServiceShouldGeneratePartialStub create(ClientContext clientContext)
73+
throws IOException {
74+
return new GrpcEchoServiceShouldGeneratePartialStub(
75+
EchoServiceShouldGeneratePartialStubSettings.newBuilder().build(), clientContext);
76+
}
77+
78+
public static final GrpcEchoServiceShouldGeneratePartialStub create(
79+
ClientContext clientContext, GrpcStubCallableFactory callableFactory) throws IOException {
80+
return new GrpcEchoServiceShouldGeneratePartialStub(
81+
EchoServiceShouldGeneratePartialStubSettings.newBuilder().build(),
82+
clientContext,
83+
callableFactory);
84+
}
85+
86+
/**
87+
* Constructs an instance of GrpcEchoServiceShouldGeneratePartialStub, using the given settings.
88+
* This is protected so that it is easy to make a subclass, but otherwise, the static factory
89+
* methods should be preferred.
90+
*/
91+
protected GrpcEchoServiceShouldGeneratePartialStub(
92+
EchoServiceShouldGeneratePartialStubSettings settings, ClientContext clientContext)
93+
throws IOException {
94+
this(settings, clientContext, new GrpcEchoServiceShouldGeneratePartialCallableFactory());
95+
}
96+
97+
/**
98+
* Constructs an instance of GrpcEchoServiceShouldGeneratePartialStub, using the given settings.
99+
* This is protected so that it is easy to make a subclass, but otherwise, the static factory
100+
* methods should be preferred.
101+
*/
102+
protected GrpcEchoServiceShouldGeneratePartialStub(
103+
EchoServiceShouldGeneratePartialStubSettings settings,
104+
ClientContext clientContext,
105+
GrpcStubCallableFactory callableFactory)
106+
throws IOException {
107+
this.callableFactory = callableFactory;
108+
this.operationsStub = GrpcOperationsStub.create(clientContext, callableFactory);
109+
110+
GrpcCallSettings<EchoRequest, EchoResponse> echoShouldIncludeTransportSettings =
111+
GrpcCallSettings.<EchoRequest, EchoResponse>newBuilder()
112+
.setMethodDescriptor(echoShouldIncludeMethodDescriptor)
113+
.build();
114+
GrpcCallSettings<EchoRequest, EchoResponse> chatShouldIncludeTransportSettings =
115+
GrpcCallSettings.<EchoRequest, EchoResponse>newBuilder()
116+
.setMethodDescriptor(chatShouldIncludeMethodDescriptor)
117+
.build();
118+
GrpcCallSettings<EchoRequest, EchoResponse> chatAgainShouldIncludeTransportSettings =
119+
GrpcCallSettings.<EchoRequest, EchoResponse>newBuilder()
120+
.setMethodDescriptor(chatAgainShouldIncludeMethodDescriptor)
121+
.build();
122+
123+
this.echoShouldIncludeCallable =
124+
callableFactory.createUnaryCallable(
125+
echoShouldIncludeTransportSettings,
126+
settings.echoShouldIncludeSettings(),
127+
clientContext);
128+
this.chatShouldIncludeCallable =
129+
callableFactory.createBidiStreamingCallable(
130+
chatShouldIncludeTransportSettings,
131+
settings.chatShouldIncludeSettings(),
132+
clientContext);
133+
this.chatAgainShouldIncludeCallable =
134+
callableFactory.createBidiStreamingCallable(
135+
chatAgainShouldIncludeTransportSettings,
136+
settings.chatAgainShouldIncludeSettings(),
137+
clientContext);
138+
139+
this.backgroundResources =
140+
new BackgroundResourceAggregation(clientContext.getBackgroundResources());
141+
}
142+
143+
public GrpcOperationsStub getOperationsStub() {
144+
return operationsStub;
145+
}
146+
147+
@Override
148+
public UnaryCallable<EchoRequest, EchoResponse> echoShouldIncludeCallable() {
149+
return echoShouldIncludeCallable;
150+
}
151+
152+
@Override
153+
public BidiStreamingCallable<EchoRequest, EchoResponse> chatShouldIncludeCallable() {
154+
return chatShouldIncludeCallable;
155+
}
156+
157+
@Override
158+
public BidiStreamingCallable<EchoRequest, EchoResponse> chatAgainShouldIncludeCallable() {
159+
return chatAgainShouldIncludeCallable;
160+
}
161+
162+
@Override
163+
public final void close() {
164+
try {
165+
backgroundResources.close();
166+
} catch (RuntimeException e) {
167+
throw e;
168+
} catch (Exception e) {
169+
throw new IllegalStateException("Failed to close resource", e);
170+
}
171+
}
172+
173+
@Override
174+
public void shutdown() {
175+
backgroundResources.shutdown();
176+
}
177+
178+
@Override
179+
public boolean isShutdown() {
180+
return backgroundResources.isShutdown();
181+
}
182+
183+
@Override
184+
public boolean isTerminated() {
185+
return backgroundResources.isTerminated();
186+
}
187+
188+
@Override
189+
public void shutdownNow() {
190+
backgroundResources.shutdownNow();
191+
}
192+
193+
@Override
194+
public boolean awaitTermination(long duration, TimeUnit unit) throws InterruptedException {
195+
return backgroundResources.awaitTermination(duration, unit);
196+
}
197+
}

0 commit comments

Comments
 (0)