From 5fd79ea433d74d27d6115d91e2e0947e7b247b5d Mon Sep 17 00:00:00 2001 From: Blake Li Date: Tue, 25 Apr 2023 08:57:06 -0600 Subject: [PATCH] fix: Unescape Java keyword field names when generating HttpJson unit tests. (#1654) --- .../api/generator/engine/lexicon/Keyword.java | 17 +++++ .../defaultvalue/DefaultValueComposer.java | 2 + .../api/generator/gapic/utils/JavaStyle.java | 2 +- .../generator/engine/lexicon/KeywordTest.java | 30 ++++++++ .../grpcrest/goldens/EchoClient.golden | 75 +++++++++++++++++++ .../goldens/EchoClientHttpJsonTest.golden | 59 +++++++++++++++ .../grpcrest/goldens/EchoClientTest.golden | 44 +++++++++++ .../grpcrest/goldens/EchoSettings.golden | 10 +++ .../grpcrest/goldens/EchoStubSettings.golden | 28 ++++++- .../grpcrest/goldens/GrpcEchoStub.golden | 29 +++++++ .../grpcrest/goldens/HttpJsonEchoStub.golden | 53 +++++++++++++ .../src/test/proto/echo_grpcrest.proto | 36 +++++++++ 12 files changed, 382 insertions(+), 3 deletions(-) diff --git a/gapic-generator-java/src/main/java/com/google/api/generator/engine/lexicon/Keyword.java b/gapic-generator-java/src/main/java/com/google/api/generator/engine/lexicon/Keyword.java index 765fe2eaf3..9e40e54027 100644 --- a/gapic-generator-java/src/main/java/com/google/api/generator/engine/lexicon/Keyword.java +++ b/gapic-generator-java/src/main/java/com/google/api/generator/engine/lexicon/Keyword.java @@ -14,6 +14,7 @@ package com.google.api.generator.engine.lexicon; +import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; public class Keyword { @@ -71,11 +72,27 @@ public class Keyword { "native", "super", "while"); + private static final String ESCAPE_CHAR = "_"; public static boolean isKeyword(String s) { return s.equals(CLASS_KEYWORD) || KEYWORDS.contains(s); } + public static String unescapeKeyword(String str) { + if (Strings.isNullOrEmpty(str)) { + return str; + } + if (!str.endsWith(ESCAPE_CHAR)) { + return str; + } + String strWithoutEscapeChar = str.substring(0, str.lastIndexOf(ESCAPE_CHAR)); + return isKeyword(strWithoutEscapeChar) ? strWithoutEscapeChar : str; + } + + public static String escapeKeyword(String str) { + return Keyword.isKeyword(str) ? str + ESCAPE_CHAR : str; + } + public static boolean isInvalidFieldName(String s) { return KEYWORDS.contains(s); } diff --git a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/defaultvalue/DefaultValueComposer.java b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/defaultvalue/DefaultValueComposer.java index 54fc1dad57..8a3bfc4fdd 100644 --- a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/defaultvalue/DefaultValueComposer.java +++ b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/defaultvalue/DefaultValueComposer.java @@ -30,6 +30,7 @@ import com.google.api.generator.engine.ast.VaporReference; import com.google.api.generator.engine.ast.Variable; import com.google.api.generator.engine.ast.VariableExpr; +import com.google.api.generator.engine.lexicon.Keyword; import com.google.api.generator.gapic.composer.resourcename.ResourceNameTokenizer; import com.google.api.generator.gapic.model.Field; import com.google.api.generator.gapic.model.HttpBindings; @@ -149,6 +150,7 @@ public static Expr createValue( Map nestedValuePatterns = new HashMap<>(); for (Map.Entry entry : valuePatterns.entrySet()) { String lowerCamelNestedFieldName = JavaStyle.toLowerCamelCase(nestedFieldName); + lowerCamelNestedFieldName = Keyword.unescapeKeyword(lowerCamelNestedFieldName); if (entry.getKey().startsWith(lowerCamelNestedFieldName + '.')) { nestedValuePatterns.put( entry.getKey().substring(lowerCamelNestedFieldName.length() + 1), entry.getValue()); diff --git a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/utils/JavaStyle.java b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/utils/JavaStyle.java index d99777bcd2..5c1be0aff8 100644 --- a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/utils/JavaStyle.java +++ b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/utils/JavaStyle.java @@ -39,7 +39,7 @@ public static String toLowerCamelCase(String s) { // Some APIs use legit java keywords as method names. Both protobuf and gGRPC add an underscore // in generated stubs to resolve name conflict, so we need to do the same. - return Keyword.isKeyword(name) ? name + '_' : name; + return Keyword.escapeKeyword(name); } public static String toUpperCamelCase(String s) { diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/engine/lexicon/KeywordTest.java b/gapic-generator-java/src/test/java/com/google/api/generator/engine/lexicon/KeywordTest.java index 17d51461ed..ef5d87e587 100644 --- a/gapic-generator-java/src/test/java/com/google/api/generator/engine/lexicon/KeywordTest.java +++ b/gapic-generator-java/src/test/java/com/google/api/generator/engine/lexicon/KeywordTest.java @@ -45,4 +45,34 @@ public void keywordDetected() { assertThat(Keyword.isKeyword("class")).isTrue(); assertThat(Keyword.isInvalidFieldName("class")).isFalse(); } + + @Test + public void unescapedKeyword_shouldReturnItselfIfEmpty() { + assertThat(Keyword.unescapeKeyword("")).isEqualTo(""); + } + + @Test + public void unescapedKeyword_shouldReturnItselfIfDoesNotEndWithEscapeChar() { + assertThat(Keyword.unescapeKeyword("hello")).isEqualTo("hello"); + } + + @Test + public void unescapedKeyword_shouldReturnItselfIfEndsWithEscapeCharButNotAKeyword() { + assertThat(Keyword.unescapeKeyword("important_")).isEqualTo("important_"); + } + + @Test + public void unescapedKeyword_shouldUnescapeIfEndsWithEscapeCharAndAKeyword() { + assertThat(Keyword.unescapeKeyword("import_")).isEqualTo("import"); + } + + @Test + public void escapeKeyword_shouldEscapeIfIsAKeyword() { + assertThat(Keyword.escapeKeyword("final")).isEqualTo("final_"); + } + + @Test + public void escapeKeyword_shouldNotEscapeIfIsNotAKeyword() { + assertThat(Keyword.escapeKeyword("fantasy")).isEqualTo("fantasy"); + } } diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/goldens/EchoClient.golden b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/goldens/EchoClient.golden index 15d3a77a29..334dacf40b 100644 --- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/goldens/EchoClient.golden +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/goldens/EchoClient.golden @@ -18,6 +18,7 @@ import com.google.api.resourcenames.ResourceName; import com.google.common.util.concurrent.MoreExecutors; import com.google.longrunning.Operation; import com.google.protobuf.Duration; +import com.google.protobuf.FieldMask; import com.google.protobuf.Timestamp; import com.google.rpc.Status; import com.google.showcase.grpcrest.v1beta1.stub.EchoStub; @@ -1045,6 +1046,80 @@ public class EchoClient implements BackgroundResource { return stub.noBindingCallable(); } + // 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()) {
+   *   Case case_ = Case.newBuilder().build();
+   *   FieldMask updateMask = FieldMask.newBuilder().build();
+   *   Case response = echoClient.updateCase(case_, updateMask);
+   * }
+   * }
+ * + * @param case_ + * @param updateMask + * @throws com.google.api.gax.rpc.ApiException if the remote call fails + */ + public final Case updateCase(Case case_, FieldMask updateMask) { + UpdateCaseRequest request = + UpdateCaseRequest.newBuilder().setCase(case_).setUpdateMask(updateMask).build(); + return updateCase(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()) {
+   *   UpdateCaseRequest request =
+   *       UpdateCaseRequest.newBuilder().setCase(Case.newBuilder().build()).build();
+   *   Case response = echoClient.updateCase(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 Case updateCase(UpdateCaseRequest request) { + return updateCaseCallable().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()) {
+   *   UpdateCaseRequest request =
+   *       UpdateCaseRequest.newBuilder().setCase(Case.newBuilder().build()).build();
+   *   ApiFuture future = echoClient.updateCaseCallable().futureCall(request);
+   *   // Do something.
+   *   Case response = future.get();
+   * }
+   * }
+ */ + public final UnaryCallable updateCaseCallable() { + return stub.updateCaseCallable(); + } + @Override public final void close() { stub.close(); diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/goldens/EchoClientHttpJsonTest.golden b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/goldens/EchoClientHttpJsonTest.golden index e3a030ccd9..a26bd912a8 100644 --- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/goldens/EchoClientHttpJsonTest.golden +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/goldens/EchoClientHttpJsonTest.golden @@ -17,6 +17,7 @@ import com.google.common.collect.Lists; import com.google.longrunning.Operation; import com.google.protobuf.Any; import com.google.protobuf.Duration; +import com.google.protobuf.FieldMask; import com.google.protobuf.Timestamp; import com.google.protobuf.Value; import com.google.rpc.Status; @@ -826,4 +827,62 @@ public class EchoClientHttpJsonTest { // The noBinding() method is not supported in REST transport. // This empty test is generated for technical reasons. } + + @Test + public void updateCaseTest() throws Exception { + Case expectedResponse = + Case.newBuilder() + .setName("name3373707") + .setDisplayName("displayName1714148973") + .setDescription("description-1724546052") + .build(); + mockService.addResponse(expectedResponse); + + Case case_ = + Case.newBuilder() + .setName("projects/project-3807/cases/case-3807") + .setDisplayName("displayName1714148973") + .setDescription("description-1724546052") + .build(); + FieldMask updateMask = FieldMask.newBuilder().build(); + + Case actualResponse = client.updateCase(case_, updateMask); + Assert.assertEquals(expectedResponse, actualResponse); + + List actualRequests = mockService.getRequestPaths(); + Assert.assertEquals(1, actualRequests.size()); + + String apiClientHeaderKey = + mockService + .getRequestHeaders() + .get(ApiClientHeaderProvider.getDefaultApiClientHeaderKey()) + .iterator() + .next(); + Assert.assertTrue( + GaxHttpJsonProperties.getDefaultApiClientHeaderPattern() + .matcher(apiClientHeaderKey) + .matches()); + } + + @Test + public void updateCaseExceptionTest() throws Exception { + ApiException exception = + ApiExceptionFactory.createException( + new Exception(), FakeStatusCode.of(StatusCode.Code.INVALID_ARGUMENT), false); + mockService.addException(exception); + + try { + Case case_ = + Case.newBuilder() + .setName("projects/project-3807/cases/case-3807") + .setDisplayName("displayName1714148973") + .setDescription("description-1724546052") + .build(); + FieldMask updateMask = FieldMask.newBuilder().build(); + client.updateCase(case_, updateMask); + 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/grpcrest/goldens/EchoClientTest.golden b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/goldens/EchoClientTest.golden index aed3486918..1d87f4584f 100644 --- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/goldens/EchoClientTest.golden +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/goldens/EchoClientTest.golden @@ -21,6 +21,7 @@ import com.google.longrunning.Operation; import com.google.protobuf.AbstractMessage; import com.google.protobuf.Any; import com.google.protobuf.Duration; +import com.google.protobuf.FieldMask; import com.google.protobuf.Timestamp; import com.google.protobuf.Value; import com.google.rpc.Status; @@ -900,4 +901,47 @@ public class EchoClientTest { // Expected exception. } } + + @Test + public void updateCaseTest() throws Exception { + Case expectedResponse = + Case.newBuilder() + .setName("name3373707") + .setDisplayName("displayName1714148973") + .setDescription("description-1724546052") + .build(); + mockEcho.addResponse(expectedResponse); + + Case case_ = Case.newBuilder().build(); + FieldMask updateMask = FieldMask.newBuilder().build(); + + Case actualResponse = client.updateCase(case_, updateMask); + Assert.assertEquals(expectedResponse, actualResponse); + + List actualRequests = mockEcho.getRequests(); + Assert.assertEquals(1, actualRequests.size()); + UpdateCaseRequest actualRequest = ((UpdateCaseRequest) actualRequests.get(0)); + + Assert.assertEquals(case_, actualRequest.getCase()); + Assert.assertEquals(updateMask, actualRequest.getUpdateMask()); + Assert.assertTrue( + channelProvider.isHeaderSent( + ApiClientHeaderProvider.getDefaultApiClientHeaderKey(), + GaxGrpcProperties.getDefaultApiClientHeaderPattern())); + } + + @Test + public void updateCaseExceptionTest() throws Exception { + StatusRuntimeException exception = new StatusRuntimeException(io.grpc.Status.INVALID_ARGUMENT); + mockEcho.addException(exception); + + try { + Case case_ = Case.newBuilder().build(); + FieldMask updateMask = FieldMask.newBuilder().build(); + client.updateCase(case_, updateMask); + 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/grpcrest/goldens/EchoSettings.golden b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/goldens/EchoSettings.golden index 9c51589512..dbc1892fc3 100644 --- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/goldens/EchoSettings.golden +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/goldens/EchoSettings.golden @@ -122,6 +122,11 @@ public class EchoSettings extends ClientSettings { return ((EchoStubSettings) getStubSettings()).noBindingSettings(); } + /** Returns the object with the settings used for calls to updateCase. */ + public UnaryCallSettings updateCaseSettings() { + return ((EchoStubSettings) getStubSettings()).updateCaseSettings(); + } + public static final EchoSettings create(EchoStubSettings stub) throws IOException { return new EchoSettings.Builder(stub.toBuilder()).build(); } @@ -296,6 +301,11 @@ public class EchoSettings extends ClientSettings { return getStubSettingsBuilder().noBindingSettings(); } + /** Returns the builder for the settings used for calls to updateCase. */ + public UnaryCallSettings.Builder updateCaseSettings() { + return getStubSettingsBuilder().updateCaseSettings(); + } + @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/grpcrest/goldens/EchoStubSettings.golden b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/goldens/EchoStubSettings.golden index bc6b181db1..05781309f0 100644 --- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/goldens/EchoStubSettings.golden +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/goldens/EchoStubSettings.golden @@ -41,12 +41,14 @@ import com.google.common.collect.Lists; import com.google.longrunning.Operation; import com.google.showcase.grpcrest.v1beta1.BlockRequest; import com.google.showcase.grpcrest.v1beta1.BlockResponse; +import com.google.showcase.grpcrest.v1beta1.Case; import com.google.showcase.grpcrest.v1beta1.EchoRequest; import com.google.showcase.grpcrest.v1beta1.EchoResponse; import com.google.showcase.grpcrest.v1beta1.ExpandRequest; import com.google.showcase.grpcrest.v1beta1.Object; import com.google.showcase.grpcrest.v1beta1.PagedExpandRequest; import com.google.showcase.grpcrest.v1beta1.PagedExpandResponse; +import com.google.showcase.grpcrest.v1beta1.UpdateCaseRequest; import com.google.showcase.grpcrest.v1beta1.WaitMetadata; import com.google.showcase.grpcrest.v1beta1.WaitRequest; import com.google.showcase.grpcrest.v1beta1.WaitResponse; @@ -113,6 +115,7 @@ public class EchoStubSettings extends StubSettings { private final UnaryCallSettings nestedBindingSettings; private final StreamingCallSettings chatSettings; private final UnaryCallSettings noBindingSettings; + private final UnaryCallSettings updateCaseSettings; private static final PagedListDescriptor PAGED_EXPAND_PAGE_STR_DESC = @@ -277,6 +280,11 @@ public class EchoStubSettings extends StubSettings { return noBindingSettings; } + /** Returns the object with the settings used for calls to updateCase. */ + public UnaryCallSettings updateCaseSettings() { + return updateCaseSettings; + } + public EchoStub createStub() throws IOException { if (getTransportChannelProvider() .getTransportName() @@ -392,6 +400,7 @@ public class EchoStubSettings extends StubSettings { nestedBindingSettings = settingsBuilder.nestedBindingSettings().build(); chatSettings = settingsBuilder.chatSettings().build(); noBindingSettings = settingsBuilder.noBindingSettings().build(); + updateCaseSettings = settingsBuilder.updateCaseSettings().build(); } /** Builder for EchoStubSettings. */ @@ -413,6 +422,7 @@ public class EchoStubSettings extends StubSettings { private final UnaryCallSettings.Builder nestedBindingSettings; private final StreamingCallSettings.Builder chatSettings; private final UnaryCallSettings.Builder noBindingSettings; + private final UnaryCallSettings.Builder updateCaseSettings; private static final ImmutableMap> RETRYABLE_CODE_DEFINITIONS; @@ -451,6 +461,7 @@ public class EchoStubSettings extends StubSettings { nestedBindingSettings = UnaryCallSettings.newUnaryCallSettingsBuilder(); chatSettings = StreamingCallSettings.newBuilder(); noBindingSettings = UnaryCallSettings.newUnaryCallSettingsBuilder(); + updateCaseSettings = UnaryCallSettings.newUnaryCallSettingsBuilder(); unaryMethodSettingsBuilders = ImmutableList.>of( @@ -461,7 +472,8 @@ public class EchoStubSettings extends StubSettings { blockSettings, collideNameSettings, nestedBindingSettings, - noBindingSettings); + noBindingSettings, + updateCaseSettings); initDefaults(this); } @@ -479,6 +491,7 @@ public class EchoStubSettings extends StubSettings { nestedBindingSettings = settings.nestedBindingSettings.toBuilder(); chatSettings = settings.chatSettings.toBuilder(); noBindingSettings = settings.noBindingSettings.toBuilder(); + updateCaseSettings = settings.updateCaseSettings.toBuilder(); unaryMethodSettingsBuilders = ImmutableList.>of( @@ -489,7 +502,8 @@ public class EchoStubSettings extends StubSettings { blockSettings, collideNameSettings, nestedBindingSettings, - noBindingSettings); + noBindingSettings, + updateCaseSettings); } private static Builder createDefault() { @@ -564,6 +578,11 @@ public class EchoStubSettings extends StubSettings { .setRetryableCodes(RETRYABLE_CODE_DEFINITIONS.get("no_retry_codes")) .setRetrySettings(RETRY_PARAM_DEFINITIONS.get("no_retry_params")); + builder + .updateCaseSettings() + .setRetryableCodes(RETRYABLE_CODE_DEFINITIONS.get("no_retry_codes")) + .setRetrySettings(RETRY_PARAM_DEFINITIONS.get("no_retry_params")); + builder .waitOperationSettings() .setInitialCallSettings( @@ -667,6 +686,11 @@ public class EchoStubSettings extends StubSettings { return noBindingSettings; } + /** Returns the builder for the settings used for calls to updateCase. */ + public UnaryCallSettings.Builder updateCaseSettings() { + return updateCaseSettings; + } + @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/grpcrest/goldens/GrpcEchoStub.golden b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/goldens/GrpcEchoStub.golden index 8cf20fee1f..c500e10c31 100644 --- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/goldens/GrpcEchoStub.golden +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/goldens/GrpcEchoStub.golden @@ -18,12 +18,14 @@ import com.google.longrunning.Operation; import com.google.longrunning.stub.GrpcOperationsStub; import com.google.showcase.grpcrest.v1beta1.BlockRequest; import com.google.showcase.grpcrest.v1beta1.BlockResponse; +import com.google.showcase.grpcrest.v1beta1.Case; import com.google.showcase.grpcrest.v1beta1.EchoRequest; import com.google.showcase.grpcrest.v1beta1.EchoResponse; import com.google.showcase.grpcrest.v1beta1.ExpandRequest; import com.google.showcase.grpcrest.v1beta1.Object; import com.google.showcase.grpcrest.v1beta1.PagedExpandRequest; import com.google.showcase.grpcrest.v1beta1.PagedExpandResponse; +import com.google.showcase.grpcrest.v1beta1.UpdateCaseRequest; import com.google.showcase.grpcrest.v1beta1.WaitMetadata; import com.google.showcase.grpcrest.v1beta1.WaitRequest; import com.google.showcase.grpcrest.v1beta1.WaitResponse; @@ -127,6 +129,14 @@ public class GrpcEchoStub extends EchoStub { .setResponseMarshaller(ProtoUtils.marshaller(EchoResponse.getDefaultInstance())) .build(); + private static final MethodDescriptor updateCaseMethodDescriptor = + MethodDescriptor.newBuilder() + .setType(MethodDescriptor.MethodType.UNARY) + .setFullMethodName("google.showcase.grpcrest.v1beta1.Echo/UpdateCase") + .setRequestMarshaller(ProtoUtils.marshaller(UpdateCaseRequest.getDefaultInstance())) + .setResponseMarshaller(ProtoUtils.marshaller(Case.getDefaultInstance())) + .build(); + private final UnaryCallable echoCallable; private final ServerStreamingCallable expandCallable; private final UnaryCallable pagedExpandCallable; @@ -142,6 +152,7 @@ public class GrpcEchoStub extends EchoStub { private final UnaryCallable nestedBindingCallable; private final BidiStreamingCallable chatCallable; private final UnaryCallable noBindingCallable; + private final UnaryCallable updateCaseCallable; private final BackgroundResource backgroundResources; private final GrpcOperationsStub operationsStub; @@ -227,6 +238,16 @@ public class GrpcEchoStub extends EchoStub { GrpcCallSettings.newBuilder() .setMethodDescriptor(noBindingMethodDescriptor) .build(); + GrpcCallSettings updateCaseTransportSettings = + GrpcCallSettings.newBuilder() + .setMethodDescriptor(updateCaseMethodDescriptor) + .setParamsExtractor( + request -> { + ImmutableMap.Builder params = ImmutableMap.builder(); + params.put("case.name", String.valueOf(request.getCase().getName())); + return params.build(); + }) + .build(); this.echoCallable = callableFactory.createUnaryCallable( @@ -271,6 +292,9 @@ public class GrpcEchoStub extends EchoStub { this.noBindingCallable = callableFactory.createUnaryCallable( noBindingTransportSettings, settings.noBindingSettings(), clientContext); + this.updateCaseCallable = + callableFactory.createUnaryCallable( + updateCaseTransportSettings, settings.updateCaseSettings(), clientContext); this.backgroundResources = new BackgroundResourceAggregation(clientContext.getBackgroundResources()); @@ -346,6 +370,11 @@ public class GrpcEchoStub extends EchoStub { return noBindingCallable; } + @Override + public UnaryCallable updateCaseCallable() { + return updateCaseCallable; + } + @Override public final void close() { try { diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/goldens/HttpJsonEchoStub.golden b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/goldens/HttpJsonEchoStub.golden index f0323ff116..909b6cfdba 100644 --- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/goldens/HttpJsonEchoStub.golden +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/goldens/HttpJsonEchoStub.golden @@ -27,12 +27,14 @@ import com.google.longrunning.Operation; import com.google.protobuf.TypeRegistry; import com.google.showcase.grpcrest.v1beta1.BlockRequest; import com.google.showcase.grpcrest.v1beta1.BlockResponse; +import com.google.showcase.grpcrest.v1beta1.Case; import com.google.showcase.grpcrest.v1beta1.EchoRequest; import com.google.showcase.grpcrest.v1beta1.EchoResponse; import com.google.showcase.grpcrest.v1beta1.ExpandRequest; import com.google.showcase.grpcrest.v1beta1.Object; import com.google.showcase.grpcrest.v1beta1.PagedExpandRequest; import com.google.showcase.grpcrest.v1beta1.PagedExpandResponse; +import com.google.showcase.grpcrest.v1beta1.UpdateCaseRequest; import com.google.showcase.grpcrest.v1beta1.WaitMetadata; import com.google.showcase.grpcrest.v1beta1.WaitRequest; import com.google.showcase.grpcrest.v1beta1.WaitResponse; @@ -332,6 +334,42 @@ public class HttpJsonEchoStub extends EchoStub { .build()) .build(); + private static final ApiMethodDescriptor updateCaseMethodDescriptor = + ApiMethodDescriptor.newBuilder() + .setFullMethodName("google.showcase.grpcrest.v1beta1.Echo/UpdateCase") + .setHttpMethod("PATCH") + .setType(ApiMethodDescriptor.MethodType.UNARY) + .setRequestFormatter( + ProtoMessageRequestFormatter.newBuilder() + .setPath( + "/v2beta/{case.name=projects/*/cases/*}", + request -> { + Map fields = new HashMap<>(); + ProtoRestSerializer serializer = + ProtoRestSerializer.create(); + serializer.putPathParam(fields, "case.name", request.getCase().getName()); + return fields; + }) + .setAdditionalPaths("/v2beta/{case.name=organizations/*/cases/*}") + .setQueryParamsExtractor( + request -> { + Map> fields = new HashMap<>(); + ProtoRestSerializer serializer = + ProtoRestSerializer.create(); + serializer.putQueryParam(fields, "updateMask", request.getUpdateMask()); + return fields; + }) + .setRequestBodyExtractor( + request -> + ProtoRestSerializer.create().toBody("case_", request.getCase(), false)) + .build()) + .setResponseParser( + ProtoMessageResponseParser.newBuilder() + .setDefaultInstance(Case.getDefaultInstance()) + .setDefaultTypeRegistry(typeRegistry) + .build()) + .build(); + private final UnaryCallable echoCallable; private final ServerStreamingCallable expandCallable; private final UnaryCallable pagedExpandCallable; @@ -345,6 +383,7 @@ public class HttpJsonEchoStub extends EchoStub { private final UnaryCallable blockCallable; private final UnaryCallable collideNameCallable; private final UnaryCallable nestedBindingCallable; + private final UnaryCallable updateCaseCallable; private final BackgroundResource backgroundResources; private final HttpJsonOperationsStub httpJsonOperationsStub; @@ -476,6 +515,11 @@ public class HttpJsonEchoStub extends EchoStub { .setMethodDescriptor(nestedBindingMethodDescriptor) .setTypeRegistry(typeRegistry) .build(); + HttpJsonCallSettings updateCaseTransportSettings = + HttpJsonCallSettings.newBuilder() + .setMethodDescriptor(updateCaseMethodDescriptor) + .setTypeRegistry(typeRegistry) + .build(); this.echoCallable = callableFactory.createUnaryCallable( @@ -517,6 +561,9 @@ public class HttpJsonEchoStub extends EchoStub { this.nestedBindingCallable = callableFactory.createUnaryCallable( nestedBindingTransportSettings, settings.nestedBindingSettings(), clientContext); + this.updateCaseCallable = + callableFactory.createUnaryCallable( + updateCaseTransportSettings, settings.updateCaseSettings(), clientContext); this.backgroundResources = new BackgroundResourceAggregation(clientContext.getBackgroundResources()); @@ -533,6 +580,7 @@ public class HttpJsonEchoStub extends EchoStub { methodDescriptors.add(blockMethodDescriptor); methodDescriptors.add(collideNameMethodDescriptor); methodDescriptors.add(nestedBindingMethodDescriptor); + methodDescriptors.add(updateCaseMethodDescriptor); return methodDescriptors; } @@ -596,6 +644,11 @@ public class HttpJsonEchoStub extends EchoStub { return nestedBindingCallable; } + @Override + public UnaryCallable updateCaseCallable() { + return updateCaseCallable; + } + @Override public BidiStreamingCallable chatCallable() { throw new UnsupportedOperationException( diff --git a/gapic-generator-java/src/test/proto/echo_grpcrest.proto b/gapic-generator-java/src/test/proto/echo_grpcrest.proto index 61efb8fd65..fbab0c2553 100644 --- a/gapic-generator-java/src/test/proto/echo_grpcrest.proto +++ b/gapic-generator-java/src/test/proto/echo_grpcrest.proto @@ -23,6 +23,7 @@ import "google/protobuf/duration.proto"; import "google/protobuf/struct.proto"; import "google/protobuf/timestamp.proto"; import "google/rpc/status.proto"; +import "google/protobuf/field_mask.proto"; package google.showcase.grpcrest.v1beta1; @@ -145,6 +146,20 @@ service Echo { } rpc NoBinding(EchoRequest) returns (EchoResponse); + + //The request message contains a field name that is a Java keyword(case), + //the field is also a resource and used in http annotations + rpc UpdateCase(UpdateCaseRequest) returns (Case) { + option (google.api.http) = { + patch: "/v2beta/{case.name=projects/*/cases/*}" + body: "case" + additional_bindings { + patch: "/v2beta/{case.name=organizations/*/cases/*}" + body: "case" + } + }; + option (google.api.method_signature) = "case,update_mask"; + } } // Generator should not fail when encounter a service without methods @@ -160,6 +175,27 @@ enum Severity { CRITICAL = 3; } +message UpdateCaseRequest { + Case case = 1 [(google.api.field_behavior) = REQUIRED]; + + google.protobuf.FieldMask update_mask = 2; +} + +message Case { + option (google.api.resource) = { + type: "cloudsupport.googleapis.com/Case" + pattern: "organizations/{organization}/cases/{case}" + pattern: "projects/{project}/cases/{case}" + }; + + // The resource name for the case. + string name = 1; + + string display_name = 2; + + string description = 3; +} + message Foobar { option (google.api.resource) = { type: "showcase.googleapis.com/Foobar"