-
Notifications
You must be signed in to change notification settings - Fork 53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: Allow custom HttpRules for REST LROs #1288
Conversation
@@ -350,6 +351,9 @@ public class HttpJsonComplianceStub extends ComplianceStub { | |||
.build()) | |||
.build(); | |||
|
|||
private static final Map<String, String> operationCustomHttpBindings = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably don't want to create unused variables if we can avoid it in the generator.
@@ -72,8 +72,14 @@ | |||
public class HttpJsonOperationsStub extends OperationsStub { | |||
private static final Pattern CLIENT_PACKAGE_VERSION_PATTERN = | |||
Pattern.compile("\\.(?<version>v\\d+[a-zA-Z]*\\d*[a-zA-Z]*\\d*)\\.[\\w.]*stub"); | |||
|
|||
private static final ApiMethodDescriptor<ListOperationsRequest, ListOperationsResponse> | |||
private static final String LRO_LIST_OPERATIONS = "google.longrunning.Operations.ListOperations"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these all possible operations? Judging from the code in the composer, looks like there could be patch etc.?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are only 5 operations (and 4 of them have http annotations): https://github.com/googleapis/googleapis/blob/c8a382b9980b59a241a561aef1599980209a5e87/google/longrunning/operations.proto#L67-L122
} | ||
|
||
private String getValueBasedOnPatternCase(HttpRule httpRule) { | ||
switch (httpRule.getPatternCase().getNumber()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be good if we can provide a reference to the http proto for these mappings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll remember to add it in later. I'm not sure if this is the best approach yet, so I haven't been adding any docs or tests yet.
@@ -1103,6 +1143,43 @@ protected List<Expr> createOperationsStubInitExpr( | |||
.build()); | |||
} | |||
|
|||
private Map<String, String> parseCustomHttpBindings(GapicContext context) { | |||
Map<String, String> customHttpBindings = new HashMap<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can add all the default operation -> url
mapping to this map and override then if needed. Basically we need to move as much code as possible to the generator from gax, as gax is a runtime dependency and it would be much harder to make changes there later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can add the defaults for bunch of mixins (or for only Operation module). I just saw that it is possible to have way more mixins (https://github.com/googleapis/gapic-showcase/blob/b94ecfc51059b49770e5bdb6f0d7ea07903158e8/schema/google/showcase/v1beta1/showcase_v1beta1.yaml#L46-L85) so I didn't want to just create a default list in the gapic-generator that needed to be manually updated later on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, I think that makes sense, we could do it the other way also, construct a map with all the bindings from the yaml, and populate default for the LRO operations. Or if we are confident about the list of mixins, we could pre-populate them as well, a related question, did you get a chance to see how mixins work for location and iam? Are we going to have similar problem for them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From what I can see, I think only the Operation proto is currently being used. I would imagine if Location and IAM are used, they would be created the same way the Operations Client was in GAX: googleapis/gax-java#1456 and we would reference it via https://github.com/googleapis/google-cloud-java/blob/10bb0cb494f64b864408ede46834e1046351370c/java-speech/google-cloud-speech/src/main/java/com/google/cloud/speech/v1/SpeechClient.java#L164-L166.
I was running under the assumption that they would be added in sometime in the future, but I'm not sure about it. Plus each generated client (operation/ iam/ location) would have the initial defaults set from the proto file: https://github.com/googleapis/gapic-generator-java/blob/d1a16195937df041f65a52717ddf9dc6ceb09b4f/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/longrunning/stub/HttpJsonOperationsStub.java#L84
@@ -147,6 +147,12 @@ public Builder<RequestT> setAdditionalPaths(String... rawAdditionalPaths) { | |||
return this; | |||
} | |||
|
|||
@InternalApi | |||
public Builder<RequestT> updateRawPath(String rawPath) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to initialize the ProtoMessageRequestFormatter
with the updated path instead of overriding it later?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ProtoMessageRequestFormatter
is created as part of the ApiMethodDescriptor
static fields HttpJsonOperationStub: https://github.com/googleapis/gapic-generator-java/blob/b7ca95f12dfe8287c133e09534be1fc46882ce6c/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/longrunning/stub/HttpJsonOperationsStub.java#L76-L108
I might be missing something obvious, but I'm not seeing a way to change this value without updating once I get the customHttpBindings map.
} | ||
|
||
public static final HttpJsonOperationsStub create( | ||
OperationsStubSettings settings, Map<String, String> customOperationHttpBindings) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This approach is fine but I would try to explore the possibility of add the customOperationHttpBindings
to settings
or clientContext
. It would be cleaner and we don't have to have so many overloaded methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(From what I understand) The core of the logic which we can change in the generated library comes from:
protected HttpJsonSpeechStub(
SpeechStubSettings settings,
ClientContext clientContext,
HttpJsonStubCallableFactory callableFactory)
throws IOException {
this.callableFactory = callableFactory;
this.httpJsonOperationsStub =
HttpJsonOperationsStub.create(
clientContext,
callableFactory,
typeRegistry,
...
The SpeechStubSettings settings
parameter is different from OperationsStubSettings
which is what HttpJsonOperationsStub.create()
is expecting. The other approach I'm looking at is to try and add it either as part of the ClientContext (currently leaning towards not putting it here as it doesn't seem to be related) or the HttpJsonStubCallableFactory
(Need to do a bit more digging into this).
The overloaded methods were the easiest way I could get the CI builds to pass. Ideally, I would just have each of the .create()
methods take in another Map
parameter (and not to duplicate the .create()
methods), but I'm having issue with the build passing. I can show you this tomorrow.
…downstream test (#1291) * ci: use java-shared-dependencies in google-cloud-java * No need to modify google-cloud-jar-parent
…figurations for netty classes (#1290) * fix(java): initialize netty-shaded at run-time and add reflection configurations for netty classes
* ci(showcase): disable rest_numeric_enum feature in showcase tests
Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
Pin Bazel version to 5.2.0 as googleapis already updated to 5.2.0
Bumps [cryptography](https://github.com/pyca/cryptography) from 38.0.3 to 39.0.1. - [Release notes](https://github.com/pyca/cryptography/releases) - [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst) - [Commits](pyca/cryptography@38.0.3...39.0.1) --- updated-dependencies: - dependency-name: cryptography dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Mridula <66699525+mpeddada1@users.noreply.github.com>
* chore: (cleanup) removing unused files * chore: working on comments
…a-common-protos, and java-iam (#1305) The build is Maven or Bazel. The Gradle files in the recently migrated repositories (api-common-java, gax-java, java-common-protos, and java-iam) are not used. Note that this pull request is not touching rules_java_gapic/resources/gradle which is still used to generate Gradle files for self-service client libraries.
- selector: google.longrunning.Operations.ListOperations | ||
get: '/v1beta1/operations' | ||
additional_bindings: | ||
- get: '/v1/operations' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to have multiple additional bindings here? If yes, can we add more here to cover the test case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In addition, do you know how additional_bindings
are used in LROs or is it even used? If not, we probably can get rid of the duplicated logics.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, I'll add more bindings for this. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In addition, do you know how
additional_bindings
are used in LROs or is it even used? If not, we probably can get rid of the duplicated logics.
I've actually just been doing a bit of digging into as I've been running into some showcase issues with additional_bindings
. For httpjson, I don't think there currently is any logic to handles checking other bindings once the first one doesn't match: https://github.com/googleapis/gapic-generator-java/blob/c23f981e2ac3c573bed51e725dc7061551179400/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpRequestRunnable.java#L184
I don't see any logic that handles checking the other paths/ additional_bindings which I think is a bug. For grpc, I'll need to do some more digging. It doesn't seem like grpc uses the paths.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Filed a bug here: #1440
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Lawrence, that bug looks generic for all additional_bindings
, anything special for the OperationClient? Or as long as it's REST, there is no differences between OperationClient and other clients?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, the bug is generic for all additional_bindings
and it's a very specific edge case.
There is nothing special for OperationsClient, but I just added multiple additional_bindings
to the Operations Client for showcase.
import org.junit.BeforeClass; | ||
import org.junit.Test; | ||
|
||
public class HttpJsonOperationsStubTest { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test can probably use some refactoring. One best practice for sharing test object is that we can create it every time in setUp
and reference the object later, similar to this test file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I originally had an HttpJsonOperationStub object inside this test, but the method to get the MethodDescriptors is static: https://github.com/googleapis/gapic-generator-java/blob/0ccf457a774bd3dbe3b101d82222bdc39f6e5b0e/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/longrunning/stub/HttpJsonOperationsStub.java#L331 and I had to invoke it with HttpJsonOperationStub.getMethodDescriptors()
.
Is there a way to test this so that I can create an Stub object in the test and then reference it in my tests?
.getAdditionalPathTemplates() | ||
.get(0) | ||
.toRawString()) | ||
.isEqualTo("testList2"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Verifying a value that was set up in setUp
method is also not a good practice, see here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test tries to cover the updateDefaultApiMethodDescriptors
function. Perhaps the easier/ cleaner way for this is to just open method as default scope and test it that way?
@@ -366,7 +368,24 @@ protected HttpJsonEchoStub( | |||
throws IOException { | |||
this.callableFactory = callableFactory; | |||
this.httpJsonOperationsStub = | |||
HttpJsonOperationsStub.create(clientContext, callableFactory, typeRegistry); | |||
HttpJsonOperationsStub.create( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess the actual showcase integration tests that verified the interactions will be done in this PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Going forward, the interactions will be covered in that PR. For this PR, I just generated the speech client locally and tested that Operations works.
case 5: | ||
return httpRule.getDelete(); | ||
default: | ||
throw new IllegalArgumentException( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would probably write some true unit tests for this method and the method above, as the input and output are very easy to mock and verify, but I know they are mostly already covered in the golden unit tests, so as long as we covered all the cases, it's fine to me.
However, this branch couldn't be covered by golden tests, so we should add a unit test for it(by changing the scope of this method to default).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However, this branch couldn't be covered by golden tests, so we should add a unit test for it
I'm a bit lost of this part. The Echo goldens for rest should have it right?
https://github.com/googleapis/gapic-generator-java/blob/88487e38f86494e2ba18d4bf7c7f23c6671aeb24/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/rest/goldens/HttpJsonEchoStub.golden#L360-L405
Please retry analysis of this Pull-Request directly on SonarCloud. |
@@ -58,6 +60,13 @@ public GapicContext parseShowcaseEcho() { | |||
ServiceDescriptor echoServiceDescriptor = echoFileDescriptor.getServices().get(0); | |||
assertEquals("Echo", echoServiceDescriptor.getName()); | |||
|
|||
String serviceYamlFileName = "showcase_v1beta1.yaml"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I probably missed this in previous reviews, instead of showcase_v1beta1.yaml
, can we rename it to reflect the fact that this yaml is only used by Echo at this point?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, can rename to echo_v1beta.yaml
. We can rename it back to showcase_v1beta1.yaml
if other tests need to use this file again for other showcase protos.
.build(); | ||
/* This function returns the list of method descriptors (custom or default) */ | ||
@InternalApi | ||
public List<ApiMethodDescriptor> getAllMethodDescriptors() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can make remove the public
here since it's only used in tests, we should also make that clear in the comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MockHttpService requires this in OperationsClientTest. I'll add @VisibleForTesting
annotation here to mark its intended use case.
if (apiVersion == null) { | ||
return methodDescriptor; | ||
/* OperationsClient's RPCs are mapped to GET/POST/DELETE and this function only expects those HttpVerbs to be used */ | ||
private static String getValueBasedOnPatternCase(HttpRule httpRule) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method does not have to be static
now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
List<ApiMethodDescriptor> apiMethodDescriptorList = | ||
httpJsonOperationsStub.getAllMethodDescriptors(); | ||
assertThat(apiMethodDescriptorList.get(0).getRequestFormatter().getPathTemplate().toRawString()) | ||
.isEqualTo("testList"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could extract this string to a variable in the method or static field in the test class, since they are reused in the setup and verification. Same thing for the test below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Kudos, SonarCloud Quality Gate passed! |
Fixes: #1279
This is both a gapic-generator and gax-java change:
For example in Java-Speech generated client:
Output: