This is an automated email from the ASF dual-hosted git repository.
eladkal pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new c7d7d704680 Use latest Google Ads API version by default (#71280)
c7d7d704680 is described below
commit c7d7d704680d1a06510f869b9ad1aad793981ac9
Author: Ulada Zakharava <[email protected]>
AuthorDate: Sat Aug 8 18:16:20 2026 +0200
Use latest Google Ads API version by default (#71280)
---
.../src/airflow/providers/google/ads/hooks/ads.py | 27 +++++++++++++---------
.../airflow/providers/google/ads/operators/ads.py | 3 ++-
.../providers/google/ads/transfers/ads_to_gcs.py | 3 ++-
.../google/tests/unit/google/ads/hooks/test_ads.py | 20 ++++++++++++++++
4 files changed, 40 insertions(+), 13 deletions(-)
diff --git a/providers/google/src/airflow/providers/google/ads/hooks/ads.py
b/providers/google/src/airflow/providers/google/ads/hooks/ads.py
index 806107dbbf0..786358625e9 100644
--- a/providers/google/src/airflow/providers/google/ads/hooks/ads.py
+++ b/providers/google/src/airflow/providers/google/ads/hooks/ads.py
@@ -31,10 +31,10 @@ from airflow.providers.common.compat.sdk import
AirflowException, BaseHook
from airflow.providers.google.common.hooks.base_google import get_field
if TYPE_CHECKING:
- from google.ads.googleads.v24.services.services.customer_service import
CustomerServiceClient
- from google.ads.googleads.v24.services.services.google_ads_service import
GoogleAdsServiceClient
- from google.ads.googleads.v24.services.services.google_ads_service.pagers
import SearchPager
- from google.ads.googleads.v24.services.types.google_ads_service import
GoogleAdsRow
+ from collections.abc import Iterable
+
+ import proto
+ from google.protobuf.message import Message as ProtobufMessage
class GoogleAdsHook(BaseHook):
@@ -111,7 +111,8 @@ class GoogleAdsHook(BaseHook):
:param gcp_conn_id: The connection ID with the service account details.
:param google_ads_conn_id: The connection ID with the details of Google
Ads config.yaml file.
- :param api_version: The Google Ads API version to use.
+ :param api_version: The Google Ads API version to use. If not set, the
hook uses the default
+ version of the installed ``google-ads`` library.
"""
conn_name_attr = "google_ads_conn_id"
@@ -162,7 +163,7 @@ class GoogleAdsHook(BaseHook):
self.google_ads_config: dict[str, Any] = {}
self.authentication_method: Literal["service_account",
"developer_token"] = "service_account"
- def search(self, client_ids: list[str], query: str, **kwargs) ->
list[GoogleAdsRow]:
+ def search(self, client_ids: list[str], query: str, **kwargs) ->
list[ProtobufMessage]:
"""
Pull data from the Google Ads API.
@@ -185,7 +186,7 @@ class GoogleAdsHook(BaseHook):
return data_native_pb
- def search_proto_plus(self, client_ids: list[str], query: str, **kwargs)
-> list[GoogleAdsRow]:
+ def search_proto_plus(self, client_ids: list[str], query: str, **kwargs)
-> list[proto.Message]:
"""
Pull data from the Google Ads API.
@@ -226,9 +227,11 @@ class GoogleAdsHook(BaseHook):
raise
@cached_property
- def _get_service(self) -> GoogleAdsServiceClient:
+ def _get_service(self) -> Any:
"""Connect and authenticate with the Google Ads API using a service
account."""
client = self._get_client
+ if self.api_version is None:
+ return client.get_service("GoogleAdsService")
return client.get_service("GoogleAdsService", version=self.api_version)
@cached_property
@@ -247,7 +250,7 @@ class GoogleAdsHook(BaseHook):
raise
@cached_property
- def _get_customer_service(self) -> CustomerServiceClient:
+ def _get_customer_service(self) -> Any:
"""Connect and authenticate with the Google Ads API using a service
account."""
with NamedTemporaryFile("w", suffix=".json") as secrets_temp:
self._get_config()
@@ -256,6 +259,8 @@ class GoogleAdsHook(BaseHook):
self._update_config_with_secret(secrets_temp)
try:
client = GoogleAdsClient.load_from_dict(self.google_ads_config)
+ if self.api_version is None:
+ return client.get_service("CustomerService")
return client.get_service("CustomerService",
version=self.api_version)
except GoogleAuthError as e:
self.log.error("Google Auth Error: %s", e)
@@ -315,7 +320,7 @@ class GoogleAdsHook(BaseHook):
self.google_ads_config["json_key_file_path"] = secrets_temp.name
- def _search(self, client_ids: list[str], query: str, **kwargs) ->
list[GoogleAdsRow]:
+ def _search(self, client_ids: list[str], query: str, **kwargs) ->
list[proto.Message]:
"""
Pull data from the Google Ads API.
@@ -335,7 +340,7 @@ class GoogleAdsHook(BaseHook):
return self._extract_rows(iterators)
- def _extract_rows(self, iterators: list[SearchPager]) ->
list[GoogleAdsRow]:
+ def _extract_rows(self, iterators: list[Iterable[proto.Message]]) ->
list[proto.Message]:
"""
Convert Google Page Iterator (SearchPager) objects to Google Ads Rows.
diff --git a/providers/google/src/airflow/providers/google/ads/operators/ads.py
b/providers/google/src/airflow/providers/google/ads/operators/ads.py
index 67ffb09b9d4..9848fe6d3c0 100644
--- a/providers/google/src/airflow/providers/google/ads/operators/ads.py
+++ b/providers/google/src/airflow/providers/google/ads/operators/ads.py
@@ -62,7 +62,8 @@ class GoogleAdsListAccountsOperator(BaseOperator):
If set as a sequence, the identities from the list must grant
Service Account Token Creator IAM role to the directly preceding
identity, with first
account from the list granting this role to the originating account
(templated).
- :param api_version: Optional Google Ads API version to use.
+ :param api_version: Optional Google Ads API version to use. If not set,
the operator uses the
+ default version of the installed ``google-ads`` library.
"""
template_fields: Sequence[str] = (
diff --git
a/providers/google/src/airflow/providers/google/ads/transfers/ads_to_gcs.py
b/providers/google/src/airflow/providers/google/ads/transfers/ads_to_gcs.py
index 041f13ec7c0..426ab5776be 100644
--- a/providers/google/src/airflow/providers/google/ads/transfers/ads_to_gcs.py
+++ b/providers/google/src/airflow/providers/google/ads/transfers/ads_to_gcs.py
@@ -61,7 +61,8 @@ class GoogleAdsToGcsOperator(BaseOperator):
If set as a sequence, the identities from the list must grant
Service Account Token Creator IAM role to the directly preceding
identity, with first
account from the list granting this role to the originating account
(templated).
- :param api_version: Optional Google Ads API version to use.
+ :param api_version: Optional Google Ads API version to use. If not set,
the operator uses the
+ default version of the installed ``google-ads`` library.
:param unwrap_single: If True, return the GCS URI as a string instead of a
list.
Defaults to False for backward compatibility.
"""
diff --git a/providers/google/tests/unit/google/ads/hooks/test_ads.py
b/providers/google/tests/unit/google/ads/hooks/test_ads.py
index 1688ac83326..1e1f9727224 100644
--- a/providers/google/tests/unit/google/ads/hooks/test_ads.py
+++ b/providers/google/tests/unit/google/ads/hooks/test_ads.py
@@ -75,6 +75,14 @@ def mock_hook(request):
yield hook
[email protected]
+def mock_default_version_hook():
+ with mock.patch(f"{BASEHOOK_PATCH_PATH}.get_connection") as conn:
+ hook = GoogleAdsHook()
+ conn.return_value.extra_dejson = EXTRAS_DEVELOPER_TOKEN
+ yield hook
+
+
@pytest.fixture(
params=[
{"input": EXTRAS_DEVELOPER_TOKEN, "expected_result":
"developer_token"},
@@ -99,6 +107,12 @@ class TestGoogleAdsHook:
client.assert_called_once_with(mock_hook.google_ads_config)
client.return_value.get_service.assert_called_once_with("CustomerService",
version=API_VERSION)
+ @mock.patch("airflow.providers.google.ads.hooks.ads.GoogleAdsClient")
+ def test_get_customer_service_uses_client_default_version(self,
mock_client, mock_default_version_hook):
+ mock_default_version_hook._get_customer_service
+
+
mock_client.load_from_dict.return_value.get_service.assert_called_once_with("CustomerService")
+
@mock.patch("airflow.providers.google.ads.hooks.ads.GoogleAdsClient")
def test_get_service(self, mock_client, mock_hook):
mock_hook._get_service()
@@ -106,6 +120,12 @@ class TestGoogleAdsHook:
client.assert_called_once_with(mock_hook.google_ads_config)
client.return_value.get_service.assert_called_once_with("GoogleAdsService",
version=API_VERSION)
+ @mock.patch("airflow.providers.google.ads.hooks.ads.GoogleAdsClient")
+ def test_get_service_uses_client_default_version(self, mock_client,
mock_default_version_hook):
+ mock_default_version_hook._get_service
+
+
mock_client.load_from_dict.return_value.get_service.assert_called_once_with("GoogleAdsService")
+
@mock.patch("airflow.providers.google.ads.hooks.ads.GoogleAdsClient")
def test_search(self, mock_client, mock_hook):
service =
mock_client.load_from_dict.return_value.get_service.return_value