Skip to content

Commit 0dd27c4

Browse files
authored
Add configurable metadata header for ads api assistant. (#1053)
1 parent e4e9f69 commit 0dd27c4

5 files changed

Lines changed: 95 additions & 16 deletions

File tree

google/ads/googleads/client.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ def _get_client_kwargs(cls, config_data: Dict[str, Any]) -> Dict[str, Any]:
204204
"use_cloud_org_for_api_access": config_data.get(
205205
"use_cloud_org_for_api_access"
206206
),
207+
"gaada": config_data.get("gaada"),
207208
}
208209

209210
@classmethod
@@ -328,6 +329,7 @@ def __init__(
328329
http_proxy: Union[str, None] = None,
329330
use_proto_plus: bool = False,
330331
use_cloud_org_for_api_access: Union[str, None] = None,
332+
gaada: Union[str, None] = None,
331333
):
332334
"""Initializer for the GoogleAdsClient.
333335
@@ -346,7 +348,8 @@ def __init__(
346348
Google Cloud Organization of your Google Cloud project instead
347349
of developer token to determine your Google Ads API access
348350
levels. Use this flag only if you are enrolled into a limited
349-
pilot that supports this configuration
351+
pilot that supports this configuration.
352+
gaada: a str specifying the Google Ads API Assistant version.
350353
"""
351354
if logging_config:
352355
logging.config.dictConfig(logging_config)
@@ -363,6 +366,7 @@ def __init__(
363366
use_cloud_org_for_api_access
364367
)
365368
self.enums: _EnumGetter = _EnumGetter(self)
369+
self.gaada: Union[str, None] = gaada
366370

367371
# If given, write the http_proxy channel option for GRPC to use
368372
if http_proxy:
@@ -442,12 +446,14 @@ def get_service(
442446
self.login_customer_id,
443447
self.linked_customer_id,
444448
self.use_cloud_org_for_api_access,
449+
gaada=self.gaada,
445450
),
446451
AsyncUnaryStreamMetadataInterceptor(
447452
self.developer_token,
448453
self.login_customer_id,
449454
self.linked_customer_id,
450455
self.use_cloud_org_for_api_access,
456+
gaada=self.gaada,
451457
),
452458
AsyncUnaryUnaryLoggingInterceptor(_logger, version, endpoint),
453459
AsyncUnaryStreamLoggingInterceptor(_logger, version, endpoint),
@@ -484,6 +490,7 @@ def get_service(
484490
self.login_customer_id,
485491
self.linked_customer_id,
486492
self.use_cloud_org_for_api_access,
493+
gaada=self.gaada,
487494
),
488495
LoggingInterceptor(_logger, version, endpoint),
489496
ExceptionInterceptor(

google/ads/googleads/config.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434
"linked_customer_id",
3535
"http_proxy",
3636
"use_cloud_org_for_api_access",
37-
"use_application_default_credentials"
37+
"use_application_default_credentials",
38+
"gaada",
3839
)
3940
_CONFIG_FILE_PATH_KEY = ("configuration_file_path",)
4041
_OAUTH2_INSTALLED_APP_KEYS = ("client_id", "client_secret", "refresh_token")

