-
Notifications
You must be signed in to change notification settings - Fork 105
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.
|
this.streamWatchdogProvider = settings.streamWatchdogProvider; | ||
this.streamWatchdogCheckInterval = settings.streamWatchdogCheckInterval; | ||
this.tracerFactory = settings.tracerFactory; | ||
} | ||
|
||
/** Get Quota Project ID from Client Context * */ | ||
private static String getQuotaProjectIDFromClientContext(ClientContext clientContext) { |
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.
Seems like we use ID
in methods and variables, and Id
in class names. Is this idiomatic?
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should use quotaProjectId
. See https://google.github.io/styleguide/javaguide.html#s5-naming
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 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for clarifying.
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 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.
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.
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. |
@@ -247,6 +287,10 @@ public B setCredentialsProvider(CredentialsProvider credentialsProvider) { | |||
@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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
}
build.gradle
Outdated
@@ -24,7 +24,7 @@ apply plugin: 'com.github.sherter.google-java-format' | |||
apply plugin: 'io.codearte.nexus-staging' | |||
|
|||
// 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@chingor13 not sure if I should upgrade a version. let me know what you think. 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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
gax/src/main/java/com/google/api/gax/rpc/internal/QuotaProjectIdHidingCredentials.java
Outdated
Show resolved
Hide resolved
gax/src/main/java/com/google/api/gax/rpc/internal/QuotaProjectIdHidingCredentials.java
Show resolved
Hide resolved
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.
One more header to fix, then LGTM
gax/src/test/java/com/google/api/gax/rpc/internal/QuotaProjectIdHidingCredentialsTest.java
Outdated
Show resolved
Hide resolved
…IdHidingCredentialsTest.java Co-authored-by: Jeff Ching <[email protected]>
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
SetQuotaProjectID
is 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.