Skip to content

Commit dbc1403

Browse files
committed
feat(generator): add model flag and allowlist parser for resumable upload RPCs
1 parent add56e8 commit dbc1403

5 files changed

Lines changed: 132 additions & 3 deletions

File tree

sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/model/Method.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ public enum Stream {
4444

4545
public abstract boolean isBatching();
4646

47+
public abstract boolean isResumableUpload();
48+
4749
public boolean isPaged() {
4850
return pageSizeFieldName() != null;
4951
}
@@ -137,7 +139,8 @@ public static Builder builder() {
137139
.setIsInternalApi(false)
138140
.setIsBatching(false)
139141
.setIsDeprecated(false)
140-
.setOperationPollingMethod(false);
142+
.setOperationPollingMethod(false)
143+
.setIsResumableUpload(false);
141144
}
142145

143146
public static Stream toStream(boolean isClientStreaming, boolean isServerStreaming) {
@@ -177,6 +180,8 @@ public abstract static class Builder {
177180

178181
public abstract Builder setIsBatching(boolean isBatching);
179182

183+
public abstract Builder setIsResumableUpload(boolean isResumableUpload);
184+
180185
public abstract Builder setPageSizeFieldName(String pagedFieldName);
181186

182187
public abstract Builder setIsDeprecated(boolean isDeprecated);

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
import java.util.function.Function;
8989
import java.util.logging.Level;
9090
import java.util.logging.Logger;
91+
import java.util.regex.Pattern;
9192
import java.util.stream.Collectors;
9293
import java.util.stream.IntStream;
9394
import org.jspecify.annotations.NullMarked;
@@ -98,9 +99,10 @@ public class Parser {
9899
enum SelectiveGapicType {
99100
// Methods will be generated and exposed externally as usual.
100101
PUBLIC,
101-
// Methods will not be generated.
102+
// Method generation will be skipped.
102103
HIDDEN,
103-
// Methods will be generated and tagged @InternalApi (internal use) during generation.
104+
// Methods will be marked as BetaApi with InternalApi, this will prevent method from being
105+
// exposed externally.
104106
INTERNAL
105107
}
106108

@@ -134,6 +136,9 @@ enum SelectiveGapicType {
134136
"google.cloud.bigquery.v2.ModelService.ListModels",
135137
"google.cloud.bigquery.v2.TableService.ListTables");
136138

139+
private static final ImmutableList<Pattern> RESUMABLE_UPLOAD_ALLOWLIST_PATTERNS =
140+
ImmutableList.of();
141+
137142
// Allow other parsers to access this.
138143
protected static final SourceCodeInfoParser SOURCE_CODE_INFO_PARSER = new SourceCodeInfoParser();
139144

@@ -872,6 +877,9 @@ static List<Method> parseMethods(
872877
.getOptions()
873878
.getExtension(ExtendedOperationsProto.operationPollingMethod)
874879
: false;
880+
boolean isResumableUpload =
881+
RESUMABLE_UPLOAD_ALLOWLIST_PATTERNS.stream()
882+
.anyMatch(pattern -> pattern.matcher(protoMethod.getFullName()).matches());
875883
RoutingHeaderRule routingHeaderRule =
876884
RoutingRuleParser.parse(protoMethod, inputMessage, messageTypes);
877885
methods.add(
@@ -895,6 +903,7 @@ static List<Method> parseMethods(
895903
.setAutoPopulatedFields(autoPopulatedFields)
896904
.setRoutingHeaderRule(routingHeaderRule)
897905
.setIsBatching(isBatching)
906+
.setIsResumableUpload(isResumableUpload)
898907
.setPageSizeFieldName(parsePageSizeFieldName(protoMethod, messageTypes, transport))
899908
.setIsDeprecated(isDeprecated)
900909
.setOperationPollingMethod(operationPollingMethod)

sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import com.google.protobuf.compiler.PluginProtos.CodeGeneratorRequest;
5656
import com.google.selective.generate.v1beta1.SelectiveApiGenerationOuterClass;
5757
import com.google.showcase.v1beta1.EchoOuterClass;
58+
import com.google.showcase.v1beta1.ResumableUpload;
5859
import com.google.showcase.v1beta1.TestingOuterClass;
5960
import com.google.testgapic.v1beta1.LockerProto;
6061
import java.nio.file.Path;
@@ -163,6 +164,7 @@ void parseMethods_basic() {
163164
assertEquals(echoMethod.name(), "Echo");
164165
assertEquals(echoMethod.stream(), Method.Stream.NONE);
165166
assertEquals(false, echoMethod.hasAutoPopulatedFields());
167+
assertFalse(echoMethod.isResumableUpload());
166168

167169
// Detailed method signature parsing tests are in a separate unit test.
168170
List<List<MethodArgument>> methodSignatures = echoMethod.methodSignatures();
@@ -203,6 +205,32 @@ void parseMethods_basic() {
203205
assertEquals(false, chatMethod.hasAutoPopulatedFields());
204206
}
205207

208+
@Test
209+
void parseMethods_resumableUpload() {
210+
FileDescriptor resumableUploadFileDescriptor = ResumableUpload.getDescriptor();
211+
ServiceDescriptor resumableUploadService = resumableUploadFileDescriptor.getServices().get(0);
212+
Map<String, Message> messageTypes = Parser.parseMessages(resumableUploadFileDescriptor);
213+
Map<String, ResourceName> resourceNames =
214+
Parser.parseResourceNames(resumableUploadFileDescriptor);
215+
Set<ResourceName> outputResourceNames = new HashSet<>();
216+
List<Method> methods =
217+
Parser.parseMethods(
218+
resumableUploadService,
219+
ECHO_PACKAGE,
220+
ECHO_PACKAGE,
221+
messageTypes,
222+
resourceNames,
223+
Optional.empty(),
224+
Optional.empty(),
225+
outputResourceNames,
226+
Transport.GRPC);
227+
228+
assertEquals(1, methods.size());
229+
Method uploadMethod = methods.get(0);
230+
assertEquals("UploadMedia", uploadMethod.name());
231+
assertFalse(uploadMethod.isResumableUpload());
232+
}
233+
206234
@Test
207235
void parseMethods_basicLro() {
208236
Map<String, Message> messageTypes = Parser.parseMessages(echoFileDescriptor);

sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/test/protoloader/TestProtoLoader.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import com.google.showcase.v1beta1.EchoOuterClass;
4646
import com.google.showcase.v1beta1.IdentityOuterClass;
4747
import com.google.showcase.v1beta1.MessagingOuterClass;
48+
import com.google.showcase.v1beta1.ResumableUpload;
4849
import com.google.showcase.v1beta1.TestingOuterClass;
4950
import com.google.test.callablenamingtype.CallableNameType;
5051
import com.google.testdata.v1.DeprecatedServiceOuterClass;
@@ -278,6 +279,45 @@ public GapicContext parseShowcaseTesting() {
278279
.build();
279280
}
280281

282+
public GapicContext parseShowcaseResumableUpload() {
283+
FileDescriptor fileDescriptor = ResumableUpload.getDescriptor();
284+
ServiceDescriptor serviceDescriptor = fileDescriptor.getServices().get(0);
285+
assertEquals("ResumableUploadService", serviceDescriptor.getName());
286+
287+
Map<String, Message> messageTypes = Parser.parseMessages(fileDescriptor);
288+
Map<String, ResourceName> resourceNames = Parser.parseResourceNames(fileDescriptor);
289+
Set<ResourceName> outputResourceNames = new HashSet<>();
290+
List<Service> services =
291+
Parser.parseService(
292+
fileDescriptor, messageTypes, resourceNames, Optional.empty(), outputResourceNames);
293+
294+
return GapicContext.builder()
295+
.setMessages(messageTypes)
296+
.setResourceNames(resourceNames)
297+
.setServices(adaptShowcaseResumableUploadForTest(services))
298+
.setHelperResourceNames(outputResourceNames)
299+
.setTransport(transport)
300+
.setServiceConfig(GapicServiceConfig.create(Optional.empty()))
301+
.build();
302+
}
303+
304+
private static List<Service> adaptShowcaseResumableUploadForTest(List<Service> services) {
305+
return services.stream()
306+
.map(
307+
s ->
308+
s.toBuilder()
309+
.setMethods(
310+
s.methods().stream()
311+
.map(
312+
m ->
313+
m.name().equals("UploadMedia")
314+
? m.toBuilder().setIsResumableUpload(true).build()
315+
: m)
316+
.collect(Collectors.toList()))
317+
.build())
318+
.collect(Collectors.toList());
319+
}
320+
281321
public GapicContext parseExplicitDynamicRoutingHeaderTesting() {
282322
FileDescriptor testingFileDescriptor =
283323
ExplicitDynamicRoutingHeaderTestingOuterClass.getDescriptor();
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
syntax = "proto3";
16+
17+
package google.showcase.v1beta1;
18+
19+
import "google/api/annotations.proto";
20+
import "google/api/client.proto";
21+
22+
option go_package = "github.com/googleapis/gapic-showcase/server/genproto";
23+
option java_package = "com.google.showcase.v1beta1";
24+
option java_multiple_files = true;
25+
option ruby_package = "Google::Showcase::V1beta1";
26+
27+
// A service showcasing universal resumable upload protocol support.
28+
service ResumableUploadService {
29+
option (google.api.default_host) = "localhost:7469";
30+
31+
// A method with media_upload annotation enabled.
32+
rpc UploadMedia(UploadMediaRequest) returns (UploadMediaResponse) {
33+
option (google.api.http) = {
34+
post: "/v1beta1/files:upload"
35+
body: "*"
36+
};
37+
}
38+
}
39+
40+
message UploadMediaRequest {
41+
string name = 1;
42+
}
43+
44+
message UploadMediaResponse {
45+
string name = 1;
46+
int64 size = 2;
47+
}

0 commit comments

Comments
 (0)