google/ads/googleads/interceptors/metadata_interceptor.py

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ def __init__(
6060
login_customer_id: Optional[str]= None,
6161
linked_customer_id: Optional[str] = None,
6262
use_cloud_org_for_api_access: Optional[bool] = None,
63+
gaada: Optional[str] = None,
6364
):
6465
"""Initialization method for this class.
6566
@@ -72,6 +73,7 @@ def __init__(
7273
of developer token to determine your Google Ads API access
7374
levels. Use this flag only if you are enrolled into a limited
7475
pilot that supports this configuration
76+
gaada: a str specifying the Google Ads API Assistant version.
7577
"""
7678
self.developer_token_meta: Tuple[str, str] = (
7779
"developer-token",
@@ -87,6 +89,7 @@ def __init__(
8789
if linked_customer_id
8890
else None
8991
)
92+
self.gaada: Optional[str] = gaada
9093
self.use_cloud_org_for_api_access: Optional[
9194
bool
9295
] = use_cloud_org_for_api_access
@@ -152,21 +155,28 @@ def _intercept(
152155
# fixed: https://github.com/googleapis/python-api-core/issues/416
153156
for i, metadatum_tuple in enumerate(metadata):
154157
# Check if the user agent header key is in the current metadatum
155-
if "x-goog-api-client" in metadatum_tuple and _PROTOBUF_VERSION:
156-
# Convert the tuple to a list so it can be modified.
158+
if "x-goog-api-client" in metadatum_tuple:
157159
metadatum: List[str] = list(metadatum_tuple)
158-
# Check that "pb" isn't already included in the user agent.
159-
if "pb" not in metadatum[1]:
160-
# Append the protobuf version key value pair to the end of
161-
# the string.
162-
metadatum[1] += f" pb/{_PROTOBUF_VERSION}{_PB_IMPL_HEADER}"
163-
# Convert the metadatum back to a tuple.
164-
metadatum_tuple: Tuple[str, str] = tuple(metadatum)
165-
# Splice the metadatum back in its original position in
166-
# order to preserve the order of the metadata list.
167-
metadata[i] = metadatum_tuple
168-
# Exit the loop since we already found the user agent.
169-
break
160+
161+
if self.gaada:
162+
metadatum[1] += f" gaada/{self.gaada}"
163+
164+
if _PROTOBUF_VERSION:
165+
# Convert the tuple to a list so it can be modified.
166+
# Check that "pb" isn't already included in the user agent.
167+
if "pb" not in metadatum[1]:
168+
# Append the protobuf version key value pair to the end of
169+
# the string.
170+
metadatum[1] += f" pb/{_PROTOBUF_VERSION}{_PB_IMPL_HEADER}"
171+
# Convert the metadatum back to a tuple.
172+
metadatum_tuple: Tuple[str, str] = tuple(metadatum)
173+
174+
# Splice the metadatum back in its original position in
175+
# order to preserve the order of the metadata list.
176+
metadata[i] = metadatum_tuple
177+
# Exit the loop since we already found the user agent.
178+
break
179+
170180

171181
client_call_details: grpc.ClientCallDetails = self._update_client_call_details_metadata(
172182
client_call_details, metadata

tests/client_test.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ def test_get_client_kwargs_login_customer_id(self):
114114
"linked_customer_id": self.linked_customer_id,
115115
"http_proxy": None,
116116
"use_cloud_org_for_api_access": None,
117+
"gaada": None
117118
},
118119
)
119120

@@ -147,6 +148,7 @@ def test_get_client_kwargs_login_customer_id_as_None(self):
147148
"linked_customer_id": None,
148149
"http_proxy": None,
149150
"use_cloud_org_for_api_access": None,
151+
"gaada": None
150152
},
151153
)
152154

@@ -180,6 +182,7 @@ def test_get_client_kwargs_linked_customer_id(self):
180182
"linked_customer_id": self.linked_customer_id,
181183
"http_proxy": None,
182184
"use_cloud_org_for_api_access": None,
185+
"gaada": None
183186
},
184187
)
185188

@@ -213,6 +216,7 @@ def test_get_client_kwargs_linked_customer_id_as_none(self):
213216
"login_customer_id": None,
214217
"http_proxy": None,
215218
"use_cloud_org_for_api_access": None,
219+
"gaada": None
216220
},
217221
)
218222

@@ -246,6 +250,7 @@ def test_get_client_kwargs(self):
246250
"linked_customer_id": None,
247251
"http_proxy": self.http_proxy,
248252
"use_cloud_org_for_api_access": None,
253+
"gaada": None
249254
},
250255
)
251256

@@ -280,6 +285,7 @@ def test_get_client_kwargs_custom_endpoint(self):
280285
"linked_customer_id": None,
281286
"http_proxy": None,
282287
"use_cloud_org_for_api_access": None,
288+
"gaada": None
283289
},
284290
)
285291

@@ -316,6 +322,7 @@ def test_load_from_env(self):
316322
version=None,
317323
http_proxy=None,
318324
use_cloud_org_for_api_access=None,
325+
gaada=None
319326
)
320327

321328
def test_load_from_env_versioned(self):
@@ -351,6 +358,7 @@ def test_load_from_env_versioned(self):
351358
version="v4",
352359
http_proxy=None,
353360
use_cloud_org_for_api_access=None,
361+
gaada=None
354362
)
355363

356364
def test_load_from_dict(self):
@@ -384,6 +392,7 @@ def test_load_from_dict(self):
384392
version=None,
385393
http_proxy=None,
386394
use_cloud_org_for_api_access=None,
395+
gaada=None
387396
)
388397

389398
def test_load_from_dict_versioned(self):
@@ -417,6 +426,7 @@ def test_load_from_dict_versioned(self):
417426
version="v4",
418427
http_proxy=None,
419428
use_cloud_org_for_api_access=None,
429+
gaada=None
420430
)
421431

422432
def test_load_from_string(self):
@@ -450,6 +460,7 @@ def test_load_from_string(self):
450460
version=None,
451461
http_proxy=None,
452462
use_cloud_org_for_api_access=None,
463+
gaada=None
453464
)
454465

455466
def test_load_from_string_versioned(self):
@@ -485,6 +496,7 @@ def test_load_from_string_versioned(self):
485496
version="v4",
486497
http_proxy=None,
487498
use_cloud_org_for_api_access=None,
499+
gaada=None
488500
)
489501

