35
35
import com .google .api .gax .core .BackgroundResource ;
36
36
import com .google .api .gax .core .ExecutorAsBackgroundResource ;
37
37
import com .google .api .gax .core .ExecutorProvider ;
38
+ import com .google .api .gax .rpc .internal .EnvironmentProvider ;
38
39
import com .google .api .gax .rpc .internal .QuotaProjectIdHidingCredentials ;
39
40
import com .google .api .gax .rpc .mtls .MtlsProvider ;
40
41
import com .google .api .gax .tracing .ApiTracerFactory ;
41
42
import com .google .api .gax .tracing .BaseApiTracerFactory ;
42
43
import com .google .auth .Credentials ;
43
44
import com .google .auto .value .AutoValue ;
45
+ import com .google .common .annotations .VisibleForTesting ;
44
46
import com .google .common .collect .ImmutableList ;
45
47
import com .google .common .collect .ImmutableMap ;
46
48
import com .google .common .collect .Sets ;
65
67
@ AutoValue
66
68
public abstract class ClientContext {
67
69
private static final String QUOTA_PROJECT_ID_HEADER_KEY = "x-goog-user-project" ;
70
+ private static final String API_KEY_HEADER_KEY = "x-goog-api-key" ;
68
71
69
72
/**
70
73
* The objects that need to be closed in order to clean up the resources created in the process of
@@ -159,6 +162,32 @@ static String getEndpoint(
159
162
return endpoint ;
160
163
}
161
164
165
+ /**
166
+ * Retrieves the API key value and add it to the headers if API key exists. It first tries to
167
+ * retrieve the value from the stub settings. If not found, it then tries the load the
168
+ * GOOGLE_API_KEY environment variable. An IOException will be thrown if both GOOGLE_API_KEY and
169
+ * GOOGLE_APPLICATION_CREDENTIALS environment variables are set.
170
+ */
171
+ @ VisibleForTesting
172
+ static void addApiKeyToHeaders (
173
+ StubSettings settings , EnvironmentProvider environmentProvider , Map <String , String > headers )
174
+ throws IOException {
175
+ if (settings .getApiKey () != null ) {
176
+ headers .put (API_KEY_HEADER_KEY , settings .getApiKey ());
177
+ return ;
178
+ }
179
+
180
+ String apiKey = environmentProvider .getenv ("GOOGLE_API_KEY" );
181
+ String applicationCredentials = environmentProvider .getenv ("GOOGLE_APPLICATION_CREDENTIALS" );
182
+ if (apiKey != null && applicationCredentials != null ) {
183
+ throw new IOException (
184
+ "Environment variables GOOGLE_API_KEY and GOOGLE_APPLICATION_CREDENTIALS are mutually exclusive" );
185
+ }
186
+ if (apiKey != null ) {
187
+ headers .put (API_KEY_HEADER_KEY , apiKey );
188
+ }
189
+ }
190
+
162
191
/**
163
192
* Instantiates the executor, credentials, and transport context based on the given client
164
193
* settings.
@@ -169,14 +198,21 @@ public static ClientContext create(StubSettings settings) throws IOException {
169
198
ExecutorProvider backgroundExecutorProvider = settings .getBackgroundExecutorProvider ();
170
199
final ScheduledExecutorService backgroundExecutor = backgroundExecutorProvider .getExecutor ();
171
200
172
- Credentials credentials = settings .getCredentialsProvider ().getCredentials ();
201
+ Credentials credentials = null ;
202
+ Map <String , String > headers = getHeadersFromSettingsAndEnvironment (settings , System ::getenv );
173
203
174
- if (settings .getQuotaProjectId () != null ) {
175
- // If the quotaProjectId is set, wrap original credentials with correct quotaProjectId as
176
- // QuotaProjectIdHidingCredentials.
177
- // Ensure that a custom set quota project id takes priority over one detected by credentials.
178
- // Avoid the backend receiving possibly conflict values of quotaProjectId
179
- credentials = new QuotaProjectIdHidingCredentials (credentials );
204
+ boolean hasApiKey = headers .containsKey (API_KEY_HEADER_KEY );
205
+ if (!hasApiKey ) {
206
+ credentials = settings .getCredentialsProvider ().getCredentials ();
207
+
208
+ if (settings .getQuotaProjectId () != null ) {
209
+ // If the quotaProjectId is set, wrap original credentials with correct quotaProjectId as
210
+ // QuotaProjectIdHidingCredentials.
211
+ // Ensure that a custom set quota project id takes priority over one detected by
212
+ // credentials.
213
+ // Avoid the backend receiving possibly conflict values of quotaProjectId
214
+ credentials = new QuotaProjectIdHidingCredentials (credentials );
215
+ }
180
216
}
181
217
182
218
TransportChannelProvider transportChannelProvider = settings .getTransportChannelProvider ();
@@ -186,11 +222,11 @@ public static ClientContext create(StubSettings settings) throws IOException {
186
222
if (transportChannelProvider .needsExecutor () && settings .getExecutorProvider () != null ) {
187
223
transportChannelProvider = transportChannelProvider .withExecutor (backgroundExecutor );
188
224
}
189
- Map < String , String > headers = getHeadersFromSettings ( settings );
225
+
190
226
if (transportChannelProvider .needsHeaders ()) {
191
227
transportChannelProvider = transportChannelProvider .withHeaders (headers );
192
228
}
193
- if (transportChannelProvider .needsCredentials () && credentials != null ) {
229
+ if (! hasApiKey && transportChannelProvider .needsCredentials ()) {
194
230
transportChannelProvider = transportChannelProvider .withCredentials (credentials );
195
231
}
196
232
String endpoint =
@@ -260,7 +296,8 @@ public static ClientContext create(StubSettings settings) throws IOException {
260
296
* Getting a header map from HeaderProvider and InternalHeaderProvider from settings with Quota
261
297
* Project Id.
262
298
*/
263
- private static Map <String , String > getHeadersFromSettings (StubSettings settings ) {
299
+ private static Map <String , String > getHeadersFromSettingsAndEnvironment (
300
+ StubSettings settings , EnvironmentProvider environmentProvider ) throws IOException {
264
301
// Resolve conflicts when merging headers from multiple sources
265
302
Map <String , String > userHeaders = settings .getHeaderProvider ().getHeaders ();
266
303
Map <String , String > internalHeaders = settings .getInternalHeaderProvider ().getHeaders ();
@@ -286,6 +323,7 @@ private static Map<String, String> getHeadersFromSettings(StubSettings settings)
286
323
effectiveHeaders .putAll (internalHeaders );
287
324
effectiveHeaders .putAll (userHeaders );
288
325
effectiveHeaders .putAll (conflictResolution );
326
+ addApiKeyToHeaders (settings , environmentProvider , effectiveHeaders );
289
327
290
328
return ImmutableMap .copyOf (effectiveHeaders );
291
329
}
0 commit comments