feat: enable setting quota_project_id#1128
Conversation
Codecov Report
@@ Coverage Diff @@
## master #1128 +/- ##
============================================
+ Coverage 78.65% 79.06% +0.41%
- Complexity 1170 1193 +23
============================================
Files 204 205 +1
Lines 5195 5264 +69
Branches 417 434 +17
============================================
+ Hits 4086 4162 +76
+ Misses 935 930 -5
+ Partials 174 172 -2
Continue to review full report at Codecov.
|
| } | ||
|
|
||
| /** Get Quota Project ID from Client Context * */ | ||
| private static String getQuotaProjectIDFromClientContext(ClientContext clientContext) { |
There was a problem hiding this comment.
Seems like we use ID in methods and variables, and Id in class names. Is this idiomatic?
There was a problem hiding this comment.
ID used in all methods and variables, which is really just my preference. I haven't find any guideline for naming.
Let me know if any rule need to be follow here, I could make change.
There was a problem hiding this comment.
We should use quotaProjectId. See https://google.github.io/styleguide/javaguide.html#s5-naming
There was a problem hiding this comment.
I will make change to quotaProjectId
| if (clientContext.getHeaders().containsKey(QUOTA_PROJECT_ID_HEADER_KEY)) { | ||
| return clientContext.getHeaders().get(QUOTA_PROJECT_ID_HEADER_KEY); | ||
| } | ||
| if (clientContext.getInternalHeaders().containsKey(QUOTA_PROJECT_ID_HEADER_KEY)) { |
There was a problem hiding this comment.
What is the difference between headers and internal headers? And is it possible for quota project to come in on either one?
Also, what is the use case for pulling it from the header? I think maybe it is not necessary to detect this (@broady ?)
There was a problem hiding this comment.
quota project id can be gotten from header and internal header. double confirm with @chingor13
but I am not sure the use case. @broady
There was a problem hiding this comment.
I believe the internal header provider is reserved for gapic and handwritten layers to inject headers. The general header provider is to allow library users to specify arbitrary extra headers.
It's not desired for library users to set the quota project via header provider, but previously that was the only mechanism for doing so. We should have an explicit strategy for how to handle that case.
There was a problem hiding this comment.
Thanks for clarifying.
There was a problem hiding this comment.
It seems ok to support this then, for backwards compatibility. And also to ensure any quota project behavior stays consistent regardless of how it's set.
chingor13
left a comment
There was a problem hiding this comment.
Maybe it's just me, but I'm not seeing how the quota project id when configured from ClientSettings is actually making it into the request or connection.
Can I have more detail about how do you use configure from ClientSetting. Try to reproduce on my side. |
Just the direct configuration method: TextToSpeechSettings settingsWithAllSource = TextToSpeechSettings.newBuilder()
.setQuotaProjectId(QUOTA_PROJECT_ID)
.build();Reading the code, I see how the For example, the credentials authorization header is supplied by providing the credentials to the ApiCallContext which is used for setting up the channel and all options. |
|
@chingor13 @bshaffer |
|
I'm still concerned that the headers are not actually configured for the gax-java/gax/src/main/java/com/google/api/gax/rpc/ClientContext.java Lines 151 to 166 in cea6118 x-goog-user-project header.
The testing code in the generated showcase client is inspecting the settings object (which is correct), but it's not ensuring that the header is being set on the gRPC request. |
| @BetaApi("The surface for customizing headers is not stable yet and may change in the future.") | ||
| public B setHeaderProvider(HeaderProvider headerProvider) { | ||
| this.headerProvider = headerProvider; | ||
| if (this.quotaProjectId == null |
There was a problem hiding this comment.
@chingor13 is it necessary to support setting quota project automatically through the incoming headers? This is not something I am familiar with, or something done in any other languages AFAIK. It also makes the precedent of these operations less intuitive.
There was a problem hiding this comment.
I think what we want to do:
In ClientContext.create(), we want to add "x-goog-user-project" => settings.getQuotaProjectId() to the Map<String, String> headers that we provide to the transportChannelProvider. This will populate the x-goog-user-project on all the requests using that transport.
If a quotaProjectId is set, we also need to tell the Credentials to no longer send the x-goog-user-project header (it could be an incorrect value as the explicitly configured quotaProjectId setting should take precedence over the ones provided by the credentials). This will be harder and likely we'll have to do something ugly like providing our a wrapper Credentials implementation:
static class QuotaProjectHidingCredentials implements Credentials {
private Credentials wrappedCredentials;
QuotaProjectHidingCredentials(Credentials wrappedCredentials) {
this.wrappedCredentials = wrappedCredentials;
}
public Map<String,List<String>> getRequestMetadata(URI uri) {
Map<String,List<String>> headers = wrappedCredentials.getRequestMetadata(URI uri);
// return new map without "x-goog-user-project" header
}
// ... other abstract methods delegate to the wrapped credentials
}
|
|
||
| // TODO: Populate this from dependencies.properties version property (for proper Gradle-Bazel sync) | ||
| project.version = "1.57.1" // {x-version-update:gax:current} | ||
| project.version = "1.57.2" // {x-version-update:gax:current} |
There was a problem hiding this comment.
@chingor13 not sure if I should upgrade a version. let me know what you think. Thanks
bshaffer
left a comment
There was a problem hiding this comment.
One question for clarification, and a minor request. Otherwise LGTM!
| if (clientContext.getHeaders().containsKey(QUOTA_PROJECT_ID_HEADER_KEY)) { | ||
| return clientContext.getHeaders().get(QUOTA_PROJECT_ID_HEADER_KEY); | ||
| } | ||
| if (clientContext.getInternalHeaders().containsKey(QUOTA_PROJECT_ID_HEADER_KEY)) { |
There was a problem hiding this comment.
It seems ok to support this then, for backwards compatibility. And also to ensure any quota project behavior stays consistent regardless of how it's set.
chingor13
left a comment
There was a problem hiding this comment.
One more header to fix, then LGTM
…IdHidingCredentialsTest.java Co-authored-by: Jeff Ching <chingor@google.com>
As part of extensible client options, enable quota_project_id in GAPIC. So that client is able to set quota project ID, like "Settings.NewBuilder().SetQuotaProjectID(String)"
A quota project id might set up from 4 ways:
Open Question:
1st priority of getting value is from
SetQuotaProjectID, if it exists, would overwrite all value set from other ways.So, what is the priority of other 3 ways, if
SetQuotaProjectIDis absent.The current implementation is based on the order of calling the method. Let me know if you have any other preference. @chingor13
Integration Test: Tested in local java-TextToSpeech repo and using the local snapshot, test passes.