490502
def test_get_service(self):
@@ -881,6 +893,7 @@ def test_load_http_proxy_from_env(self):
881893
version=None,
882894
http_proxy=self.http_proxy,
883895
use_cloud_org_for_api_access=None,
896+
gaada=None
884897
)
885898

886899
def test_load_http_proxy_from_dict(self):
@@ -915,6 +928,7 @@ def test_load_http_proxy_from_dict(self):
915928
version=None,
916929
http_proxy=self.http_proxy,
917930
use_cloud_org_for_api_access=None,
931+
gaada=None
918932
)
919933

920934
def test_load_http_proxy_from_string(self):
@@ -949,6 +963,7 @@ def test_load_http_proxy_from_string(self):
949963
version=None,
950964
http_proxy=self.http_proxy,
951965
use_cloud_org_for_api_access=None,
966+
gaada=None
952967
)
953968

954969
def test_client_info_package_not_found(self):
@@ -1017,6 +1032,7 @@ def test_load_http_proxy_from_storage(self):
10171032
version=None,
10181033
http_proxy=self.http_proxy,
10191034
use_cloud_org_for_api_access=None,
1035+
gaada=None
10201036
)
10211037

10221038
def test_load_from_storage(self):
@@ -1058,6 +1074,7 @@ def test_load_from_storage(self):
10581074
version=None,
10591075
http_proxy=None,
10601076
use_cloud_org_for_api_access=None,
1077+
gaada=None
10611078
)
10621079

10631080
def test_load_from_storage_versioned(self):
@@ -1099,6 +1116,7 @@ def test_load_from_storage_versioned(self):
10991116
version="v4",
11001117
http_proxy=None,
11011118
use_cloud_org_for_api_access=None,
1119+
gaada=None
11021120
)
11031121

11041122
def test_load_from_storage_login_cid_int(self):
@@ -1142,6 +1160,7 @@ def test_load_from_storage_login_cid_int(self):
11421160
version=None,
11431161
http_proxy=None,
11441162
use_cloud_org_for_api_access=None,
1163+
gaada=None
11451164
)
11461165

11471166
def test_load_from_storage_custom_path(self):
@@ -1177,6 +1196,7 @@ def test_load_from_storage_custom_path(self):
11771196
version=None,
11781197
http_proxy=None,
11791198
use_cloud_org_for_api_access=None,
1199+
gaada=None
11801200
)
11811201

11821202
def test_load_from_storage_file_not_found(self):
@@ -1239,4 +1259,5 @@ def test_load_from_storage_service_account_config(self):
12391259
version=latest_version,
12401260
http_proxy=None,
12411261
use_cloud_org_for_api_access=None,
1262+
gaada=None
12421263
)

tests/config_test.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,17 @@ def test_load_from_dict_logging(self):
320320
}
321321
self.assertEqual(config.load_from_dict(config_data), config_data)
322322

323+
def test_load_from_dict_gaada(self):
324+
"""Should load "gaada" config from dict."""
325+
config_data = {
326+
**self.default_dict_config,
327+
**{
328+
"gaada": "1.6.0",
329+
},
330+
}
331+
result = config.load_from_dict(config_data)
332+
self.assertEqual(result["gaada"], "1.6.0")
333+
323334
def test_load_from_dict_secondary_service_account_keys(self):
324335
"""Should convert secondary keys to primary keys."""
325336
config_data = {
@@ -664,6 +675,35 @@ def test_disambiguate_string_bool_raises_value_error(self):
664675
def test_disambiguate_string_bool_raises_type_error(self):
665676
self.assertRaises(TypeError, config.disambiguate_string_bool, {})
666677

678+
def test_load_from_env_gaada(self):
679+
"""Should load ads api assistant flag from environment when specified"""
680+
environ = {
681+
**self.default_env_var_config,
682+
**{
683+
"GOOGLE_ADS_GAADA": "1.6.0",
684+
},
685+
}
686+
687+
with mock.patch("os.environ", environ):
688+
results = config.load_from_env()
689+
self.assertEqual(
690+
results["gaada"],
691+
"1.6.0",
692+
)
693+
694+
def test_load_from_yaml_file_gaada(self):
695+
"""Should load "gaada" config from a yaml."""
696+
self._create_mock_yaml({"gaada": "1.6.0"})
697+
698+
result = config.load_from_yaml_file()
699+
self.assertEqual(result["gaada"], "1.6.0")
700+
701+
def test_load_from_yaml_file_gaada_not_set(self):
702+
"""Should set "gaada" as False when not set."""
703+
self._create_mock_yaml({})
704+
result = config.load_from_yaml_file()
705+
self.assertEqual(result.get("gaada"), None)
706+
667707
def test_load_from_env_use_cloud_org_for_api_access(self):
668708
"""Should load api access flag from environment when specified"""
669709
environ = {

0 commit comments

Comments
 (0)