From 841aee81b1f7683b75a877707cf00908233105dd Mon Sep 17 00:00:00 2001 From: PhongChuong Date: Fri, 6 Jun 2025 10:17:26 -0400 Subject: [PATCH 1/4] feat: handle auto pagination for BigQuery v2 --- .../api/generator/engine/ast/TypeNode.java | 14 +++ ...tractServiceStubSettingsClassComposer.java | 62 ++++++++++++-- .../generator/gapic/protoparser/Parser.java | 11 ++- ...lientLibraryReflectConfigComposerTest.java | 2 + .../composer/grpc/goldens/EchoClient.golden | 71 ++++++++++++++++ .../grpc/goldens/EchoClientTest.golden | 52 ++++++++++++ .../composer/grpc/goldens/EchoSettings.golden | 12 +++ .../composer/grpc/goldens/EchoStub.golden | 5 ++ .../grpc/goldens/EchoStubSettings.golden | 31 ++++++- .../composer/grpc/goldens/GrpcEchoStub.golden | 29 +++++++ .../composer/grpc/goldens/MockEchoImpl.golden | 21 +++++ .../echoclient/AsyncPagedExpandLegacy.golden | 51 +++++++++++ .../echoclient/SyncPagedExpandLegacy.golden | 47 ++++++++++ .../rest/goldens/HttpJsonEchoStub.golden | 56 ++++++++++++ .../gapic/protoparser/ParserTest.java | 43 +++++++++- .../src/test/proto/bigquery_jobs.proto | 85 +++++++++++++++++++ .../src/test/proto/echo.proto | 26 ++++++ .../src/test/resources/bigquery_v2.yaml | 22 +++++ 18 files changed, 629 insertions(+), 11 deletions(-) create mode 100644 gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/samples/echoclient/AsyncPagedExpandLegacy.golden create mode 100644 gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/samples/echoclient/SyncPagedExpandLegacy.golden create mode 100644 gapic-generator-java/src/test/proto/bigquery_jobs.proto create mode 100644 gapic-generator-java/src/test/resources/bigquery_v2.yaml diff --git a/gapic-generator-java/src/main/java/com/google/api/generator/engine/ast/TypeNode.java b/gapic-generator-java/src/main/java/com/google/api/generator/engine/ast/TypeNode.java index b217feb057..6d02c39882 100644 --- a/gapic-generator-java/src/main/java/com/google/api/generator/engine/ast/TypeNode.java +++ b/gapic-generator-java/src/main/java/com/google/api/generator/engine/ast/TypeNode.java @@ -74,6 +74,20 @@ public enum TypeKind { withReference( VaporReference.builder().setName("Value").setPakkage("com.google.protobuf").build()); + public static final TypeNode UINT32VALUE = + withReference( + VaporReference.builder() + .setName("UInt32Value") + .setPakkage("com.google.protobuf") + .build()); + + public static final TypeNode INT32VALUE = + withReference( + VaporReference.builder() + .setName("Int32Value") + .setPakkage("com.google.protobuf") + .build()); + private static final Map BOXED_TYPE_MAP = createBoxedTypeMap(); public static final TypeNode VOID = builder().setTypeKind(TypeKind.VOID).build(); diff --git a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceStubSettingsClassComposer.java b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceStubSettingsClassComposer.java index 6357ee3f3c..565907e54f 100644 --- a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceStubSettingsClassComposer.java +++ b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceStubSettingsClassComposer.java @@ -149,6 +149,23 @@ public abstract class AbstractServiceStubSettingsClassComposer implements ClassC private final TransportContext transportContext; + // Maps of package->methodName to types for pagination with max_results field. + private static final ImmutableMap> + PAGINATE_MAX_RESULT_TYPES = + ImmutableMap.of( + "com.google.cloud.bigquery.v2", + ImmutableMap.of( + "ListDatasets", + TypeNode.UINT32VALUE, + "ListJobs", + TypeNode.INT32VALUE, + "ListModels", + TypeNode.UINT32VALUE, + "ListRoutines", + TypeNode.UINT32VALUE, + "ListTables", + TypeNode.UINT32VALUE)); + protected static final VariableExpr DEFAULT_SERVICE_SCOPES_VAR_EXPR = createDefaultServiceScopesVarExpr(); @@ -727,9 +744,12 @@ private static Expr createPagedListDescriptorAssignExpr( .build()); // Create injectPageSize method. + String pkg = method.inputType().reference().pakkage(); + String methodName = method.name(); VariableExpr pageSizeVarExpr = VariableExpr.withVariable( Variable.builder().setType(TypeNode.INT).setName("pageSize").build()); + // Re-declare for clarity and easier readability. returnType = method.inputType(); returnExpr = @@ -738,6 +758,20 @@ private static Expr createPagedListDescriptorAssignExpr( .setMethodName("set" + JavaStyle.toUpperCamelCase(method.pageSizeFieldName())) .setArguments(pageSizeVarExpr) .build(); + if (PAGINATE_MAX_RESULT_TYPES.containsKey(pkg) + && PAGINATE_MAX_RESULT_TYPES.get(pkg).containsKey(methodName)) { + returnExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(newBuilderExpr) + .setMethodName("set" + JavaStyle.toUpperCamelCase(method.pageSizeFieldName())) + .setArguments( + MethodInvocationExpr.builder() + .setStaticReferenceType(PAGINATE_MAX_RESULT_TYPES.get(pkg).get(methodName)) + .setMethodName("of") + .setArguments(pageSizeVarExpr) + .build()) + .build(); + } returnExpr = MethodInvocationExpr.builder() .setExprReferenceExpr(returnExpr) @@ -758,17 +792,33 @@ private static Expr createPagedListDescriptorAssignExpr( // TODO(miraleung): Test the edge cases where these proto fields aren't present. // Create extractPageSize method. returnType = TypeNode.INT_OBJECT; + returnExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(payloadVarExpr) + .setMethodName("get" + JavaStyle.toUpperCamelCase(method.pageSizeFieldName())) + .setReturnType(returnType) + .build(); + if (PAGINATE_MAX_RESULT_TYPES.containsKey(pkg) + && PAGINATE_MAX_RESULT_TYPES.get(pkg).containsKey(methodName)) { + // Return type is UINT32VALUE or INT32VALUE so use getValue to unwrap. + returnExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(payloadVarExpr) + .setMethodName("get" + JavaStyle.toUpperCamelCase(method.pageSizeFieldName())) + .build(); + returnExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(returnExpr) + .setMethodName("getValue") + .setReturnType(returnType) + .build(); + } anonClassMethods.add( methodStarterBuilder .setReturnType(returnType) .setName("extractPageSize") .setArguments(payloadVarExpr.toBuilder().setIsDecl(true).build()) - .setReturnExpr( - MethodInvocationExpr.builder() - .setExprReferenceExpr(payloadVarExpr) - .setMethodName("get" + JavaStyle.toUpperCamelCase(method.pageSizeFieldName())) - .setReturnType(returnType) - .build()) + .setReturnExpr(returnExpr) .build()); // Create extractNextToken method. diff --git a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java index 975ccf58fa..e4e99d13f3 100644 --- a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java +++ b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java @@ -121,6 +121,14 @@ enum SelectiveGapicType { private static final Set MIXIN_JAVA_PACKAGE_ALLOWLIST = ImmutableSet.of("com.google.iam.v1", "com.google.longrunning", "com.google.cloud.location"); + // List of services that can use max_result field as an alternative to page_size for pagination. + private static final ImmutableSet PAGINATION_MAX_RESULTS_SERVICES_ALLOWLIST = + ImmutableSet.of("google.cloud.bigquery.v2.JobService.ListJobs", + "google.cloud.bigquery.v2.RoutineService.ListRoutines", + "google.cloud.bigquery.v2.DatasetService.ListDatasets", + "google.cloud.bigquery.v2.ModelService.ListModels", + "google.cloud.bigquery.v2.TableService.ListTables"); + // Allow other parsers to access this. protected static final SourceCodeInfoParser SOURCE_CODE_INFO_PARSER = new SourceCodeInfoParser(); @@ -1029,7 +1037,8 @@ static String parsePageSizeFieldName( // page_size gets priority over max_results if both are present List fieldNames = new ArrayList<>(); fieldNames.add("page_size"); - if (transport == Transport.REST) { + if ((transport == Transport.REST) || (PAGINATION_MAX_RESULTS_SERVICES_ALLOWLIST.contains( + methodDescriptor.getFullName()))) { fieldNames.add("max_results"); } for (String fieldName : fieldNames) { diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/ClientLibraryReflectConfigComposerTest.java b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/ClientLibraryReflectConfigComposerTest.java index 715c7d5f7f..31d3f7a578 100644 --- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/ClientLibraryReflectConfigComposerTest.java +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/ClientLibraryReflectConfigComposerTest.java @@ -61,6 +61,8 @@ void composeReflectConfigs_showcase() { "com.google.showcase.v1beta1.Foobar$Builder", "com.google.showcase.v1beta1.Object", "com.google.showcase.v1beta1.Object$Builder", + "com.google.showcase.v1beta1.PagedExpandLegacyRequest", + "com.google.showcase.v1beta1.PagedExpandLegacyRequest$Builder", "com.google.showcase.v1beta1.PagedExpandRequest", "com.google.showcase.v1beta1.PagedExpandRequest$Builder", "com.google.showcase.v1beta1.PagedExpandResponse", diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/EchoClient.golden b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/EchoClient.golden index 431700191c..612111aea0 100644 --- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/EchoClient.golden +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/EchoClient.golden @@ -206,6 +206,20 @@ import javax.annotation.Generated; * * * + * + *

PagedExpandLegacy + *

+ * + *

Request object method variants only take one parameter, a request object, which must be constructed before the call.

+ *
    + *
  • pagedExpandLegacy(PagedExpandLegacyRequest request) + *

+ *

Callable method variants take no parameters and return an immutable API callable object, which can be used to initiate calls to the service.

+ *
    + *
  • pagedExpandLegacyCallable() + *

+ * + * * * *

See the individual methods for example code. @@ -1124,6 +1138,63 @@ public class EchoClient implements BackgroundResource { return stub.collideNameCallable(); } + // AUTO-GENERATED DOCUMENTATION AND METHOD. + /** + * Sample code: + * + *

{@code
+   * // This snippet has been automatically generated and should be regarded as a code template only.
+   * // It will require modifications to work:
+   * // - It may require correct/in-range values for request initialization.
+   * // - It may require specifying regional endpoints when creating the service client as shown in
+   * // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library
+   * try (EchoClient echoClient = EchoClient.create()) {
+   *   PagedExpandLegacyRequest request =
+   *       PagedExpandLegacyRequest.newBuilder()
+   *           .setContent("content951530617")
+   *           .setMaxResults(1128457243)
+   *           .setPageToken("pageToken873572522")
+   *           .build();
+   *   PagedExpandResponse response = echoClient.pagedExpandLegacy(request);
+   * }
+   * }
+ * + * @param request The request object containing all of the parameters for the API call. + * @throws com.google.api.gax.rpc.ApiException if the remote call fails + */ + public final PagedExpandResponse pagedExpandLegacy(PagedExpandLegacyRequest request) { + return pagedExpandLegacyCallable().call(request); + } + + // AUTO-GENERATED DOCUMENTATION AND METHOD. + /** + * Sample code: + * + *
{@code
+   * // This snippet has been automatically generated and should be regarded as a code template only.
+   * // It will require modifications to work:
+   * // - It may require correct/in-range values for request initialization.
+   * // - It may require specifying regional endpoints when creating the service client as shown in
+   * // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library
+   * try (EchoClient echoClient = EchoClient.create()) {
+   *   PagedExpandLegacyRequest request =
+   *       PagedExpandLegacyRequest.newBuilder()
+   *           .setContent("content951530617")
+   *           .setMaxResults(1128457243)
+   *           .setPageToken("pageToken873572522")
+   *           .build();
+   *   ApiFuture future =
+   *       echoClient.pagedExpandLegacyCallable().futureCall(request);
+   *   // Do something.
+   *   PagedExpandResponse response = future.get();
+   * }
+   * }
+ */ + public final UnaryCallable + pagedExpandLegacyCallable() { + return stub.pagedExpandLegacyCallable(); + } + @Override public final void close() { stub.close(); diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/EchoClientTest.golden b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/EchoClientTest.golden index 1372cdd94e..515649a093 100644 --- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/EchoClientTest.golden +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/EchoClientTest.golden @@ -26,6 +26,7 @@ import com.google.protobuf.Timestamp; import com.google.rpc.Status; import io.grpc.StatusRuntimeException; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.UUID; @@ -890,4 +891,55 @@ public class EchoClientTest { // Expected exception. } } + + @Test + public void pagedExpandLegacyTest() throws Exception { + PagedExpandResponse expectedResponse = + PagedExpandResponse.newBuilder() + .addAllResponses(new ArrayList()) + .setNextPageToken("nextPageToken-1386094857") + .build(); + mockEcho.addResponse(expectedResponse); + + PagedExpandLegacyRequest request = + PagedExpandLegacyRequest.newBuilder() + .setContent("content951530617") + .setMaxResults(1128457243) + .setPageToken("pageToken873572522") + .build(); + + PagedExpandResponse actualResponse = client.pagedExpandLegacy(request); + Assert.assertEquals(expectedResponse, actualResponse); + + List actualRequests = mockEcho.getRequests(); + Assert.assertEquals(1, actualRequests.size()); + PagedExpandLegacyRequest actualRequest = ((PagedExpandLegacyRequest) actualRequests.get(0)); + + Assert.assertEquals(request.getContent(), actualRequest.getContent()); + Assert.assertEquals(request.getMaxResults(), actualRequest.getMaxResults()); + Assert.assertEquals(request.getPageToken(), actualRequest.getPageToken()); + Assert.assertTrue( + channelProvider.isHeaderSent( + ApiClientHeaderProvider.getDefaultApiClientHeaderKey(), + GaxGrpcProperties.getDefaultApiClientHeaderPattern())); + } + + @Test + public void pagedExpandLegacyExceptionTest() throws Exception { + StatusRuntimeException exception = new StatusRuntimeException(io.grpc.Status.INVALID_ARGUMENT); + mockEcho.addException(exception); + + try { + PagedExpandLegacyRequest request = + PagedExpandLegacyRequest.newBuilder() + .setContent("content951530617") + .setMaxResults(1128457243) + .setPageToken("pageToken873572522") + .build(); + client.pagedExpandLegacy(request); + Assert.fail("No exception raised"); + } catch (InvalidArgumentException e) { + // Expected exception. + } + } } diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/EchoSettings.golden b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/EchoSettings.golden index e3cfa3f0f2..deb3a387bf 100644 --- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/EchoSettings.golden +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/EchoSettings.golden @@ -159,6 +159,12 @@ public class EchoSettings extends ClientSettings { return ((EchoStubSettings) getStubSettings()).collideNameSettings(); } + /** Returns the object with the settings used for calls to pagedExpandLegacy. */ + public UnaryCallSettings + pagedExpandLegacySettings() { + return ((EchoStubSettings) getStubSettings()).pagedExpandLegacySettings(); + } + public static final EchoSettings create(EchoStubSettings stub) throws IOException { return new EchoSettings.Builder(stub.toBuilder()).build(); } @@ -314,6 +320,12 @@ public class EchoSettings extends ClientSettings { return getStubSettingsBuilder().collideNameSettings(); } + /** Returns the builder for the settings used for calls to pagedExpandLegacy. */ + public UnaryCallSettings.Builder + pagedExpandLegacySettings() { + return getStubSettingsBuilder().pagedExpandLegacySettings(); + } + @Override public EchoSettings build() throws IOException { return new EchoSettings(this); diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/EchoStub.golden b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/EchoStub.golden index 9270f22851..c43465da68 100644 --- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/EchoStub.golden +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/EchoStub.golden @@ -18,6 +18,7 @@ import com.google.showcase.v1beta1.EchoRequest; import com.google.showcase.v1beta1.EchoResponse; import com.google.showcase.v1beta1.ExpandRequest; import com.google.showcase.v1beta1.Object; +import com.google.showcase.v1beta1.PagedExpandLegacyRequest; import com.google.showcase.v1beta1.PagedExpandRequest; import com.google.showcase.v1beta1.PagedExpandResponse; import com.google.showcase.v1beta1.WaitMetadata; @@ -92,6 +93,10 @@ public abstract class EchoStub implements BackgroundResource { throw new UnsupportedOperationException("Not implemented: collideNameCallable()"); } + public UnaryCallable pagedExpandLegacyCallable() { + throw new UnsupportedOperationException("Not implemented: pagedExpandLegacyCallable()"); + } + @Override public abstract void close(); } diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/EchoStubSettings.golden b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/EchoStubSettings.golden index e43bb64604..77d8fbf45a 100644 --- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/EchoStubSettings.golden +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/EchoStubSettings.golden @@ -43,6 +43,7 @@ import com.google.showcase.v1beta1.EchoRequest; import com.google.showcase.v1beta1.EchoResponse; import com.google.showcase.v1beta1.ExpandRequest; import com.google.showcase.v1beta1.Object; +import com.google.showcase.v1beta1.PagedExpandLegacyRequest; import com.google.showcase.v1beta1.PagedExpandRequest; import com.google.showcase.v1beta1.PagedExpandResponse; import com.google.showcase.v1beta1.WaitMetadata; @@ -149,6 +150,8 @@ public class EchoStubSettings extends StubSettings { waitOperationSettings; private final UnaryCallSettings blockSettings; private final UnaryCallSettings collideNameSettings; + private final UnaryCallSettings + pagedExpandLegacySettings; private static final PagedListDescriptor PAGED_EXPAND_PAGE_STR_DESC = @@ -309,6 +312,12 @@ public class EchoStubSettings extends StubSettings { return collideNameSettings; } + /** Returns the object with the settings used for calls to pagedExpandLegacy. */ + public UnaryCallSettings + pagedExpandLegacySettings() { + return pagedExpandLegacySettings; + } + public EchoStub createStub() throws IOException { if (getTransportChannelProvider() .getTransportName() @@ -394,6 +403,7 @@ public class EchoStubSettings extends StubSettings { waitOperationSettings = settingsBuilder.waitOperationSettings().build(); blockSettings = settingsBuilder.blockSettings().build(); collideNameSettings = settingsBuilder.collideNameSettings().build(); + pagedExpandLegacySettings = settingsBuilder.pagedExpandLegacySettings().build(); } /** Builder for EchoStubSettings. */ @@ -415,6 +425,8 @@ public class EchoStubSettings extends StubSettings { waitOperationSettings; private final UnaryCallSettings.Builder blockSettings; private final UnaryCallSettings.Builder collideNameSettings; + private final UnaryCallSettings.Builder + pagedExpandLegacySettings; private static final ImmutableMap> RETRYABLE_CODE_DEFINITIONS; @@ -476,6 +488,7 @@ public class EchoStubSettings extends StubSettings { waitOperationSettings = OperationCallSettings.newBuilder(); blockSettings = UnaryCallSettings.newUnaryCallSettingsBuilder(); collideNameSettings = UnaryCallSettings.newUnaryCallSettingsBuilder(); + pagedExpandLegacySettings = UnaryCallSettings.newUnaryCallSettingsBuilder(); unaryMethodSettingsBuilders = ImmutableList.>of( @@ -484,7 +497,8 @@ public class EchoStubSettings extends StubSettings { simplePagedExpandSettings, waitSettings, blockSettings, - collideNameSettings); + collideNameSettings, + pagedExpandLegacySettings); initDefaults(this); } @@ -502,6 +516,7 @@ public class EchoStubSettings extends StubSettings { waitOperationSettings = settings.waitOperationSettings.toBuilder(); blockSettings = settings.blockSettings.toBuilder(); collideNameSettings = settings.collideNameSettings.toBuilder(); + pagedExpandLegacySettings = settings.pagedExpandLegacySettings.toBuilder(); unaryMethodSettingsBuilders = ImmutableList.>of( @@ -510,7 +525,8 @@ public class EchoStubSettings extends StubSettings { simplePagedExpandSettings, waitSettings, blockSettings, - collideNameSettings); + collideNameSettings, + pagedExpandLegacySettings); } private static Builder createDefault() { @@ -561,6 +577,11 @@ public class EchoStubSettings extends StubSettings { .setRetryableCodes(RETRYABLE_CODE_DEFINITIONS.get("no_retry_0_codes")) .setRetrySettings(RETRY_PARAM_DEFINITIONS.get("no_retry_0_params")); + builder + .pagedExpandLegacySettings() + .setRetryableCodes(RETRYABLE_CODE_DEFINITIONS.get("no_retry_0_codes")) + .setRetrySettings(RETRY_PARAM_DEFINITIONS.get("no_retry_0_params")); + builder .waitOperationSettings() .setInitialCallSettings( @@ -662,6 +683,12 @@ public class EchoStubSettings extends StubSettings { return collideNameSettings; } + /** Returns the builder for the settings used for calls to pagedExpandLegacy. */ + public UnaryCallSettings.Builder + pagedExpandLegacySettings() { + return pagedExpandLegacySettings; + } + @Override public EchoStubSettings build() throws IOException { return new EchoStubSettings(this); diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/GrpcEchoStub.golden b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/GrpcEchoStub.golden index 940ab7d4c4..7972dc929b 100644 --- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/GrpcEchoStub.golden +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/GrpcEchoStub.golden @@ -22,6 +22,7 @@ import com.google.showcase.v1beta1.EchoRequest; import com.google.showcase.v1beta1.EchoResponse; import com.google.showcase.v1beta1.ExpandRequest; import com.google.showcase.v1beta1.Object; +import com.google.showcase.v1beta1.PagedExpandLegacyRequest; import com.google.showcase.v1beta1.PagedExpandRequest; import com.google.showcase.v1beta1.PagedExpandResponse; import com.google.showcase.v1beta1.WaitMetadata; @@ -126,6 +127,17 @@ public class GrpcEchoStub extends EchoStub { .setResponseMarshaller(ProtoUtils.marshaller(Object.getDefaultInstance())) .build(); + private static final MethodDescriptor + pagedExpandLegacyMethodDescriptor = + MethodDescriptor.newBuilder() + .setType(MethodDescriptor.MethodType.UNARY) + .setFullMethodName("google.showcase.v1beta1.Echo/PagedExpandLegacy") + .setRequestMarshaller( + ProtoUtils.marshaller(PagedExpandLegacyRequest.getDefaultInstance())) + .setResponseMarshaller( + ProtoUtils.marshaller(PagedExpandResponse.getDefaultInstance())) + .build(); + private final UnaryCallable echoCallable; private final ServerStreamingCallable expandCallable; private final ClientStreamingCallable collectCallable; @@ -141,6 +153,8 @@ public class GrpcEchoStub extends EchoStub { private final OperationCallable waitOperationCallable; private final UnaryCallable blockCallable; private final UnaryCallable collideNameCallable; + private final UnaryCallable + pagedExpandLegacyCallable; private final BackgroundResource backgroundResources; private final GrpcOperationsStub operationsStub; @@ -220,6 +234,11 @@ public class GrpcEchoStub extends EchoStub { GrpcCallSettings.newBuilder() .setMethodDescriptor(collideNameMethodDescriptor) .build(); + GrpcCallSettings + pagedExpandLegacyTransportSettings = + GrpcCallSettings.newBuilder() + .setMethodDescriptor(pagedExpandLegacyMethodDescriptor) + .build(); this.echoCallable = callableFactory.createUnaryCallable( @@ -264,6 +283,11 @@ public class GrpcEchoStub extends EchoStub { this.collideNameCallable = callableFactory.createUnaryCallable( collideNameTransportSettings, settings.collideNameSettings(), clientContext); + this.pagedExpandLegacyCallable = + callableFactory.createUnaryCallable( + pagedExpandLegacyTransportSettings, + settings.pagedExpandLegacySettings(), + clientContext); this.backgroundResources = new BackgroundResourceAggregation(clientContext.getBackgroundResources()); @@ -339,6 +363,11 @@ public class GrpcEchoStub extends EchoStub { return collideNameCallable; } + @Override + public UnaryCallable pagedExpandLegacyCallable() { + return pagedExpandLegacyCallable; + } + @Override public final void close() { try { diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/MockEchoImpl.golden b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/MockEchoImpl.golden index ae3ff13888..f8b42faff0 100644 --- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/MockEchoImpl.golden +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/MockEchoImpl.golden @@ -293,4 +293,25 @@ public class MockEchoImpl extends EchoImplBase { Exception.class.getName()))); } } + + @Override + public void pagedExpandLegacy( + PagedExpandLegacyRequest request, StreamObserver responseObserver) { + java.lang.Object response = responses.poll(); + if (response instanceof PagedExpandResponse) { + requests.add(request); + responseObserver.onNext(((PagedExpandResponse) response)); + responseObserver.onCompleted(); + } else if (response instanceof Exception) { + responseObserver.onError(((Exception) response)); + } else { + responseObserver.onError( + new IllegalArgumentException( + String.format( + "Unrecognized response type %s for method PagedExpandLegacy, expected %s or %s", + response == null ? "null" : response.getClass().getName(), + PagedExpandResponse.class.getName(), + Exception.class.getName()))); + } + } } diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/samples/echoclient/AsyncPagedExpandLegacy.golden b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/samples/echoclient/AsyncPagedExpandLegacy.golden new file mode 100644 index 0000000000..04fdf3b93f --- /dev/null +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/samples/echoclient/AsyncPagedExpandLegacy.golden @@ -0,0 +1,51 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.showcase.v1beta1.samples; + +// [START goldensample_generated_Echo_PagedExpandLegacy_async] +import com.google.api.core.ApiFuture; +import com.google.showcase.v1beta1.EchoClient; +import com.google.showcase.v1beta1.PagedExpandLegacyRequest; +import com.google.showcase.v1beta1.PagedExpandResponse; + +public class AsyncPagedExpandLegacy { + + public static void main(String[] args) throws Exception { + asyncPagedExpandLegacy(); + } + + public static void asyncPagedExpandLegacy() throws Exception { + // This snippet has been automatically generated and should be regarded as a code template only. + // It will require modifications to work: + // - It may require correct/in-range values for request initialization. + // - It may require specifying regional endpoints when creating the service client as shown in + // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library + try (EchoClient echoClient = EchoClient.create()) { + PagedExpandLegacyRequest request = + PagedExpandLegacyRequest.newBuilder() + .setContent("content951530617") + .setMaxResults(1128457243) + .setPageToken("pageToken873572522") + .build(); + ApiFuture future = + echoClient.pagedExpandLegacyCallable().futureCall(request); + // Do something. + PagedExpandResponse response = future.get(); + } + } +} +// [END goldensample_generated_Echo_PagedExpandLegacy_async] diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/samples/echoclient/SyncPagedExpandLegacy.golden b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/samples/echoclient/SyncPagedExpandLegacy.golden new file mode 100644 index 0000000000..51a0bad09a --- /dev/null +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/samples/echoclient/SyncPagedExpandLegacy.golden @@ -0,0 +1,47 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.showcase.v1beta1.samples; + +// [START goldensample_generated_Echo_PagedExpandLegacy_sync] +import com.google.showcase.v1beta1.EchoClient; +import com.google.showcase.v1beta1.PagedExpandLegacyRequest; +import com.google.showcase.v1beta1.PagedExpandResponse; + +public class SyncPagedExpandLegacy { + + public static void main(String[] args) throws Exception { + syncPagedExpandLegacy(); + } + + public static void syncPagedExpandLegacy() throws Exception { + // This snippet has been automatically generated and should be regarded as a code template only. + // It will require modifications to work: + // - It may require correct/in-range values for request initialization. + // - It may require specifying regional endpoints when creating the service client as shown in + // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library + try (EchoClient echoClient = EchoClient.create()) { + PagedExpandLegacyRequest request = + PagedExpandLegacyRequest.newBuilder() + .setContent("content951530617") + .setMaxResults(1128457243) + .setPageToken("pageToken873572522") + .build(); + PagedExpandResponse response = echoClient.pagedExpandLegacy(request); + } + } +} +// [END goldensample_generated_Echo_PagedExpandLegacy_sync] diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/rest/goldens/HttpJsonEchoStub.golden b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/rest/goldens/HttpJsonEchoStub.golden index 18904bdfbe..85129de60b 100644 --- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/rest/goldens/HttpJsonEchoStub.golden +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/rest/goldens/HttpJsonEchoStub.golden @@ -32,6 +32,7 @@ import com.google.showcase.v1beta1.EchoRequest; import com.google.showcase.v1beta1.EchoResponse; import com.google.showcase.v1beta1.ExpandRequest; import com.google.showcase.v1beta1.Object; +import com.google.showcase.v1beta1.PagedExpandLegacyRequest; import com.google.showcase.v1beta1.PagedExpandRequest; import com.google.showcase.v1beta1.PagedExpandResponse; import com.google.showcase.v1beta1.WaitMetadata; @@ -302,6 +303,42 @@ public class HttpJsonEchoStub extends EchoStub { .build()) .build(); + private static final ApiMethodDescriptor + pagedExpandLegacyMethodDescriptor = + ApiMethodDescriptor.newBuilder() + .setFullMethodName("google.showcase.v1beta1.Echo/PagedExpandLegacy") + .setHttpMethod("POST") + .setType(ApiMethodDescriptor.MethodType.UNARY) + .setRequestFormatter( + ProtoMessageRequestFormatter.newBuilder() + .setPath( + "/v1beta1/echo:pagedExpandLegacy", + request -> { + Map fields = new HashMap<>(); + ProtoRestSerializer serializer = + ProtoRestSerializer.create(); + return fields; + }) + .setQueryParamsExtractor( + request -> { + Map> fields = new HashMap<>(); + ProtoRestSerializer serializer = + ProtoRestSerializer.create(); + serializer.putQueryParam(fields, "$alt", "json;enum-encoding=int"); + return fields; + }) + .setRequestBodyExtractor( + request -> + ProtoRestSerializer.create() + .toBody("*", request.toBuilder().build(), true)) + .build()) + .setResponseParser( + ProtoMessageResponseParser.newBuilder() + .setDefaultInstance(PagedExpandResponse.getDefaultInstance()) + .setDefaultTypeRegistry(typeRegistry) + .build()) + .build(); + private final UnaryCallable echoCallable; private final ServerStreamingCallable expandCallable; private final UnaryCallable pagedExpandCallable; @@ -314,6 +351,8 @@ public class HttpJsonEchoStub extends EchoStub { private final OperationCallable waitOperationCallable; private final UnaryCallable blockCallable; private final UnaryCallable collideNameCallable; + private final UnaryCallable + pagedExpandLegacyCallable; private final BackgroundResource backgroundResources; private final HttpJsonOperationsStub httpJsonOperationsStub; @@ -440,6 +479,12 @@ public class HttpJsonEchoStub extends EchoStub { .setMethodDescriptor(collideNameMethodDescriptor) .setTypeRegistry(typeRegistry) .build(); + HttpJsonCallSettings + pagedExpandLegacyTransportSettings = + HttpJsonCallSettings.newBuilder() + .setMethodDescriptor(pagedExpandLegacyMethodDescriptor) + .setTypeRegistry(typeRegistry) + .build(); this.echoCallable = callableFactory.createUnaryCallable( @@ -478,6 +523,11 @@ public class HttpJsonEchoStub extends EchoStub { this.collideNameCallable = callableFactory.createUnaryCallable( collideNameTransportSettings, settings.collideNameSettings(), clientContext); + this.pagedExpandLegacyCallable = + callableFactory.createUnaryCallable( + pagedExpandLegacyTransportSettings, + settings.pagedExpandLegacySettings(), + clientContext); this.backgroundResources = new BackgroundResourceAggregation(clientContext.getBackgroundResources()); @@ -493,6 +543,7 @@ public class HttpJsonEchoStub extends EchoStub { methodDescriptors.add(waitMethodDescriptor); methodDescriptors.add(blockMethodDescriptor); methodDescriptors.add(collideNameMethodDescriptor); + methodDescriptors.add(pagedExpandLegacyMethodDescriptor); return methodDescriptors; } @@ -551,6 +602,11 @@ public class HttpJsonEchoStub extends EchoStub { return collideNameCallable; } + @Override + public UnaryCallable pagedExpandLegacyCallable() { + return pagedExpandLegacyCallable; + } + @Override public ClientStreamingCallable collectCallable() { throw new UnsupportedOperationException( diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java index 6ddf533cbb..8cf976d65c 100644 --- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java @@ -53,6 +53,7 @@ import com.google.protobuf.Descriptors.ServiceDescriptor; import com.google.protobuf.compiler.PluginProtos.CodeGeneratorRequest; import com.google.selective.generate.v1beta1.SelectiveApiGenerationOuterClass; +import com.google.cloud.bigquery.v2.JobProto; import com.google.showcase.v1beta1.EchoOuterClass; import com.google.showcase.v1beta1.TestingOuterClass; import com.google.testgapic.v1beta1.LockerProto; @@ -154,7 +155,7 @@ void parseMethods_basic() { outputResourceNames, Transport.GRPC); - assertEquals(10, methods.size()); + assertEquals(11, methods.size()); // Methods should appear in the same order as in the protobuf file. Method echoMethod = methods.get(0); @@ -218,7 +219,7 @@ void parseMethods_basicLro() { outputResourceNames, Transport.GRPC); - assertEquals(10, methods.size()); + assertEquals(11, methods.size()); // Methods should appear in the same order as in the protobuf file. Method waitMethod = methods.get(7); @@ -373,6 +374,44 @@ void parseMethodSignatures_basic() { argument); } + @Test + void parsePageSizeFieldName_basic(){ + MethodDescriptor methodDescriptor = echoService.getMethods().get(5); + assertEquals("PagedExpand", methodDescriptor.getName()); + Map messageTypes = Parser.parseMessages(echoFileDescriptor); + String pageSizeFieldName = Parser.parsePageSizeFieldName(methodDescriptor, messageTypes, Transport.GRPC); + assertEquals("page_size", pageSizeFieldName); + } + + @Test + void parsePageSizeFieldName_grpcLegacy(){ + MethodDescriptor methodDescriptor = echoService.getMethods().get(10); + assertEquals("PagedExpandLegacy", methodDescriptor.getName()); + Map messageTypes = Parser.parseMessages(echoFileDescriptor); + String pageSizeFieldName = Parser.parsePageSizeFieldName(methodDescriptor, messageTypes, Transport.GRPC); + assertNull(pageSizeFieldName); + } + + @Test + void parsePageSizeFieldName_restLegacy(){ + MethodDescriptor methodDescriptor = echoService.getMethods().get(10); + assertEquals("PagedExpandLegacy", methodDescriptor.getName()); + Map messageTypes = Parser.parseMessages(echoFileDescriptor); + String pageSizeFieldName = Parser.parsePageSizeFieldName(methodDescriptor, messageTypes, Transport.REST); + assertEquals("max_results", pageSizeFieldName); + } + + @Test + void parsePageSizeFieldName_bigqueryLegacy(){ + FileDescriptor bqJobFileDescriptor = JobProto.getDescriptor(); + ServiceDescriptor jobService = bqJobFileDescriptor.getServices().get(0); + MethodDescriptor methodDescriptor = jobService.getMethods().get(0); + assertEquals("ListJobs", methodDescriptor.getName()); + Map messageTypes = Parser.parseMessages(bqJobFileDescriptor); + String pageSizeFieldName = Parser.parsePageSizeFieldName(methodDescriptor, messageTypes, Transport.GRPC); + assertEquals("max_results", pageSizeFieldName); + } + @Test void parseMessagesAndResourceNames_update() { FileDescriptor lockerServiceFileDescriptor = LockerProto.getDescriptor(); diff --git a/gapic-generator-java/src/test/proto/bigquery_jobs.proto b/gapic-generator-java/src/test/proto/bigquery_jobs.proto new file mode 100644 index 0000000000..49424d7c32 --- /dev/null +++ b/gapic-generator-java/src/test/proto/bigquery_jobs.proto @@ -0,0 +1,85 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.cloud.bigquery.v2; + +import "google/api/annotations.proto"; +import "google/api/client.proto"; +import "google/api/field_behavior.proto"; +import "google/api/resource.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/struct.proto"; +import "google/protobuf/wrappers.proto"; + +option java_outer_classname = "JobProto"; +option java_package = "com.google.cloud.bigquery.v2"; + +service JobService { + option (google.api.default_host) = "bigquery.googleapis.com"; + option (google.api.oauth_scopes) = + "https://www.googleapis.com/auth/bigquery," + "https://www.googleapis.com/auth/cloud-platform," + "https://www.googleapis.com/auth/cloud-platform.read-only," + "https://www.googleapis.com/auth/devstorage.full_control," + "https://www.googleapis.com/auth/devstorage.read_only," + "https://www.googleapis.com/auth/devstorage.read_write"; + + // Lists all jobs that you started in the specified project. Job information + // is available for a six month period after creation. The job list is sorted + // in reverse chronological order, by job creation time. Requires the Can View + // project role, or the Is Owner project role if you set the allUsers + // property. + rpc ListJobs(ListJobsRequest) returns (JobList) { + option (google.api.http) = { + get: "/bigquery/v2/projects/{project_id=*}/jobs" + }; + } +} + +// Simplified BigQuery Jobs service ListJobsRequest for testing. +message ListJobsRequest { + // Project ID of the jobs to list. + string project_id = 1; + + // The maximum number of results to return in a single response page. + // Leverage the page tokens to iterate through the entire collection. + google.protobuf.Int32Value max_results = 3; + + // Page token, returned by a previous call, to request the next page of + // results. + string page_token = 6; +} + +// Simplified ListFormatJob for testing. +message ListFormatJob { + // Unique opaque ID of the job. + string id = 1; +} + +// Simplified JobList response for testing. +message JobList { + // A hash of this page of results. + string etag = 1; + + // The resource type of the response. + string kind = 2; + + // A token to request the next page of results. + string next_page_token = 3; + + // List of jobs that were requested. + repeated ListFormatJob jobs = 4; +} \ No newline at end of file diff --git a/gapic-generator-java/src/test/proto/echo.proto b/gapic-generator-java/src/test/proto/echo.proto index 361e661459..28052e26c6 100644 --- a/gapic-generator-java/src/test/proto/echo.proto +++ b/gapic-generator-java/src/test/proto/echo.proto @@ -140,6 +140,16 @@ service Echo { body: "*" }; } + + // This is similar to the PagedExpand except that it uses + // max_results instead of page_size, as some legacy APIs still + // do. New APIs should NOT use this pattern. + rpc PagedExpandLegacy(PagedExpandLegacyRequest) returns (PagedExpandResponse) { + option (google.api.http) = { + post: "/v1beta1/echo:pagedExpandLegacy" + body: "*" + }; + } } // A severity enum used to test enum capabilities in GAPIC surfaces @@ -295,3 +305,19 @@ message BlockResponse { // here. string content = 1; } + +// The request for the PagedExpandLegacy method. This is a pattern used by some legacy APIs. New +// APIs should NOT use this pattern, but rather something like PagedExpandRequest which conforms to +// aip.dev/158. +message PagedExpandLegacyRequest { + // The string to expand. + string content = 1 [(google.api.field_behavior) = REQUIRED]; + + // The number of words to returned in each page. + // (-- aip.dev/not-precedent: This is a legacy, non-standard pattern that + // violates aip.dev/158. Ordinarily, this should be page_size. --) + int32 max_results = 2; + + // The position of the page to be returned. + string page_token = 3; +} diff --git a/gapic-generator-java/src/test/resources/bigquery_v2.yaml b/gapic-generator-java/src/test/resources/bigquery_v2.yaml new file mode 100644 index 0000000000..0eae4d861c --- /dev/null +++ b/gapic-generator-java/src/test/resources/bigquery_v2.yaml @@ -0,0 +1,22 @@ +type: google.api.Service +config_version: 3 +name: bigquery.googleapis.com +title: BigQuery GAPIC Testing API + +publishing: + # ... + library_settings: + - version: google.cloud.bigquery.v2 + java_settings: + common: + selective_gapic_generation: + methods: + - google.cloud.bigquery.v2.JobService.ListJobs + generate_omitted_as_internal: false + reference_docs_uri: www.abc.net + destinations: + - PACKAGE_MANAGER + python_settings: + common: + destinations: + - PACKAGE_MANAGER From b5498a060616dbd3167578307322dab93e94895d Mon Sep 17 00:00:00 2001 From: PhongChuong Date: Fri, 6 Jun 2025 11:11:35 -0400 Subject: [PATCH 2/4] fix lint --- .../api/generator/engine/ast/TypeNode.java | 5 +--- ...tractServiceStubSettingsClassComposer.java | 24 +++++++++---------- .../generator/gapic/protoparser/Parser.java | 7 +++--- .../gapic/protoparser/ParserTest.java | 22 ++++++++++------- 4 files changed, 30 insertions(+), 28 deletions(-) diff --git a/gapic-generator-java/src/main/java/com/google/api/generator/engine/ast/TypeNode.java b/gapic-generator-java/src/main/java/com/google/api/generator/engine/ast/TypeNode.java index 6d02c39882..12236842fc 100644 --- a/gapic-generator-java/src/main/java/com/google/api/generator/engine/ast/TypeNode.java +++ b/gapic-generator-java/src/main/java/com/google/api/generator/engine/ast/TypeNode.java @@ -83,10 +83,7 @@ public enum TypeKind { public static final TypeNode INT32VALUE = withReference( - VaporReference.builder() - .setName("Int32Value") - .setPakkage("com.google.protobuf") - .build()); + VaporReference.builder().setName("Int32Value").setPakkage("com.google.protobuf").build()); private static final Map BOXED_TYPE_MAP = createBoxedTypeMap(); diff --git a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceStubSettingsClassComposer.java b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceStubSettingsClassComposer.java index 565907e54f..2dac2422eb 100644 --- a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceStubSettingsClassComposer.java +++ b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceStubSettingsClassComposer.java @@ -152,19 +152,19 @@ public abstract class AbstractServiceStubSettingsClassComposer implements ClassC // Maps of package->methodName to types for pagination with max_results field. private static final ImmutableMap> PAGINATE_MAX_RESULT_TYPES = - ImmutableMap.of( - "com.google.cloud.bigquery.v2", ImmutableMap.of( - "ListDatasets", - TypeNode.UINT32VALUE, - "ListJobs", - TypeNode.INT32VALUE, - "ListModels", - TypeNode.UINT32VALUE, - "ListRoutines", - TypeNode.UINT32VALUE, - "ListTables", - TypeNode.UINT32VALUE)); + "com.google.cloud.bigquery.v2", + ImmutableMap.of( + "ListDatasets", + TypeNode.UINT32VALUE, + "ListJobs", + TypeNode.INT32VALUE, + "ListModels", + TypeNode.UINT32VALUE, + "ListRoutines", + TypeNode.UINT32VALUE, + "ListTables", + TypeNode.UINT32VALUE)); protected static final VariableExpr DEFAULT_SERVICE_SCOPES_VAR_EXPR = createDefaultServiceScopesVarExpr(); diff --git a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java index e4e99d13f3..f29c4a1561 100644 --- a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java +++ b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java @@ -123,7 +123,8 @@ enum SelectiveGapicType { // List of services that can use max_result field as an alternative to page_size for pagination. private static final ImmutableSet PAGINATION_MAX_RESULTS_SERVICES_ALLOWLIST = - ImmutableSet.of("google.cloud.bigquery.v2.JobService.ListJobs", + ImmutableSet.of( + "google.cloud.bigquery.v2.JobService.ListJobs", "google.cloud.bigquery.v2.RoutineService.ListRoutines", "google.cloud.bigquery.v2.DatasetService.ListDatasets", "google.cloud.bigquery.v2.ModelService.ListModels", @@ -1037,8 +1038,8 @@ static String parsePageSizeFieldName( // page_size gets priority over max_results if both are present List fieldNames = new ArrayList<>(); fieldNames.add("page_size"); - if ((transport == Transport.REST) || (PAGINATION_MAX_RESULTS_SERVICES_ALLOWLIST.contains( - methodDescriptor.getFullName()))) { + if ((transport == Transport.REST) + || (PAGINATION_MAX_RESULTS_SERVICES_ALLOWLIST.contains(methodDescriptor.getFullName()))) { fieldNames.add("max_results"); } for (String fieldName : fieldNames) { diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java index 8cf976d65c..26a59746cf 100644 --- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java @@ -46,6 +46,7 @@ import com.google.api.version.test.ApiVersionTestingOuterClass; import com.google.auto.populate.field.AutoPopulateFieldTestingOuterClass; import com.google.bookshop.v1beta1.BookshopProto; +import com.google.cloud.bigquery.v2.JobProto; import com.google.common.collect.ImmutableList; import com.google.common.truth.Truth; import com.google.protobuf.Descriptors.FileDescriptor; @@ -53,7 +54,6 @@ import com.google.protobuf.Descriptors.ServiceDescriptor; import com.google.protobuf.compiler.PluginProtos.CodeGeneratorRequest; import com.google.selective.generate.v1beta1.SelectiveApiGenerationOuterClass; -import com.google.cloud.bigquery.v2.JobProto; import com.google.showcase.v1beta1.EchoOuterClass; import com.google.showcase.v1beta1.TestingOuterClass; import com.google.testgapic.v1beta1.LockerProto; @@ -375,40 +375,44 @@ void parseMethodSignatures_basic() { } @Test - void parsePageSizeFieldName_basic(){ + void parsePageSizeFieldName_basic() { MethodDescriptor methodDescriptor = echoService.getMethods().get(5); assertEquals("PagedExpand", methodDescriptor.getName()); Map messageTypes = Parser.parseMessages(echoFileDescriptor); - String pageSizeFieldName = Parser.parsePageSizeFieldName(methodDescriptor, messageTypes, Transport.GRPC); + String pageSizeFieldName = + Parser.parsePageSizeFieldName(methodDescriptor, messageTypes, Transport.GRPC); assertEquals("page_size", pageSizeFieldName); } @Test - void parsePageSizeFieldName_grpcLegacy(){ + void parsePageSizeFieldName_grpcLegacy() { MethodDescriptor methodDescriptor = echoService.getMethods().get(10); assertEquals("PagedExpandLegacy", methodDescriptor.getName()); Map messageTypes = Parser.parseMessages(echoFileDescriptor); - String pageSizeFieldName = Parser.parsePageSizeFieldName(methodDescriptor, messageTypes, Transport.GRPC); + String pageSizeFieldName = + Parser.parsePageSizeFieldName(methodDescriptor, messageTypes, Transport.GRPC); assertNull(pageSizeFieldName); } @Test - void parsePageSizeFieldName_restLegacy(){ + void parsePageSizeFieldName_restLegacy() { MethodDescriptor methodDescriptor = echoService.getMethods().get(10); assertEquals("PagedExpandLegacy", methodDescriptor.getName()); Map messageTypes = Parser.parseMessages(echoFileDescriptor); - String pageSizeFieldName = Parser.parsePageSizeFieldName(methodDescriptor, messageTypes, Transport.REST); + String pageSizeFieldName = + Parser.parsePageSizeFieldName(methodDescriptor, messageTypes, Transport.REST); assertEquals("max_results", pageSizeFieldName); } @Test - void parsePageSizeFieldName_bigqueryLegacy(){ + void parsePageSizeFieldName_bigqueryLegacy() { FileDescriptor bqJobFileDescriptor = JobProto.getDescriptor(); ServiceDescriptor jobService = bqJobFileDescriptor.getServices().get(0); MethodDescriptor methodDescriptor = jobService.getMethods().get(0); assertEquals("ListJobs", methodDescriptor.getName()); Map messageTypes = Parser.parseMessages(bqJobFileDescriptor); - String pageSizeFieldName = Parser.parsePageSizeFieldName(methodDescriptor, messageTypes, Transport.GRPC); + String pageSizeFieldName = + Parser.parsePageSizeFieldName(methodDescriptor, messageTypes, Transport.GRPC); assertEquals("max_results", pageSizeFieldName); } From cfa71b7ea74d4efb6c00f29c78e0e1a1c832755b Mon Sep 17 00:00:00 2001 From: PhongChuong Date: Wed, 11 Jun 2025 11:17:56 -0400 Subject: [PATCH 3/4] add golden unit tests --- .../GrpcServiceStubClassComposerTest.java | 11 + .../ServiceStubSettingsClassComposerTest.java | 8 +- .../goldens/GrpcBigQueryJobServiceStub.golden | 156 ++++++++ .../goldens/JobServiceStubSettings.golden | 355 ++++++++++++++++++ .../servicesettings/stub/SyncListJobs.golden | 55 +++ .../test/protoloader/TestProtoLoader.java | 63 +++- .../src/test/resources/bigquery_v2.yaml | 27 +- .../resources/bigquery_v2_service_config.json | 10 + 8 files changed, 650 insertions(+), 35 deletions(-) create mode 100644 gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/GrpcBigQueryJobServiceStub.golden create mode 100644 gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/JobServiceStubSettings.golden create mode 100644 gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/samples/servicesettings/stub/SyncListJobs.golden create mode 100644 gapic-generator-java/src/test/resources/bigquery_v2_service_config.json diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/GrpcServiceStubClassComposerTest.java b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/GrpcServiceStubClassComposerTest.java index 5c910cd618..bde0a9c2d5 100644 --- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/GrpcServiceStubClassComposerTest.java +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/GrpcServiceStubClassComposerTest.java @@ -101,4 +101,15 @@ void generateGrpcServiceStubClass_callableNameType() { Assert.assertGoldenClass(this.getClass(), clazz, "GrpcCallableNameTypeStub.golden"); Assert.assertEmptySamples(clazz.samples()); } + + @Test + void generateGrpcServiceStubClass_bigQuery() { + GapicContext context = GrpcTestProtoLoader.instance().parseBigqueryService(); + + Service bigqueryJobService = context.services().get(0); + GapicClass clazz = + GrpcServiceStubClassComposer.instance().generate(context, bigqueryJobService); + Assert.assertGoldenClass(this.getClass(), clazz, "GrpcBigQueryJobServiceStub.golden"); + Assert.assertEmptySamples(clazz.samples()); + } } diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/ServiceStubSettingsClassComposerTest.java b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/ServiceStubSettingsClassComposerTest.java index fb6c34d451..3287309ee4 100644 --- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/ServiceStubSettingsClassComposerTest.java +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/ServiceStubSettingsClassComposerTest.java @@ -62,7 +62,13 @@ static Stream data() { GrpcTestProtoLoader.instance().parseSelectiveGenerationTesting(), "localhost:7469", "v1beta1", - 1)); + 1), + Arguments.of( + "JobServiceStubSettings", + GrpcTestProtoLoader.instance().parseBigqueryService(), + "bigquery", + "v2", + 0)); } @ParameterizedTest diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/GrpcBigQueryJobServiceStub.golden b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/GrpcBigQueryJobServiceStub.golden new file mode 100644 index 0000000000..5474098d37 --- /dev/null +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/GrpcBigQueryJobServiceStub.golden @@ -0,0 +1,156 @@ +package com.google.cloud.bigquery.v2.stub; + +import static com.google.cloud.bigquery.v2.JobServiceClient.ListJobsPagedResponse; + +import com.google.api.gax.core.BackgroundResource; +import com.google.api.gax.core.BackgroundResourceAggregation; +import com.google.api.gax.grpc.GrpcCallSettings; +import com.google.api.gax.grpc.GrpcStubCallableFactory; +import com.google.api.gax.rpc.ClientContext; +import com.google.api.gax.rpc.RequestParamsBuilder; +import com.google.api.gax.rpc.UnaryCallable; +import com.google.cloud.bigquery.v2.JobProto; +import com.google.longrunning.stub.GrpcOperationsStub; +import io.grpc.MethodDescriptor; +import io.grpc.protobuf.ProtoUtils; +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import javax.annotation.Generated; + +// AUTO-GENERATED DOCUMENTATION AND CLASS. +/** + * gRPC stub implementation for the JobService service API. + * + *

This class is for advanced usage and reflects the underlying API directly. + */ +@Generated("by gapic-generator-java") +public class GrpcJobServiceStub extends JobServiceStub { + private static final MethodDescriptor + listJobsMethodDescriptor = + MethodDescriptor.newBuilder() + .setType(MethodDescriptor.MethodType.UNARY) + .setFullMethodName("google.cloud.bigquery.v2.JobService/ListJobs") + .setRequestMarshaller( + ProtoUtils.marshaller(JobProto.ListJobsRequest.getDefaultInstance())) + .setResponseMarshaller(ProtoUtils.marshaller(JobProto.JobList.getDefaultInstance())) + .build(); + + private final UnaryCallable listJobsCallable; + private final UnaryCallable + listJobsPagedCallable; + + private final BackgroundResource backgroundResources; + private final GrpcOperationsStub operationsStub; + private final GrpcStubCallableFactory callableFactory; + + public static final GrpcJobServiceStub create(JobServiceStubSettings settings) + throws IOException { + return new GrpcJobServiceStub(settings, ClientContext.create(settings)); + } + + public static final GrpcJobServiceStub create(ClientContext clientContext) throws IOException { + return new GrpcJobServiceStub(JobServiceStubSettings.newBuilder().build(), clientContext); + } + + public static final GrpcJobServiceStub create( + ClientContext clientContext, GrpcStubCallableFactory callableFactory) throws IOException { + return new GrpcJobServiceStub( + JobServiceStubSettings.newBuilder().build(), clientContext, callableFactory); + } + + /** + * Constructs an instance of GrpcJobServiceStub, using the given settings. This is protected so + * that it is easy to make a subclass, but otherwise, the static factory methods should be + * preferred. + */ + protected GrpcJobServiceStub(JobServiceStubSettings settings, ClientContext clientContext) + throws IOException { + this(settings, clientContext, new GrpcJobServiceCallableFactory()); + } + + /** + * Constructs an instance of GrpcJobServiceStub, using the given settings. This is protected so + * that it is easy to make a subclass, but otherwise, the static factory methods should be + * preferred. + */ + protected GrpcJobServiceStub( + JobServiceStubSettings settings, + ClientContext clientContext, + GrpcStubCallableFactory callableFactory) + throws IOException { + this.callableFactory = callableFactory; + this.operationsStub = GrpcOperationsStub.create(clientContext, callableFactory); + + GrpcCallSettings listJobsTransportSettings = + GrpcCallSettings.newBuilder() + .setMethodDescriptor(listJobsMethodDescriptor) + .setParamsExtractor( + request -> { + RequestParamsBuilder builder = RequestParamsBuilder.create(); + builder.add("project_id", String.valueOf(request.getProjectId())); + return builder.build(); + }) + .build(); + + this.listJobsCallable = + callableFactory.createUnaryCallable( + listJobsTransportSettings, settings.listJobsSettings(), clientContext); + this.listJobsPagedCallable = + callableFactory.createPagedCallable( + listJobsTransportSettings, settings.listJobsSettings(), clientContext); + + this.backgroundResources = + new BackgroundResourceAggregation(clientContext.getBackgroundResources()); + } + + public GrpcOperationsStub getOperationsStub() { + return operationsStub; + } + + @Override + public UnaryCallable listJobsCallable() { + return listJobsCallable; + } + + @Override + public UnaryCallable listJobsPagedCallable() { + return listJobsPagedCallable; + } + + @Override + public final void close() { + try { + backgroundResources.close(); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new IllegalStateException("Failed to close resource", e); + } + } + + @Override + public void shutdown() { + backgroundResources.shutdown(); + } + + @Override + public boolean isShutdown() { + return backgroundResources.isShutdown(); + } + + @Override + public boolean isTerminated() { + return backgroundResources.isTerminated(); + } + + @Override + public void shutdownNow() { + backgroundResources.shutdownNow(); + } + + @Override + public boolean awaitTermination(long duration, TimeUnit unit) throws InterruptedException { + return backgroundResources.awaitTermination(duration, unit); + } +} diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/JobServiceStubSettings.golden b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/JobServiceStubSettings.golden new file mode 100644 index 0000000000..1d82b497bd --- /dev/null +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/JobServiceStubSettings.golden @@ -0,0 +1,355 @@ +package com.google.cloud.bigquery.v2.stub; + +import static com.google.cloud.bigquery.v2.JobServiceClient.ListJobsPagedResponse; + +import com.google.api.core.ApiFunction; +import com.google.api.core.ApiFuture; +import com.google.api.core.ObsoleteApi; +import com.google.api.gax.core.GaxProperties; +import com.google.api.gax.core.GoogleCredentialsProvider; +import com.google.api.gax.core.InstantiatingExecutorProvider; +import com.google.api.gax.grpc.GaxGrpcProperties; +import com.google.api.gax.grpc.GrpcTransportChannel; +import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider; +import com.google.api.gax.retrying.RetrySettings; +import com.google.api.gax.rpc.ApiCallContext; +import com.google.api.gax.rpc.ApiClientHeaderProvider; +import com.google.api.gax.rpc.ClientContext; +import com.google.api.gax.rpc.PageContext; +import com.google.api.gax.rpc.PagedCallSettings; +import com.google.api.gax.rpc.PagedListDescriptor; +import com.google.api.gax.rpc.PagedListResponseFactory; +import com.google.api.gax.rpc.StatusCode; +import com.google.api.gax.rpc.StubSettings; +import com.google.api.gax.rpc.TransportChannelProvider; +import com.google.api.gax.rpc.UnaryCallSettings; +import com.google.api.gax.rpc.UnaryCallable; +import com.google.cloud.bigquery.v2.JobProto; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.protobuf.Int32Value; +import java.io.IOException; +import java.time.Duration; +import java.util.List; +import javax.annotation.Generated; + +// AUTO-GENERATED DOCUMENTATION AND CLASS. +/** + * Settings class to configure an instance of {@link JobServiceStub}. + * + *

The default instance has everything set to sensible defaults: + * + *

    + *
  • The default service address (bigquery.googleapis.com) and default port (443) are used. + *
  • Credentials are acquired automatically through Application Default Credentials. + *
  • Retries are configured for idempotent methods but not for non-idempotent methods. + *
+ * + *

The builder of this class is recursive, so contained classes are themselves builders. When + * build() is called, the tree of builders is called to create the complete settings object. + * + *

For example, to set the + * [RetrySettings](https://cloud.google.com/java/docs/reference/gax/latest/com.google.api.gax.retrying.RetrySettings) + * of listJobs: + * + *

{@code
+ * // This snippet has been automatically generated and should be regarded as a code template only.
+ * // It will require modifications to work:
+ * // - It may require correct/in-range values for request initialization.
+ * // - It may require specifying regional endpoints when creating the service client as shown in
+ * // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library
+ * JobServiceStubSettings.Builder jobServiceSettingsBuilder = JobServiceStubSettings.newBuilder();
+ * jobServiceSettingsBuilder
+ *     .listJobsSettings()
+ *     .setRetrySettings(
+ *         jobServiceSettingsBuilder
+ *             .listJobsSettings()
+ *             .getRetrySettings()
+ *             .toBuilder()
+ *             .setInitialRetryDelayDuration(Duration.ofSeconds(1))
+ *             .setInitialRpcTimeoutDuration(Duration.ofSeconds(5))
+ *             .setMaxAttempts(5)
+ *             .setMaxRetryDelayDuration(Duration.ofSeconds(30))
+ *             .setMaxRpcTimeoutDuration(Duration.ofSeconds(60))
+ *             .setRetryDelayMultiplier(1.3)
+ *             .setRpcTimeoutMultiplier(1.5)
+ *             .setTotalTimeoutDuration(Duration.ofSeconds(300))
+ *             .build());
+ * JobServiceStubSettings jobServiceSettings = jobServiceSettingsBuilder.build();
+ * }
+ * + * Please refer to the [Client Side Retry + * Guide](https://github.com/googleapis/google-cloud-java/blob/main/docs/client_retries.md) for + * additional support in setting retries. + */ +@Generated("by gapic-generator-java") +public class JobServiceStubSettings extends StubSettings { + /** The default scopes of the service. */ + private static final ImmutableList DEFAULT_SERVICE_SCOPES = + ImmutableList.builder() + .add("https://www.googleapis.com/auth/bigquery") + .add("https://www.googleapis.com/auth/cloud-platform") + .add("https://www.googleapis.com/auth/cloud-platform.read-only") + .add("https://www.googleapis.com/auth/devstorage.full_control") + .add("https://www.googleapis.com/auth/devstorage.read_only") + .add("https://www.googleapis.com/auth/devstorage.read_write") + .build(); + + private final PagedCallSettings + listJobsSettings; + + private static final PagedListDescriptor< + JobProto.ListJobsRequest, JobProto.JobList, JobProto.ListFormatJob> + LIST_JOBS_PAGE_STR_DESC = + new PagedListDescriptor< + JobProto.ListJobsRequest, JobProto.JobList, JobProto.ListFormatJob>() { + @Override + public String emptyToken() { + return ""; + } + + @Override + public JobProto.ListJobsRequest injectToken( + JobProto.ListJobsRequest payload, String token) { + return JobProto.ListJobsRequest.newBuilder(payload).setPageToken(token).build(); + } + + @Override + public JobProto.ListJobsRequest injectPageSize( + JobProto.ListJobsRequest payload, int pageSize) { + return JobProto.ListJobsRequest.newBuilder(payload) + .setMaxResults(Int32Value.of(pageSize)) + .build(); + } + + @Override + public Integer extractPageSize(JobProto.ListJobsRequest payload) { + return payload.getMaxResults().getValue(); + } + + @Override + public String extractNextToken(JobProto.JobList payload) { + return payload.getNextPageToken(); + } + + @Override + public Iterable extractResources(JobProto.JobList payload) { + return payload.getJobsList(); + } + }; + + private static final PagedListResponseFactory< + JobProto.ListJobsRequest, JobProto.JobList, ListJobsPagedResponse> + LIST_JOBS_PAGE_STR_FACT = + new PagedListResponseFactory< + JobProto.ListJobsRequest, JobProto.JobList, ListJobsPagedResponse>() { + @Override + public ApiFuture getFuturePagedResponse( + UnaryCallable callable, + JobProto.ListJobsRequest request, + ApiCallContext context, + ApiFuture futureResponse) { + PageContext + pageContext = + PageContext.create(callable, LIST_JOBS_PAGE_STR_DESC, request, context); + return ListJobsPagedResponse.createAsync(pageContext, futureResponse); + } + }; + + /** Returns the object with the settings used for calls to listJobs. */ + public PagedCallSettings + listJobsSettings() { + return listJobsSettings; + } + + public JobServiceStub createStub() throws IOException { + if (getTransportChannelProvider() + .getTransportName() + .equals(GrpcTransportChannel.getGrpcTransportName())) { + return GrpcJobServiceStub.create(this); + } + throw new UnsupportedOperationException( + String.format( + "Transport not supported: %s", getTransportChannelProvider().getTransportName())); + } + + /** Returns the default service name. */ + @Override + public String getServiceName() { + return "bigquery"; + } + + /** Returns a builder for the default ExecutorProvider for this service. */ + public static InstantiatingExecutorProvider.Builder defaultExecutorProviderBuilder() { + return InstantiatingExecutorProvider.newBuilder(); + } + + /** Returns the default service endpoint. */ + @ObsoleteApi("Use getEndpoint() instead") + public static String getDefaultEndpoint() { + return "bigquery.googleapis.com:443"; + } + + /** Returns the default mTLS service endpoint. */ + public static String getDefaultMtlsEndpoint() { + return "bigquery.mtls.googleapis.com:443"; + } + + /** Returns the default service scopes. */ + public static List getDefaultServiceScopes() { + return DEFAULT_SERVICE_SCOPES; + } + + /** Returns a builder for the default credentials for this service. */ + public static GoogleCredentialsProvider.Builder defaultCredentialsProviderBuilder() { + return GoogleCredentialsProvider.newBuilder() + .setScopesToApply(DEFAULT_SERVICE_SCOPES) + .setUseJwtAccessWithScope(true); + } + + /** Returns a builder for the default ChannelProvider for this service. */ + public static InstantiatingGrpcChannelProvider.Builder defaultGrpcTransportProviderBuilder() { + return InstantiatingGrpcChannelProvider.newBuilder() + .setMaxInboundMessageSize(Integer.MAX_VALUE); + } + + public static TransportChannelProvider defaultTransportChannelProvider() { + return defaultGrpcTransportProviderBuilder().build(); + } + + public static ApiClientHeaderProvider.Builder defaultApiClientHeaderProviderBuilder() { + return ApiClientHeaderProvider.newBuilder() + .setGeneratedLibToken( + "gapic", GaxProperties.getLibraryVersion(JobServiceStubSettings.class)) + .setTransportToken( + GaxGrpcProperties.getGrpcTokenName(), GaxGrpcProperties.getGrpcVersion()); + } + + /** Returns a new builder for this class. */ + public static Builder newBuilder() { + return Builder.createDefault(); + } + + /** Returns a new builder for this class. */ + public static Builder newBuilder(ClientContext clientContext) { + return new Builder(clientContext); + } + + /** Returns a builder containing all the values of this settings class. */ + public Builder toBuilder() { + return new Builder(this); + } + + protected JobServiceStubSettings(Builder settingsBuilder) throws IOException { + super(settingsBuilder); + + listJobsSettings = settingsBuilder.listJobsSettings().build(); + } + + /** Builder for JobServiceStubSettings. */ + public static class Builder extends StubSettings.Builder { + private final ImmutableList> unaryMethodSettingsBuilders; + private final PagedCallSettings.Builder< + JobProto.ListJobsRequest, JobProto.JobList, ListJobsPagedResponse> + listJobsSettings; + private static final ImmutableMap> + RETRYABLE_CODE_DEFINITIONS; + + static { + ImmutableMap.Builder> definitions = + ImmutableMap.builder(); + definitions.put( + "no_retry_0_codes", ImmutableSet.copyOf(Lists.newArrayList())); + RETRYABLE_CODE_DEFINITIONS = definitions.build(); + } + + private static final ImmutableMap RETRY_PARAM_DEFINITIONS; + + static { + ImmutableMap.Builder definitions = ImmutableMap.builder(); + RetrySettings settings = null; + settings = + RetrySettings.newBuilder() + .setInitialRpcTimeoutDuration(Duration.ofMillis(5000L)) + .setRpcTimeoutMultiplier(1.0) + .setMaxRpcTimeoutDuration(Duration.ofMillis(5000L)) + .setTotalTimeoutDuration(Duration.ofMillis(5000L)) + .build(); + definitions.put("no_retry_0_params", settings); + RETRY_PARAM_DEFINITIONS = definitions.build(); + } + + protected Builder() { + this(((ClientContext) null)); + } + + protected Builder(ClientContext clientContext) { + super(clientContext); + + listJobsSettings = PagedCallSettings.newBuilder(LIST_JOBS_PAGE_STR_FACT); + + unaryMethodSettingsBuilders = + ImmutableList.>of(listJobsSettings); + initDefaults(this); + } + + protected Builder(JobServiceStubSettings settings) { + super(settings); + + listJobsSettings = settings.listJobsSettings.toBuilder(); + + unaryMethodSettingsBuilders = + ImmutableList.>of(listJobsSettings); + } + + private static Builder createDefault() { + Builder builder = new Builder(((ClientContext) null)); + + builder.setTransportChannelProvider(defaultTransportChannelProvider()); + builder.setCredentialsProvider(defaultCredentialsProviderBuilder().build()); + builder.setInternalHeaderProvider(defaultApiClientHeaderProviderBuilder().build()); + builder.setMtlsEndpoint(getDefaultMtlsEndpoint()); + builder.setSwitchToMtlsEndpointAllowed(true); + + return initDefaults(builder); + } + + private static Builder initDefaults(Builder builder) { + builder + .listJobsSettings() + .setRetryableCodes(RETRYABLE_CODE_DEFINITIONS.get("no_retry_0_codes")) + .setRetrySettings(RETRY_PARAM_DEFINITIONS.get("no_retry_0_params")); + + return builder; + } + + /** + * Applies the given settings updater function to all of the unary API methods in this service. + * + *

Note: This method does not support applying settings to streaming methods. + */ + public Builder applyToAllUnaryMethods( + ApiFunction, Void> settingsUpdater) { + super.applyToAllUnaryMethods(unaryMethodSettingsBuilders, settingsUpdater); + return this; + } + + public ImmutableList> unaryMethodSettingsBuilders() { + return unaryMethodSettingsBuilders; + } + + /** Returns the builder for the settings used for calls to listJobs. */ + public PagedCallSettings.Builder< + JobProto.ListJobsRequest, JobProto.JobList, ListJobsPagedResponse> + listJobsSettings() { + return listJobsSettings; + } + + @Override + public JobServiceStubSettings build() throws IOException { + return new JobServiceStubSettings(this); + } + } +} diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/samples/servicesettings/stub/SyncListJobs.golden b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/samples/servicesettings/stub/SyncListJobs.golden new file mode 100644 index 0000000000..ba4be080cd --- /dev/null +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/samples/servicesettings/stub/SyncListJobs.golden @@ -0,0 +1,55 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigquery.v2.stub.samples; + +// [START goldensample_generated_JobServiceStubSettings_ListJobs_sync] +import com.google.cloud.bigquery.v2.stub.JobServiceStubSettings; +import java.time.Duration; + +public class SyncListJobs { + + public static void main(String[] args) throws Exception { + syncListJobs(); + } + + public static void syncListJobs() throws Exception { + // This snippet has been automatically generated and should be regarded as a code template only. + // It will require modifications to work: + // - It may require correct/in-range values for request initialization. + // - It may require specifying regional endpoints when creating the service client as shown in + // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library + JobServiceStubSettings.Builder jobServiceSettingsBuilder = JobServiceStubSettings.newBuilder(); + jobServiceSettingsBuilder + .listJobsSettings() + .setRetrySettings( + jobServiceSettingsBuilder + .listJobsSettings() + .getRetrySettings() + .toBuilder() + .setInitialRetryDelayDuration(Duration.ofSeconds(1)) + .setInitialRpcTimeoutDuration(Duration.ofSeconds(5)) + .setMaxAttempts(5) + .setMaxRetryDelayDuration(Duration.ofSeconds(30)) + .setMaxRpcTimeoutDuration(Duration.ofSeconds(60)) + .setRetryDelayMultiplier(1.3) + .setRpcTimeoutMultiplier(1.5) + .setTotalTimeoutDuration(Duration.ofSeconds(300)) + .build()); + JobServiceStubSettings jobServiceSettings = jobServiceSettingsBuilder.build(); + } +} +// [END goldensample_generated_JobServiceStubSettings_ListJobs_sync] diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/test/protoloader/TestProtoLoader.java b/gapic-generator-java/src/test/java/com/google/api/generator/test/protoloader/TestProtoLoader.java index 1030e8231e..883ebbb446 100644 --- a/gapic-generator-java/src/test/java/com/google/api/generator/test/protoloader/TestProtoLoader.java +++ b/gapic-generator-java/src/test/java/com/google/api/generator/test/protoloader/TestProtoLoader.java @@ -31,6 +31,7 @@ import com.google.api.version.test.ApiVersionTestingOuterClass; import com.google.auto.populate.field.AutoPopulateFieldTestingOuterClass; import com.google.bookshop.v1beta1.BookshopProto; +import com.google.cloud.bigquery.v2.JobProto; import com.google.explicit.dynamic.routing.header.ExplicitDynamicRoutingHeaderTestingOuterClass; import com.google.logging.v2.LogEntryProto; import com.google.logging.v2.LoggingConfigProto; @@ -86,7 +87,7 @@ public static TestProtoLoader instance() { public GapicContext parseDeprecatedService() { FileDescriptor fileDescriptor = DeprecatedServiceOuterClass.getDescriptor(); ServiceDescriptor serviceDescriptor = fileDescriptor.getServices().get(0); - assertEquals(serviceDescriptor.getName(), "DeprecatedService"); + assertEquals("DeprecatedService", serviceDescriptor.getName()); Map messageTypes = Parser.parseMessages(fileDescriptor); Map resourceNames = new HashMap<>(); @@ -114,7 +115,7 @@ public GapicContext parseDeprecatedService() { public GapicContext parseBookshopService() { FileDescriptor fileDescriptor = BookshopProto.getDescriptor(); ServiceDescriptor serviceDescriptor = fileDescriptor.getServices().get(0); - assertEquals(serviceDescriptor.getName(), "Bookshop"); + assertEquals("Bookshop", serviceDescriptor.getName()); Map messageTypes = Parser.parseMessages(fileDescriptor); Map resourceNames = new HashMap<>(); @@ -142,7 +143,7 @@ public GapicContext parseBookshopService() { public GapicContext parseNestedMessage() { FileDescriptor fileDescriptor = TypesTestingProto.getDescriptor(); ServiceDescriptor serviceDescriptor = fileDescriptor.getServices().get(0); - assertEquals(serviceDescriptor.getName(), "NestedMessageService"); + assertEquals("NestedMessageService", serviceDescriptor.getName()); Map messageTypes = Parser.parseMessages(fileDescriptor); FileDescriptor messageFileDescriptor = NestedMessageProto.getDescriptor(); @@ -166,7 +167,7 @@ public GapicContext parseNestedMessage() { public GapicContext parseShowcaseEcho() { FileDescriptor echoFileDescriptor = EchoOuterClass.getDescriptor(); ServiceDescriptor echoServiceDescriptor = echoFileDescriptor.getServices().get(0); - assertEquals(echoServiceDescriptor.getName(), "Echo"); + assertEquals("Echo", echoServiceDescriptor.getName()); String serviceYamlFilename = "echo_v1beta1.yaml"; Path serviceYamlPath = Paths.get(testFilesDirectory, serviceYamlFilename); @@ -207,7 +208,7 @@ public GapicContext parseShowcaseEcho() { public GapicContext parseShowcaseIdentity() { FileDescriptor fileDescriptor = IdentityOuterClass.getDescriptor(); ServiceDescriptor identityService = fileDescriptor.getServices().get(0); - assertEquals(identityService.getName(), "Identity"); + assertEquals("Identity", identityService.getName()); Map messageTypes = Parser.parseMessages(fileDescriptor); Map resourceNames = Parser.parseResourceNames(fileDescriptor); @@ -228,7 +229,7 @@ public GapicContext parseShowcaseIdentity() { public GapicContext parseShowcaseMessaging() { FileDescriptor fileDescriptor = MessagingOuterClass.getDescriptor(); ServiceDescriptor messagingService = fileDescriptor.getServices().get(0); - assertEquals(messagingService.getName(), "Messaging"); + assertEquals("Messaging", messagingService.getName()); Map messageTypes = Parser.parseMessages(fileDescriptor); Map resourceNames = Parser.parseResourceNames(fileDescriptor); @@ -249,7 +250,7 @@ public GapicContext parseShowcaseMessaging() { public GapicContext parseShowcaseTesting() { FileDescriptor testingFileDescriptor = TestingOuterClass.getDescriptor(); ServiceDescriptor testingService = testingFileDescriptor.getServices().get(0); - assertEquals(testingService.getName(), "Testing"); + assertEquals("Testing", testingService.getName()); Map messageTypes = Parser.parseMessages(testingFileDescriptor); Map resourceNames = Parser.parseResourceNames(testingFileDescriptor); @@ -275,7 +276,7 @@ public GapicContext parseExplicitDynamicRoutingHeaderTesting() { FileDescriptor testingFileDescriptor = ExplicitDynamicRoutingHeaderTestingOuterClass.getDescriptor(); ServiceDescriptor testingService = testingFileDescriptor.getServices().get(0); - assertEquals(testingService.getName(), "ExplicitDynamicRoutingHeaderTesting"); + assertEquals("ExplicitDynamicRoutingHeaderTesting", testingService.getName()); Map messageTypes = Parser.parseMessages(testingFileDescriptor); Map resourceNames = Parser.parseResourceNames(testingFileDescriptor); @@ -300,7 +301,7 @@ public GapicContext parseExplicitDynamicRoutingHeaderTesting() { public GapicContext parseApiVersionTesting() { FileDescriptor testingFileDescriptor = ApiVersionTestingOuterClass.getDescriptor(); ServiceDescriptor testingService = testingFileDescriptor.getServices().get(0); - assertEquals(testingService.getName(), "EchoWithVersion"); + assertEquals("EchoWithVersion", testingService.getName()); Map messageTypes = Parser.parseMessages(testingFileDescriptor); Map resourceNames = Parser.parseResourceNames(testingFileDescriptor); @@ -333,7 +334,7 @@ public GapicContext parseAutoPopulateFieldTesting() { AutoPopulateFieldTestingOuterClass.getDescriptor(); ServiceDescriptor autopopulationServiceDescriptor = autopopulationFileDescriptor.getServices().get(0); - assertEquals(autopopulationServiceDescriptor.getName(), "AutoPopulateFieldTesting"); + assertEquals("AutoPopulateFieldTesting", autopopulationServiceDescriptor.getName()); String serviceYamlFilename = "auto_populate_field_testing.yaml"; Path serviceYamlPath = Paths.get(testFilesDirectory, serviceYamlFilename); @@ -451,7 +452,7 @@ public GapicContext parsePubSubPublisher() { public GapicContext parseLogging() { FileDescriptor serviceFileDescriptor = LoggingProto.getDescriptor(); ServiceDescriptor serviceDescriptor = serviceFileDescriptor.getServices().get(0); - assertEquals(serviceDescriptor.getName(), "LoggingServiceV2"); + assertEquals("LoggingServiceV2", serviceDescriptor.getName()); List protoFiles = Arrays.asList( @@ -506,7 +507,7 @@ public GapicContext parseLogging() { public GapicContext parseCallabeNameType() { FileDescriptor serviceFileDescriptor = CallableNameType.getDescriptor(); ServiceDescriptor serviceDescriptor = serviceFileDescriptor.getServices().get(0); - assertEquals(serviceDescriptor.getName(), "CallableNameTypeService"); + assertEquals("CallableNameTypeService", serviceDescriptor.getName()); List protoFiles = Collections.singletonList(serviceFileDescriptor); @@ -539,6 +540,44 @@ public GapicContext parseCallabeNameType() { .build(); } + public GapicContext parseBigqueryService() { + FileDescriptor bigqueryFileDescriptor = JobProto.getDescriptor(); + ServiceDescriptor biqqueryServiceDescriptor = bigqueryFileDescriptor.getServices().get(0); + assertEquals("JobService", biqqueryServiceDescriptor.getName()); + + String serviceYamlFilename = "bigquery_v2.yaml"; + Path serviceYamlPath = Paths.get(testFilesDirectory, serviceYamlFilename); + Optional serviceYamlOpt = + ServiceYamlParser.parse(serviceYamlPath.toString()); + assertTrue(serviceYamlOpt.isPresent()); + + Map messageTypes = Parser.parseMessages(bigqueryFileDescriptor); + Map resourceNames = Parser.parseResourceNames(bigqueryFileDescriptor); + Set outputResourceNames = new HashSet<>(); + List services = + Parser.parseService( + bigqueryFileDescriptor, + messageTypes, + resourceNames, + serviceYamlOpt, + outputResourceNames); + + String jsonFilename = "bigquery_v2_service_config.json"; + Path jsonPath = Paths.get(testFilesDirectory, jsonFilename); + Optional configOpt = ServiceConfigParser.parse(jsonPath.toString()); + assertTrue(configOpt.isPresent()); + GapicServiceConfig config = configOpt.get(); + + return GapicContext.builder() + .setMessages(messageTypes) + .setResourceNames(resourceNames) + .setServices(services) + .setServiceConfig(config) + .setHelperResourceNames(outputResourceNames) + .setTransport(transport) + .build(); + } + public String getTestFilesDirectory() { return testFilesDirectory; } diff --git a/gapic-generator-java/src/test/resources/bigquery_v2.yaml b/gapic-generator-java/src/test/resources/bigquery_v2.yaml index 0eae4d861c..7f974c6f84 100644 --- a/gapic-generator-java/src/test/resources/bigquery_v2.yaml +++ b/gapic-generator-java/src/test/resources/bigquery_v2.yaml @@ -1,22 +1,5 @@ -type: google.api.Service -config_version: 3 -name: bigquery.googleapis.com -title: BigQuery GAPIC Testing API - -publishing: - # ... - library_settings: - - version: google.cloud.bigquery.v2 - java_settings: - common: - selective_gapic_generation: - methods: - - google.cloud.bigquery.v2.JobService.ListJobs - generate_omitted_as_internal: false - reference_docs_uri: www.abc.net - destinations: - - PACKAGE_MANAGER - python_settings: - common: - destinations: - - PACKAGE_MANAGER +type: com.google.api.codegen.ConfigProto +config_schema_version: 2.0.0 +language_settings: + java: + package_name: com.google.cloud.bigquery.v2 \ No newline at end of file diff --git a/gapic-generator-java/src/test/resources/bigquery_v2_service_config.json b/gapic-generator-java/src/test/resources/bigquery_v2_service_config.json new file mode 100644 index 0000000000..8bc947af4e --- /dev/null +++ b/gapic-generator-java/src/test/resources/bigquery_v2_service_config.json @@ -0,0 +1,10 @@ +{ + "methodConfig": [ + { + "name": [ + {"service": "google.cloud.bigquery.v2.JobService"} + ], + "timeout": "5s" + } + ] +} From e429787e7611a3c58fc801b7a2bccfff60d3d828 Mon Sep 17 00:00:00 2001 From: PhongChuong Date: Tue, 17 Jun 2025 12:24:18 -0400 Subject: [PATCH 4/4] addressed PR comments --- ...tractServiceStubSettingsClassComposer.java | 42 +++++++------- .../generator/gapic/protoparser/Parser.java | 7 ++- .../goldens/GrpcBigQueryJobServiceStub.golden | 31 +++++++++++ .../goldens/JobServiceStubSettings.golden | 31 +++++++++-- .../servicesettings/stub/SyncDeleteJob.golden | 55 +++++++++++++++++++ .../gapic/protoparser/ParserTest.java | 8 ++- .../test/protoloader/TestProtoLoader.java | 8 +-- .../src/test/proto/bigquery_jobs.proto | 27 +++++++++ .../src/test/resources/bigquery_v2.yaml | 5 -- 9 files changed, 168 insertions(+), 46 deletions(-) create mode 100644 gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/samples/servicesettings/stub/SyncDeleteJob.golden delete mode 100644 gapic-generator-java/src/test/resources/bigquery_v2.yaml diff --git a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceStubSettingsClassComposer.java b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceStubSettingsClassComposer.java index 2dac2422eb..45c5d2193e 100644 --- a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceStubSettingsClassComposer.java +++ b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceStubSettingsClassComposer.java @@ -149,22 +149,19 @@ public abstract class AbstractServiceStubSettingsClassComposer implements ClassC private final TransportContext transportContext; - // Maps of package->methodName to types for pagination with max_results field. - private static final ImmutableMap> - PAGINATE_MAX_RESULT_TYPES = - ImmutableMap.of( - "com.google.cloud.bigquery.v2", - ImmutableMap.of( - "ListDatasets", - TypeNode.UINT32VALUE, - "ListJobs", - TypeNode.INT32VALUE, - "ListModels", - TypeNode.UINT32VALUE, - "ListRoutines", - TypeNode.UINT32VALUE, - "ListTables", - TypeNode.UINT32VALUE)); + // Maps of BigQuery methods to pagination types. + private static final ImmutableMap BIGQUERY_PAGINATE_MAX_RESULT_TYPES = + ImmutableMap.of( + "com.google.cloud.bigquery.v2.ListDatasets", + TypeNode.UINT32VALUE, + "com.google.cloud.bigquery.v2.ListJobs", + TypeNode.INT32VALUE, + "com.google.cloud.bigquery.v2.ListModels", + TypeNode.UINT32VALUE, + "com.google.cloud.bigquery.v2.ListRoutines", + TypeNode.UINT32VALUE, + "com.google.cloud.bigquery.v2.ListTables", + TypeNode.UINT32VALUE); protected static final VariableExpr DEFAULT_SERVICE_SCOPES_VAR_EXPR = createDefaultServiceScopesVarExpr(); @@ -744,8 +741,8 @@ private static Expr createPagedListDescriptorAssignExpr( .build()); // Create injectPageSize method. - String pkg = method.inputType().reference().pakkage(); - String methodName = method.name(); + String methodFullName = + String.format("%s.%s", method.inputType().reference().pakkage(), method.name()); VariableExpr pageSizeVarExpr = VariableExpr.withVariable( Variable.builder().setType(TypeNode.INT).setName("pageSize").build()); @@ -758,15 +755,15 @@ private static Expr createPagedListDescriptorAssignExpr( .setMethodName("set" + JavaStyle.toUpperCamelCase(method.pageSizeFieldName())) .setArguments(pageSizeVarExpr) .build(); - if (PAGINATE_MAX_RESULT_TYPES.containsKey(pkg) - && PAGINATE_MAX_RESULT_TYPES.get(pkg).containsKey(methodName)) { + if (BIGQUERY_PAGINATE_MAX_RESULT_TYPES.containsKey(methodFullName)) { returnExpr = MethodInvocationExpr.builder() .setExprReferenceExpr(newBuilderExpr) .setMethodName("set" + JavaStyle.toUpperCamelCase(method.pageSizeFieldName())) .setArguments( MethodInvocationExpr.builder() - .setStaticReferenceType(PAGINATE_MAX_RESULT_TYPES.get(pkg).get(methodName)) + .setStaticReferenceType( + BIGQUERY_PAGINATE_MAX_RESULT_TYPES.get(methodFullName)) .setMethodName("of") .setArguments(pageSizeVarExpr) .build()) @@ -798,8 +795,7 @@ private static Expr createPagedListDescriptorAssignExpr( .setMethodName("get" + JavaStyle.toUpperCamelCase(method.pageSizeFieldName())) .setReturnType(returnType) .build(); - if (PAGINATE_MAX_RESULT_TYPES.containsKey(pkg) - && PAGINATE_MAX_RESULT_TYPES.get(pkg).containsKey(methodName)) { + if (BIGQUERY_PAGINATE_MAX_RESULT_TYPES.containsKey(methodFullName)) { // Return type is UINT32VALUE or INT32VALUE so use getValue to unwrap. returnExpr = MethodInvocationExpr.builder() diff --git a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java index f29c4a1561..0ff6a71039 100644 --- a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java +++ b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java @@ -121,8 +121,9 @@ enum SelectiveGapicType { private static final Set MIXIN_JAVA_PACKAGE_ALLOWLIST = ImmutableSet.of("com.google.iam.v1", "com.google.longrunning", "com.google.cloud.location"); - // List of services that can use max_result field as an alternative to page_size for pagination. - private static final ImmutableSet PAGINATION_MAX_RESULTS_SERVICES_ALLOWLIST = + // List of BigQuery methods that can use max_result field as an alternative to page_size for + // pagination. + private static final ImmutableSet BIGQUERY_LEGACY_PAGINATION_ALLOWLIST = ImmutableSet.of( "google.cloud.bigquery.v2.JobService.ListJobs", "google.cloud.bigquery.v2.RoutineService.ListRoutines", @@ -1039,7 +1040,7 @@ static String parsePageSizeFieldName( List fieldNames = new ArrayList<>(); fieldNames.add("page_size"); if ((transport == Transport.REST) - || (PAGINATION_MAX_RESULTS_SERVICES_ALLOWLIST.contains(methodDescriptor.getFullName()))) { + || (BIGQUERY_LEGACY_PAGINATION_ALLOWLIST.contains(methodDescriptor.getFullName()))) { fieldNames.add("max_results"); } for (String fieldName : fieldNames) { diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/GrpcBigQueryJobServiceStub.golden b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/GrpcBigQueryJobServiceStub.golden index 5474098d37..c1c51ff44b 100644 --- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/GrpcBigQueryJobServiceStub.golden +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/GrpcBigQueryJobServiceStub.golden @@ -11,6 +11,7 @@ import com.google.api.gax.rpc.RequestParamsBuilder; import com.google.api.gax.rpc.UnaryCallable; import com.google.cloud.bigquery.v2.JobProto; import com.google.longrunning.stub.GrpcOperationsStub; +import com.google.protobuf.Empty; import io.grpc.MethodDescriptor; import io.grpc.protobuf.ProtoUtils; import java.io.IOException; @@ -26,6 +27,16 @@ import javax.annotation.Generated; */ @Generated("by gapic-generator-java") public class GrpcJobServiceStub extends JobServiceStub { + private static final MethodDescriptor + deleteJobMethodDescriptor = + MethodDescriptor.newBuilder() + .setType(MethodDescriptor.MethodType.UNARY) + .setFullMethodName("google.cloud.bigquery.v2.JobService/DeleteJob") + .setRequestMarshaller( + ProtoUtils.marshaller(JobProto.DeleteJobRequest.getDefaultInstance())) + .setResponseMarshaller(ProtoUtils.marshaller(Empty.getDefaultInstance())) + .build(); + private static final MethodDescriptor listJobsMethodDescriptor = MethodDescriptor.newBuilder() @@ -36,6 +47,7 @@ public class GrpcJobServiceStub extends JobServiceStub { .setResponseMarshaller(ProtoUtils.marshaller(JobProto.JobList.getDefaultInstance())) .build(); + private final UnaryCallable deleteJobCallable; private final UnaryCallable listJobsCallable; private final UnaryCallable listJobsPagedCallable; @@ -82,6 +94,17 @@ public class GrpcJobServiceStub extends JobServiceStub { this.callableFactory = callableFactory; this.operationsStub = GrpcOperationsStub.create(clientContext, callableFactory); + GrpcCallSettings deleteJobTransportSettings = + GrpcCallSettings.newBuilder() + .setMethodDescriptor(deleteJobMethodDescriptor) + .setParamsExtractor( + request -> { + RequestParamsBuilder builder = RequestParamsBuilder.create(); + builder.add("job_id", String.valueOf(request.getJobId())); + builder.add("project_id", String.valueOf(request.getProjectId())); + return builder.build(); + }) + .build(); GrpcCallSettings listJobsTransportSettings = GrpcCallSettings.newBuilder() .setMethodDescriptor(listJobsMethodDescriptor) @@ -93,6 +116,9 @@ public class GrpcJobServiceStub extends JobServiceStub { }) .build(); + this.deleteJobCallable = + callableFactory.createUnaryCallable( + deleteJobTransportSettings, settings.deleteJobSettings(), clientContext); this.listJobsCallable = callableFactory.createUnaryCallable( listJobsTransportSettings, settings.listJobsSettings(), clientContext); @@ -108,6 +134,11 @@ public class GrpcJobServiceStub extends JobServiceStub { return operationsStub; } + @Override + public UnaryCallable deleteJobCallable() { + return deleteJobCallable; + } + @Override public UnaryCallable listJobsCallable() { return listJobsCallable; diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/JobServiceStubSettings.golden b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/JobServiceStubSettings.golden index 1d82b497bd..d4bf730476 100644 --- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/JobServiceStubSettings.golden +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/JobServiceStubSettings.golden @@ -29,6 +29,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; +import com.google.protobuf.Empty; import com.google.protobuf.Int32Value; import java.io.IOException; import java.time.Duration; @@ -52,7 +53,7 @@ import javax.annotation.Generated; * *

For example, to set the * [RetrySettings](https://cloud.google.com/java/docs/reference/gax/latest/com.google.api.gax.retrying.RetrySettings) - * of listJobs: + * of deleteJob: * *

{@code
  * // This snippet has been automatically generated and should be regarded as a code template only.
@@ -62,10 +63,10 @@ import javax.annotation.Generated;
  * // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library
  * JobServiceStubSettings.Builder jobServiceSettingsBuilder = JobServiceStubSettings.newBuilder();
  * jobServiceSettingsBuilder
- *     .listJobsSettings()
+ *     .deleteJobSettings()
  *     .setRetrySettings(
  *         jobServiceSettingsBuilder
- *             .listJobsSettings()
+ *             .deleteJobSettings()
  *             .getRetrySettings()
  *             .toBuilder()
  *             .setInitialRetryDelayDuration(Duration.ofSeconds(1))
@@ -97,6 +98,7 @@ public class JobServiceStubSettings extends StubSettings
           .add("https://www.googleapis.com/auth/devstorage.read_write")
           .build();
 
+  private final UnaryCallSettings deleteJobSettings;
   private final PagedCallSettings
       listJobsSettings;
 
@@ -158,6 +160,11 @@ public class JobServiceStubSettings extends StubSettings
             }
           };
 
+  /** Returns the object with the settings used for calls to deleteJob. */
+  public UnaryCallSettings deleteJobSettings() {
+    return deleteJobSettings;
+  }
+
   /** Returns the object with the settings used for calls to listJobs. */
   public PagedCallSettings
       listJobsSettings() {
@@ -245,12 +252,14 @@ public class JobServiceStubSettings extends StubSettings
   protected JobServiceStubSettings(Builder settingsBuilder) throws IOException {
     super(settingsBuilder);
 
+    deleteJobSettings = settingsBuilder.deleteJobSettings().build();
     listJobsSettings = settingsBuilder.listJobsSettings().build();
   }
 
   /** Builder for JobServiceStubSettings. */
   public static class Builder extends StubSettings.Builder {
     private final ImmutableList> unaryMethodSettingsBuilders;
+    private final UnaryCallSettings.Builder deleteJobSettings;
     private final PagedCallSettings.Builder<
             JobProto.ListJobsRequest, JobProto.JobList, ListJobsPagedResponse>
         listJobsSettings;
@@ -288,20 +297,22 @@ public class JobServiceStubSettings extends StubSettings
     protected Builder(ClientContext clientContext) {
       super(clientContext);
 
+      deleteJobSettings = UnaryCallSettings.newUnaryCallSettingsBuilder();
       listJobsSettings = PagedCallSettings.newBuilder(LIST_JOBS_PAGE_STR_FACT);
 
       unaryMethodSettingsBuilders =
-          ImmutableList.>of(listJobsSettings);
+          ImmutableList.>of(deleteJobSettings, listJobsSettings);
       initDefaults(this);
     }
 
     protected Builder(JobServiceStubSettings settings) {
       super(settings);
 
+      deleteJobSettings = settings.deleteJobSettings.toBuilder();
       listJobsSettings = settings.listJobsSettings.toBuilder();
 
       unaryMethodSettingsBuilders =
-          ImmutableList.>of(listJobsSettings);
+          ImmutableList.>of(deleteJobSettings, listJobsSettings);
     }
 
     private static Builder createDefault() {
@@ -317,6 +328,11 @@ public class JobServiceStubSettings extends StubSettings
     }
 
     private static Builder initDefaults(Builder builder) {
+      builder
+          .deleteJobSettings()
+          .setRetryableCodes(RETRYABLE_CODE_DEFINITIONS.get("no_retry_0_codes"))
+          .setRetrySettings(RETRY_PARAM_DEFINITIONS.get("no_retry_0_params"));
+
       builder
           .listJobsSettings()
           .setRetryableCodes(RETRYABLE_CODE_DEFINITIONS.get("no_retry_0_codes"))
@@ -340,6 +356,11 @@ public class JobServiceStubSettings extends StubSettings
       return unaryMethodSettingsBuilders;
     }
 
+    /** Returns the builder for the settings used for calls to deleteJob. */
+    public UnaryCallSettings.Builder deleteJobSettings() {
+      return deleteJobSettings;
+    }
+
     /** Returns the builder for the settings used for calls to listJobs. */
     public PagedCallSettings.Builder<
             JobProto.ListJobsRequest, JobProto.JobList, ListJobsPagedResponse>
diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/samples/servicesettings/stub/SyncDeleteJob.golden b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/samples/servicesettings/stub/SyncDeleteJob.golden
new file mode 100644
index 0000000000..db01c32cd8
--- /dev/null
+++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/samples/servicesettings/stub/SyncDeleteJob.golden
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2025 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.bigquery.v2.stub.samples;
+
+// [START goldensample_generated_JobServiceStubSettings_DeleteJob_sync]
+import com.google.cloud.bigquery.v2.stub.JobServiceStubSettings;
+import java.time.Duration;
+
+public class SyncDeleteJob {
+
+  public static void main(String[] args) throws Exception {
+    syncDeleteJob();
+  }
+
+  public static void syncDeleteJob() throws Exception {
+    // This snippet has been automatically generated and should be regarded as a code template only.
+    // It will require modifications to work:
+    // - It may require correct/in-range values for request initialization.
+    // - It may require specifying regional endpoints when creating the service client as shown in
+    // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library
+    JobServiceStubSettings.Builder jobServiceSettingsBuilder = JobServiceStubSettings.newBuilder();
+    jobServiceSettingsBuilder
+        .deleteJobSettings()
+        .setRetrySettings(
+            jobServiceSettingsBuilder
+                .deleteJobSettings()
+                .getRetrySettings()
+                .toBuilder()
+                .setInitialRetryDelayDuration(Duration.ofSeconds(1))
+                .setInitialRpcTimeoutDuration(Duration.ofSeconds(5))
+                .setMaxAttempts(5)
+                .setMaxRetryDelayDuration(Duration.ofSeconds(30))
+                .setMaxRpcTimeoutDuration(Duration.ofSeconds(60))
+                .setRetryDelayMultiplier(1.3)
+                .setRpcTimeoutMultiplier(1.5)
+                .setTotalTimeoutDuration(Duration.ofSeconds(300))
+                .build());
+    JobServiceStubSettings jobServiceSettings = jobServiceSettingsBuilder.build();
+  }
+}
+// [END goldensample_generated_JobServiceStubSettings_DeleteJob_sync]
diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java
index 26a59746cf..e6325ded69 100644
--- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java
+++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java
@@ -408,11 +408,13 @@ void parsePageSizeFieldName_restLegacy() {
   void parsePageSizeFieldName_bigqueryLegacy() {
     FileDescriptor bqJobFileDescriptor = JobProto.getDescriptor();
     ServiceDescriptor jobService = bqJobFileDescriptor.getServices().get(0);
-    MethodDescriptor methodDescriptor = jobService.getMethods().get(0);
-    assertEquals("ListJobs", methodDescriptor.getName());
+    MethodDescriptor deleteJobMethodDescriptor = jobService.getMethods().get(0);
+    assertEquals("DeleteJob", deleteJobMethodDescriptor.getName());
+    MethodDescriptor listJobsMethodDescriptor = jobService.getMethods().get(1);
+    assertEquals("ListJobs", listJobsMethodDescriptor.getName());
     Map messageTypes = Parser.parseMessages(bqJobFileDescriptor);
     String pageSizeFieldName =
-        Parser.parsePageSizeFieldName(methodDescriptor, messageTypes, Transport.GRPC);
+        Parser.parsePageSizeFieldName(listJobsMethodDescriptor, messageTypes, Transport.GRPC);
     assertEquals("max_results", pageSizeFieldName);
   }
 
diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/test/protoloader/TestProtoLoader.java b/gapic-generator-java/src/test/java/com/google/api/generator/test/protoloader/TestProtoLoader.java
index 883ebbb446..0e4148110e 100644
--- a/gapic-generator-java/src/test/java/com/google/api/generator/test/protoloader/TestProtoLoader.java
+++ b/gapic-generator-java/src/test/java/com/google/api/generator/test/protoloader/TestProtoLoader.java
@@ -545,12 +545,6 @@ public GapicContext parseBigqueryService() {
     ServiceDescriptor biqqueryServiceDescriptor = bigqueryFileDescriptor.getServices().get(0);
     assertEquals("JobService", biqqueryServiceDescriptor.getName());
 
-    String serviceYamlFilename = "bigquery_v2.yaml";
-    Path serviceYamlPath = Paths.get(testFilesDirectory, serviceYamlFilename);
-    Optional serviceYamlOpt =
-        ServiceYamlParser.parse(serviceYamlPath.toString());
-    assertTrue(serviceYamlOpt.isPresent());
-
     Map messageTypes = Parser.parseMessages(bigqueryFileDescriptor);
     Map resourceNames = Parser.parseResourceNames(bigqueryFileDescriptor);
     Set outputResourceNames = new HashSet<>();
@@ -559,7 +553,7 @@ public GapicContext parseBigqueryService() {
             bigqueryFileDescriptor,
             messageTypes,
             resourceNames,
-            serviceYamlOpt,
+            Optional.empty(),
             outputResourceNames);
 
     String jsonFilename = "bigquery_v2_service_config.json";
diff --git a/gapic-generator-java/src/test/proto/bigquery_jobs.proto b/gapic-generator-java/src/test/proto/bigquery_jobs.proto
index 49424d7c32..9d6da3c8d9 100644
--- a/gapic-generator-java/src/test/proto/bigquery_jobs.proto
+++ b/gapic-generator-java/src/test/proto/bigquery_jobs.proto
@@ -37,6 +37,14 @@ service JobService {
     "https://www.googleapis.com/auth/devstorage.read_only,"
     "https://www.googleapis.com/auth/devstorage.read_write";
 
+  // Requests the deletion of the metadata of a job. This call returns when the
+  // job's metadata is deleted.
+  rpc DeleteJob(DeleteJobRequest) returns (google.protobuf.Empty) {
+    option (google.api.http) = {
+      delete: "/bigquery/v2/projects/{project_id=*}/jobs/{job_id=*}/delete"
+    };
+  }
+
   // Lists all jobs that you started in the specified project. Job information
   // is available for a six month period after creation. The job list is sorted
   // in reverse chronological order, by job creation time. Requires the Can View
@@ -63,6 +71,25 @@ message ListJobsRequest {
   string page_token = 6;
 }
 
+// Describes the format of a jobs deletion request.
+message DeleteJobRequest {
+  // Required. Project ID of the job for which metadata is to be deleted.
+  string project_id = 1 [(google.api.field_behavior) = REQUIRED];
+
+  // Required. Job ID of the job for which metadata is to be deleted. If this is
+  // a parent job which has child jobs, the metadata from all child jobs will be
+  // deleted as well. Direct deletion of the metadata of child jobs is not
+  // allowed.
+  string job_id = 2 [(google.api.field_behavior) = REQUIRED];
+
+  // The geographic location of the job. Required.
+  //
+  // For more information, see how to
+  // [specify
+  // locations](https://cloud.google.com/bigquery/docs/locations#specify_locations).
+  string location = 3;
+}
+
 // Simplified ListFormatJob for testing.
 message ListFormatJob {
   // Unique opaque ID of the job.
diff --git a/gapic-generator-java/src/test/resources/bigquery_v2.yaml b/gapic-generator-java/src/test/resources/bigquery_v2.yaml
deleted file mode 100644
index 7f974c6f84..0000000000
--- a/gapic-generator-java/src/test/resources/bigquery_v2.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-type: com.google.api.codegen.ConfigProto
-config_schema_version: 2.0.0
-language_settings:
-  java:
-    package_name: com.google.cloud.bigquery.v2
\ No newline at end of file