Skip to content
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

feat: Update Gapic generator and Gax to emit api-versioning via header #2671

Merged
merged 9 commits into from
Apr 26, 2024

Conversation

zhumin8
Copy link
Contributor

@zhumin8 zhumin8 commented Apr 19, 2024

This is part 2, following changes in #2630

Changes in this pr:

  • ApiClientHeaderProvider: add key and token setter for api version.
  • AbstractServiceStubSettingsClassComposer: add logic to set this header when api-version is present (not null or empty)
  • Unit and golden tests to reflect above changes. (This includeds a few new golden files created for tests based on the dedicated proto created in feat: Parser to consume the api-versioning value from proto #2630)

This pr does not include gapic-showcased testing for now. Showcase test draft: #2737


manual tested with gapic-showcase v0.33.0

steps:

  • Use gapic-showcase v0.33.0 (which includes update in 1484)
  • use gapic-showcase run -v which prints the request header
  • From showcase folder, run mvn verify -P enable-integration-tests -Dit.test=ITAutoPopulatedFields.java -pl=gapic-showcase

Printed header in manual testing
Grpc

2024/04/16 10:26:41 Received Unary Request for Method: /google.showcase.v1beta1.Echo/Echo
2024/04/16 10:26:41     Request headers:
2024/04/16 10:26:41       grpc-accept-encoding: gzip
2024/04/16 10:26:41       content-type: application/grpc
2024/04/16 10:26:41       x-goog-api-version: v1_20240408
2024/04/16 10:26:41       :authority: localhost:7469
2024/04/16 10:26:41       user-agent: grpc-java-netty/1.62.2
2024/04/16 10:26:41       x-goog-api-client: gl-java/17.0.7__Eclipse-Adoptium__Temurin-17.0.7-7 gapic/0.0.1-SNAPSHOT gax/2.46.2-SNAPSHOT grpc/1.62.2
2024/04/16 10:26:41     Request:  error:{code:2}  request_id:"1e26ee16-ed07-4eeb-be0e-49b769678779"  other_request_id:"40883e5d-3b20-4add-854c-9808bd009260"
2024/04/16 10:26:41     Returning Error: rpc error: code = Unknown desc = 

Httpjson

2024/04/16 10:26:41 Received POST request matching '/v1beta1/echo:echo': "/v1beta1/echo:echo"                                                               
2024/04/16 10:26:41   urlPathParams (expect 0, have 0): map[]                 
2024/04/16 10:26:41   urlRequestHeaders:                                                                                                                    
    User-Agent: "Google-HTTP-Java-Client/1.44.1 (gzip)"                                                                                                     
    Content-Type: "application/json; charset=utf-8"                           
    Connection: "keep-alive"                                                                                                                                
    Accept-Encoding: "gzip"                                                   
    X-Goog-Api-Client: "gl-java/17.0.7__Eclipse-Adoptium__Temurin-17.0.7-7 gapic/0.0.1-SNAPSHOT gax/2.46.2-SNAPSHOT rest/"                                  
    X-Goog-Api-Version: "v1_20240408"                                                                                                                       
    Accept: "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2"            
    Content-Length: "129"                                                     
2024/04/16 10:26:41   request: {                                                                                                                            
  "error":  {                                                                                                                                               
    "code":  500,                                                                                                                                           
    "message":  "",                                                                                                                                         
    "details":  []                                                            
  },                                                                                                                                                        
  "severity":  "UNNECESSARY",                                                                                                                               
  "header":  "",                                                                                                                                            
  "otherHeader":  "",                                                         
  "requestId":  "e690a84b-176d-421e-9e5d-ba752f68fec8",                       
  "otherRequestId":  "47e0fbde-056a-43ae-bba6-8b6770d861f7"                   
}    

@product-auto-label product-auto-label bot added the size: l Pull request size is large. label Apr 19, 2024
@zhumin8
Copy link
Contributor Author

zhumin8 commented Apr 19, 2024

@lqiu96 As we talked off-line, I am still working on some details, plus integration testing. But feel free to take a first look.
(Build and tests are expected to fail for now, because changes in #2630 which this pr depends on is not included. I will rebase and fix when #2630 goes in.)

@zhumin8 zhumin8 requested a review from lqiu96 April 19, 2024 17:24
@@ -68,6 +70,9 @@ protected ApiClientHeaderProvider(Builder builder) {
headersBuilder.put(QUOTA_PROJECT_ID_HEADER_KEY, builder.getQuotaProjectIdToken());
}

if (builder.getApiVersionToken() != null) {
Copy link
Contributor

@lqiu96 lqiu96 Apr 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be Token != null && token != "".

The constructor sets it to null, but there is a public setter so it can be set to "". IIRC, we should treat both as null.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not exactly sure I am following your thoughts here.
If you are talking about the public setter in the Builder, then I think we are safe here. This constructor is protected and is meant to enforce construction through the builder.

or is this what you have in mind?

ApiClientHeaderProvider emptyProvider = ApiClientHeaderProvider.newBuilder().setApiVersionToken("")
        .build();

I don't think it's this HeaderProvider's responsibility to make any judgement on the header values. This logic belongs to AbstractServiceStubSettingsClassComposer.java via service.hasApiVersion(). I'll add in relevant tests to demonstrate.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's this HeaderProvider's responsibility to make any judgement on the header values. This logic belongs to AbstractServiceStubSettingsClassComposer.java via service.hasApiVersion(). I'll add in relevant tests to demonstrate.

The composer only checks the api versionvalue from the proto file. This case is for any runtime configurations that the user adds to the client library. We currently treat any service configuration where api_version of "" as null and I am currently thinking that it would make sense for the same behavior here.

IIUC, ClientContext does make judgement on the header values and does some validation:

Set<String> conflicts = Sets.intersection(userHeaders.keySet(), internalHeaders.keySet());
for (String key : conflicts) {
if ("user-agent".equals(key)) {
conflictResolution.put(key, userHeaders.get(key) + " " + internalHeaders.get(key));
continue;
}
// Backwards compat: quota project id can conflict if its overriden in settings
if (QUOTA_PROJECT_ID_HEADER_KEY.equals(key) && settings.getQuotaProjectId() != null) {
continue;
}
throw new IllegalArgumentException("Header provider can't override the header: " + key);
}
. I was thinking that it should be enforced, but that isn't required from this PR.

Copy link
Contributor Author

@zhumin8 zhumin8 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Working through testing changes after separated out api_version_testing.proto. Will update once ready.

@product-auto-label product-auto-label bot added size: xl Pull request size is extra large. and removed size: l Pull request size is large. labels Apr 24, 2024
@zhumin8
Copy link
Contributor Author

zhumin8 commented Apr 24, 2024

Force pushed after rebase with main + mainly test updates.

Comment on lines 61 to 72
@Test
public void generateServiceClassesWicked() {
GapicContext context = GrpcRestTestProtoLoader.instance().parseShowcaseWicked();
Service wickedProtoService = context.services().get(0);
GapicClass clazz =
ServiceStubSettingsClassComposer.instance().generate(context, wickedProtoService);

JavaWriterVisitor visitor = new JavaWriterVisitor();
clazz.classDefinition().accept(visitor);
GoldenFileWriter.saveCodegenToFile(
this.getClass(), "WickedStubSettings.golden", visitor.write());
Path goldenFilePath =
Paths.get(GoldenFileWriter.getGoldenDir(this.getClass()), "WickedStubSettings.golden");
GoldenFileWriter.saveCodegenToFile(this.getClass(), goldenFileName, visitor.write());
Path goldenFilePath = Paths.get(GoldenFileWriter.getGoldenDir(this.getClass()), goldenFileName);
Assert.assertCodeEquals(goldenFilePath, visitor.write());
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that this is included as a parameterized test. I don't think we need this method anymore.

Copy link
Contributor Author

@zhumin8 zhumin8 Apr 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah. good catch. Done aea6e77.

Comment on lines +150 to +153
ApiClientHeaderProvider emptyProvider =
ApiClientHeaderProvider.newBuilder().setApiVersionToken("").build();
assertThat(
emptyProvider.getHeaders().get(ApiClientHeaderProvider.API_VERSION_HEADER_KEY).isEmpty());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My personal opinion is that I think this case should result in the ApiVersion header not being passed (similar to how it would behave if the service proto doesn't have this option). I know the client library can't reasonably do validation all values passed in to the ApiClientHeader, but empty string and null are cases which we know aren't valid ApiVersions.

The reason I have this case in mind is because it is something we check for inside the EndpointContext:

if (universeDomain != null && universeDomain.isEmpty()) {
throw new IllegalArgumentException("The universe domain value cannot be empty.");
}

Copy link
Contributor Author

@zhumin8 zhumin8 Apr 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the context.

The way I see it currently is that, this logic of validating api version should not be repeated if possible. One benefit is that in case of future changes to it, say additional logic to treat a group of versions in a way (totally hypothetical here), change can be centralized.
Currently, this apiVersion information flows from:
proto --> via Parser --> model (Service) --> via composer --> output code (StubSettings) --> creates a ApiClientHeaderProvider instance.
ApiClientHeaderProvider.java does not need to know the details about how to validate api versions and it is not its responsibility to validate.

WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we might be on two different pages here. I don't intend for ApiClientHeaderProvider to try and validate the value that is set by the service team inside the proto file. If that value is not set by the team or is set as empty, then I trust that the generator has the correct logic to determine when the setApiVersionToken() code should or should not be generated.

My concern is that both ApiClientHeaderProvider and the ApiVersionToken setter is public. Users can create an ApiClientHeaderProvider separately and then pass it into the client via StubSettings (

protected B setInternalHeaderProvider(HeaderProvider internalHeaderProvider) {
this.internalHeaderProvider = internalHeaderProvider;
if (this.quotaProjectId == null
&& internalHeaderProvider.getHeaders().containsKey(QUOTA_PROJECT_ID_HEADER_KEY)) {
this.quotaProjectId = internalHeaderProvider.getHeaders().get(QUOTA_PROJECT_ID_HEADER_KEY);
}
return self();
}
). I believe it's possible given that we previously have suggested to users to set their own header provider values for certain workaround use cases.

I agree that's it's a extremely rare possibility that users are both creating a custom HeaderProvider and intentionally setting the ApiVersionToken to "", but because it's possible, I don't want to rule that possibility out. IMO, we should keep consistent with the default behavior done automatically for GAPICs (i.e. if it is set to be null or empty string, then it's not set in the header).

Granted this is a small nit pick that I think is a better UX. If you feel strongly otherwise, I don't want to continue to block on this small part. I think the other bits LGTM and I will do another pass before approving. Thanks for all the changes :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Followup discussions directed off-line.
Merging this pr for now. May raise followup based on discussion result.

Copy link

Quality Gate Failed Quality Gate failed for 'gapic-generator-java-root'

Failed conditions
68.4% Coverage on New Code (required ≥ 80%)

See analysis details on SonarCloud

Copy link

Quality Gate Passed Quality Gate passed for 'java_showcase_integration_tests'

Issues
1 New issue
0 Accepted issues

Measures
0 Security Hotspots
100.0% Coverage on New Code
0.0% Duplication on New Code

See analysis details on SonarCloud

@zhumin8 zhumin8 marked this pull request as ready for review April 26, 2024 18:07
@zhumin8 zhumin8 requested a review from a team as a code owner April 26, 2024 18:07
Copy link

snippet-bot bot commented Apr 26, 2024

Here is the summary of possible violations 😱

There is a possible violation for not having product prefix.

The end of the violation section. All the stuff below is FYI purposes only.


Here is the summary of changes.

You are about to add 1 region tag.

This comment is generated by snippet-bot.
If you find problems with this result, please file an issue at:
https://github.com/googleapis/repo-automation-bots/issues.
To update this comment, add snippet-bot:force-run label or use the checkbox below:

  • Refresh this comment

@zhumin8 zhumin8 added the owlbot:run Add this label to trigger the Owlbot post processor. label Apr 26, 2024
@gcf-owl-bot gcf-owl-bot bot removed the owlbot:run Add this label to trigger the Owlbot post processor. label Apr 26, 2024
@zhumin8 zhumin8 merged commit e63d1b4 into main Apr 26, 2024
38 of 40 checks passed
@zhumin8 zhumin8 deleted the api-version-2 branch April 26, 2024 21:30
alicejli pushed a commit that referenced this pull request May 2, 2024
🤖 I have created a release *beep* *boop*
---


<details><summary>2.40.0</summary>

##
[2.40.0](v2.39.0...v2.40.0)
(2024-05-02)


### Features

* [common-protos] add `Weight` to common types for Shopping APIs to be
used for accounts bundle
([#2699](#2699))
([5bb9770](5bb9770))
* add a CLI tool to validate generation configuration
([#2691](#2691))
([f2ce524](f2ce524))
* Parser to consume the api-versioning value from proto
([#2630](#2630))
([40711fd](40711fd))
* Update Gapic generator and Gax to emit api-versioning via header
([#2671](#2671))
([e63d1b4](e63d1b4))


### Bug Fixes

* change folder prefix for adding headers
([#2688](#2688))
([4e92be8](4e92be8))
* Log HttpJson's async thread pool core size
([#2697](#2697))
([34b4bc3](34b4bc3))
* replace `cfg = "host"` with `cfg = "exec"`
([#2637](#2637))
([6d673f3](6d673f3))
* Return resolved endpoint from StubSettings' Builder
([#2715](#2715))
([32c9995](32c9995))


### Dependencies

* Make opentelemetry-api an optional dependency.
([#2681](#2681))
([3967a19](3967a19))
* update dependency absl-py to v2.1.0
([#2659](#2659))
([cae6d79](cae6d79))
* update dependency gitpython to v3.1.43
([#2656](#2656))
([208bef4](208bef4))
* update dependency lxml to v5.2.1
([#2661](#2661))
([b95ad49](b95ad49))
* update dependency net.bytebuddy:byte-buddy to v1.14.14
([#2703](#2703))
([87069bc](87069bc))
* update dependency typing to v3.10.0.0
([#2663](#2663))
([7fb5653](7fb5653))
* update gapic-showcase to v0.33.0
([#2653](#2653))
([0a71cbf](0a71cbf))


### Documentation

* Add contributing guidelines to PR and issue templates
([#2682](#2682))
([42526dc](42526dc))
</details>

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
zhumin8 added a commit that referenced this pull request May 10, 2024
This pr updates gapic-showcase version to 0.35.0 and adds showcase tests
to verify behavior of api version headers being emitted (changes in
#2630 and #2671).
lqiu96 pushed a commit that referenced this pull request May 22, 2024
#2671)

This is part 2, following changes in
#2630

Changes in this pr:
- ApiClientHeaderProvider: add key and token setter for api version.
- AbstractServiceStubSettingsClassComposer: add logic to set this header
when api-version is present (not null or empty)
- Unit and golden tests to reflect above changes. (This includeds a few
new golden files created for tests based on the dedicated proto created
in #2630)

This pr does not include gapic-showcased testing for now.
---
manual tested with
[gapic-showcase](https://github.com/googleapis/gapic-showcase) v0.33.0

steps: 
- Use gapic-showcase v0.33.0 (which includes update in 1484)
- use `gapic-showcase run -v` which prints the request header
- From showcase folder, run `mvn verify -P enable-integration-tests
-Dit.test=ITAutoPopulatedFields.java -pl=gapic-showcase`

Printed header in manual testing
Grpc
```
2024/04/16 10:26:41 Received Unary Request for Method: /google.showcase.v1beta1.Echo/Echo
2024/04/16 10:26:41     Request headers:
2024/04/16 10:26:41       grpc-accept-encoding: gzip
2024/04/16 10:26:41       content-type: application/grpc
2024/04/16 10:26:41       x-goog-api-version: v1_20240408
2024/04/16 10:26:41       :authority: localhost:7469
2024/04/16 10:26:41       user-agent: grpc-java-netty/1.62.2
2024/04/16 10:26:41       x-goog-api-client: gl-java/17.0.7__Eclipse-Adoptium__Temurin-17.0.7-7 gapic/0.0.1-SNAPSHOT gax/2.46.2-SNAPSHOT grpc/1.62.2
2024/04/16 10:26:41     Request:  error:{code:2}  request_id:"1e26ee16-ed07-4eeb-be0e-49b769678779"  other_request_id:"40883e5d-3b20-4add-854c-9808bd009260"
2024/04/16 10:26:41     Returning Error: rpc error: code = Unknown desc = 
```
Httpjson
```
2024/04/16 10:26:41 Received POST request matching '/v1beta1/echo:echo': "/v1beta1/echo:echo"                                                               
2024/04/16 10:26:41   urlPathParams (expect 0, have 0): map[]                 
2024/04/16 10:26:41   urlRequestHeaders:                                                                                                                    
    User-Agent: "Google-HTTP-Java-Client/1.44.1 (gzip)"                                                                                                     
    Content-Type: "application/json; charset=utf-8"                           
    Connection: "keep-alive"                                                                                                                                
    Accept-Encoding: "gzip"                                                   
    X-Goog-Api-Client: "gl-java/17.0.7__Eclipse-Adoptium__Temurin-17.0.7-7 gapic/0.0.1-SNAPSHOT gax/2.46.2-SNAPSHOT rest/"                                  
    X-Goog-Api-Version: "v1_20240408"                                                                                                                       
    Accept: "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2"            
    Content-Length: "129"                                                     
2024/04/16 10:26:41   request: {                                                                                                                            
  "error":  {                                                                                                                                               
    "code":  500,                                                                                                                                           
    "message":  "",                                                                                                                                         
    "details":  []                                                            
  },                                                                                                                                                        
  "severity":  "UNNECESSARY",                                                                                                                               
  "header":  "",                                                                                                                                            
  "otherHeader":  "",                                                         
  "requestId":  "e690a84b-176d-421e-9e5d-ba752f68fec8",                       
  "otherRequestId":  "47e0fbde-056a-43ae-bba6-8b6770d861f7"                   
}    
```
lqiu96 pushed a commit that referenced this pull request May 22, 2024
🤖 I have created a release *beep* *boop*
---


<details><summary>2.40.0</summary>

##
[2.40.0](v2.39.0...v2.40.0)
(2024-05-02)


### Features

* [common-protos] add `Weight` to common types for Shopping APIs to be
used for accounts bundle
([#2699](#2699))
([5bb9770](5bb9770))
* add a CLI tool to validate generation configuration
([#2691](#2691))
([f2ce524](f2ce524))
* Parser to consume the api-versioning value from proto
([#2630](#2630))
([40711fd](40711fd))
* Update Gapic generator and Gax to emit api-versioning via header
([#2671](#2671))
([e63d1b4](e63d1b4))


### Bug Fixes

* change folder prefix for adding headers
([#2688](#2688))
([4e92be8](4e92be8))
* Log HttpJson's async thread pool core size
([#2697](#2697))
([34b4bc3](34b4bc3))
* replace `cfg = "host"` with `cfg = "exec"`
([#2637](#2637))
([6d673f3](6d673f3))
* Return resolved endpoint from StubSettings' Builder
([#2715](#2715))
([32c9995](32c9995))


### Dependencies

* Make opentelemetry-api an optional dependency.
([#2681](#2681))
([3967a19](3967a19))
* update dependency absl-py to v2.1.0
([#2659](#2659))
([cae6d79](cae6d79))
* update dependency gitpython to v3.1.43
([#2656](#2656))
([208bef4](208bef4))
* update dependency lxml to v5.2.1
([#2661](#2661))
([b95ad49](b95ad49))
* update dependency net.bytebuddy:byte-buddy to v1.14.14
([#2703](#2703))
([87069bc](87069bc))
* update dependency typing to v3.10.0.0
([#2663](#2663))
([7fb5653](7fb5653))
* update gapic-showcase to v0.33.0
([#2653](#2653))
([0a71cbf](0a71cbf))


### Documentation

* Add contributing guidelines to PR and issue templates
([#2682](#2682))
([42526dc](42526dc))
</details>

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
lqiu96 pushed a commit that referenced this pull request May 22, 2024
This pr updates gapic-showcase version to 0.35.0 and adds showcase tests
to verify behavior of api version headers being emitted (changes in
#2630 and #2671).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
size: xl Pull request size is extra large.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants