This repository has been archived by the owner on Sep 26, 2023. It is now read-only.
feat: enable setting quota_project_id #1128
Merged
Merged
Changes from 3 commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
8e7b92f
Enable Quota Project ID in Client setting
summer-ji-eng c641572
Add quota project Id in clientContext
summer-ji-eng cea6118
change from quotaProjectID to quotaProjectId
summer-ji-eng a157f44
Add Executor
summer-ji-eng 1151fdc
Add quotaProjectHidingCredentialsTest
summer-ji-eng c6b9d47
correct getRequestMetadata
summer-ji-eng 0a8d179
Add unit test for QuotaProjectIdHidingCredentials
summer-ji-eng a63ffae
add unit test for client context
summer-ji-eng 74541a5
Merge branch 'master' into quota_project
summer-ji-eng 6a56e6b
Merge branch 'master' into quota_project
summer-ji-eng 0e1e201
Add comment and beta annotation
summer-ji-eng 7f91763
Update gax/src/test/java/com/google/api/gax/rpc/internal/QuotaProject…
summer-ji-eng File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,6 +41,8 @@ | |
import com.google.api.gax.core.NoCredentialsProvider; | ||
import com.google.api.gax.tracing.ApiTracerFactory; | ||
import com.google.api.gax.tracing.NoopApiTracerFactory; | ||
import com.google.auth.Credentials; | ||
import com.google.auth.oauth2.QuotaProjectIdProvider; | ||
import com.google.common.base.MoreObjects; | ||
import com.google.common.base.Preconditions; | ||
import java.io.IOException; | ||
|
@@ -60,13 +62,16 @@ | |
*/ | ||
public abstract class StubSettings<SettingsT extends StubSettings<SettingsT>> { | ||
|
||
static final String QUOTA_PROJECT_ID_HEADER_KEY = "x-google-user-project"; | ||
|
||
private final ExecutorProvider executorProvider; | ||
private final CredentialsProvider credentialsProvider; | ||
private final HeaderProvider headerProvider; | ||
private final HeaderProvider internalHeaderProvider; | ||
private final TransportChannelProvider transportChannelProvider; | ||
private final ApiClock clock; | ||
private final String endpoint; | ||
private final String quotaProjectId; | ||
@Nullable private final WatchdogProvider streamWatchdogProvider; | ||
@Nonnull private final Duration streamWatchdogCheckInterval; | ||
@Nonnull private final ApiTracerFactory tracerFactory; | ||
|
@@ -80,6 +85,7 @@ protected StubSettings(Builder builder) { | |
this.internalHeaderProvider = builder.internalHeaderProvider; | ||
this.clock = builder.clock; | ||
this.endpoint = builder.endpoint; | ||
this.quotaProjectId = builder.quotaProjectId; | ||
this.streamWatchdogProvider = builder.streamWatchdogProvider; | ||
this.streamWatchdogCheckInterval = builder.streamWatchdogCheckInterval; | ||
this.tracerFactory = builder.tracerFactory; | ||
|
@@ -115,6 +121,10 @@ public final String getEndpoint() { | |
return endpoint; | ||
} | ||
|
||
public final String getQuotaProjectId() { | ||
return quotaProjectId; | ||
} | ||
|
||
@BetaApi("The surface for streaming is not stable yet and may change in the future.") | ||
@Nullable | ||
public final WatchdogProvider getStreamWatchdogProvider() { | ||
|
@@ -146,6 +156,7 @@ public String toString() { | |
.add("internalHeaderProvider", internalHeaderProvider) | ||
.add("clock", clock) | ||
.add("endpoint", endpoint) | ||
.add("quotaProjectId", quotaProjectId) | ||
.add("streamWatchdogProvider", streamWatchdogProvider) | ||
.add("streamWatchdogCheckInterval", streamWatchdogCheckInterval) | ||
.add("tracerFactory", tracerFactory) | ||
|
@@ -164,6 +175,7 @@ public abstract static class Builder< | |
private TransportChannelProvider transportChannelProvider; | ||
private ApiClock clock; | ||
private String endpoint; | ||
private String quotaProjectId; | ||
@Nullable private WatchdogProvider streamWatchdogProvider; | ||
@Nonnull private Duration streamWatchdogCheckInterval; | ||
@Nonnull private ApiTracerFactory tracerFactory; | ||
|
@@ -177,11 +189,29 @@ protected Builder(StubSettings settings) { | |
this.internalHeaderProvider = settings.internalHeaderProvider; | ||
this.clock = settings.clock; | ||
this.endpoint = settings.endpoint; | ||
this.quotaProjectId = settings.quotaProjectId; | ||
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) { | ||
if (clientContext.getQuotaProjectId() != null) { | ||
return clientContext.getQuotaProjectId(); | ||
} | ||
if (clientContext.getCredentials() instanceof QuotaProjectIdProvider) { | ||
return ((QuotaProjectIdProvider) clientContext.getCredentials()).getQuotaProjectId(); | ||
} | ||
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)) { | ||
return clientContext.getInternalHeaders().get(QUOTA_PROJECT_ID_HEADER_KEY); | ||
} | ||
return null; | ||
} | ||
|
||
protected Builder(ClientContext clientContext) { | ||
if (clientContext == null) { | ||
this.executorProvider = InstantiatingExecutorProvider.newBuilder().build(); | ||
|
@@ -191,6 +221,7 @@ protected Builder(ClientContext clientContext) { | |
this.internalHeaderProvider = new NoHeaderProvider(); | ||
this.clock = NanoClock.getDefaultClock(); | ||
this.endpoint = null; | ||
this.quotaProjectId = null; | ||
this.streamWatchdogProvider = InstantiatingWatchdogProvider.create(); | ||
this.streamWatchdogCheckInterval = Duration.ofSeconds(10); | ||
this.tracerFactory = NoopApiTracerFactory.getInstance(); | ||
|
@@ -208,6 +239,7 @@ protected Builder(ClientContext clientContext) { | |
FixedWatchdogProvider.create(clientContext.getStreamWatchdog()); | ||
this.streamWatchdogCheckInterval = clientContext.getStreamWatchdogCheckInterval(); | ||
this.tracerFactory = clientContext.getTracerFactory(); | ||
this.quotaProjectId = getQuotaProjectIdFromClientContext(clientContext); | ||
} | ||
} | ||
|
||
|
@@ -234,6 +266,14 @@ public B setExecutorProvider(ExecutorProvider executorProvider) { | |
/** Sets the CredentialsProvider to use for getting the credentials to make calls with. */ | ||
public B setCredentialsProvider(CredentialsProvider credentialsProvider) { | ||
this.credentialsProvider = Preconditions.checkNotNull(credentialsProvider); | ||
try { | ||
Credentials credentials = credentialsProvider.getCredentials(); | ||
if (this.quotaProjectId == null && credentials instanceof QuotaProjectIdProvider) { | ||
this.quotaProjectId = ((QuotaProjectIdProvider) credentials).getQuotaProjectId(); | ||
} | ||
} catch (IOException e) { | ||
System.out.println("fail to fetch credentials"); | ||
} | ||
return self(); | ||
} | ||
|
||
|
@@ -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 commentThe 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. |
||
&& headerProvider.getHeaders().containsKey(QUOTA_PROJECT_ID_HEADER_KEY)) { | ||
this.quotaProjectId = headerProvider.getHeaders().get(QUOTA_PROJECT_ID_HEADER_KEY); | ||
} | ||
return self(); | ||
} | ||
|
||
|
@@ -260,6 +304,10 @@ public B setHeaderProvider(HeaderProvider headerProvider) { | |
@BetaApi("The surface for customizing headers is not stable yet and may change in the future.") | ||
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(); | ||
} | ||
|
||
|
@@ -298,6 +346,11 @@ public B setEndpoint(String endpoint) { | |
return self(); | ||
} | ||
|
||
public B setQuotaProjectId(String quotaProjectId) { | ||
this.quotaProjectId = quotaProjectId; | ||
return self(); | ||
} | ||
|
||
/** | ||
* Sets how often the {@link Watchdog} will check ongoing streaming RPCs. Defaults to 10 secs. | ||
* Use {@link Duration#ZERO} to disable. | ||
|
@@ -364,6 +417,11 @@ public String getEndpoint() { | |
return endpoint; | ||
} | ||
|
||
/** Gets the QuotaProjectId that was previously set on this Builder. */ | ||
public String getQuotaProjectId() { | ||
return quotaProjectId; | ||
} | ||
|
||
@BetaApi("The surface for streaming is not stable yet and may change in the future.") | ||
@Nonnull | ||
public Duration getStreamWatchdogCheckInterval() { | ||
|
@@ -396,6 +454,7 @@ public String toString() { | |
.add("internalHeaderProvider", internalHeaderProvider) | ||
.add("clock", clock) | ||
.add("endpoint", endpoint) | ||
.add("quotaProjectId", quotaProjectId) | ||
.add("streamWatchdogProvider", streamWatchdogProvider) | ||
.add("streamWatchdogCheckInterval", streamWatchdogCheckInterval) | ||
.add("tracerFactory", tracerFactory) | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back 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.
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.