Skip to content

Commit c7d7d70

Browse files
Use latest Google Ads API version by default (#71280)
1 parent 7790df7 commit c7d7d70

4 files changed

Lines changed: 40 additions & 13 deletions

File tree

providers/google/src/airflow/providers/google/ads/hooks/ads.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@
3131
from airflow.providers.google.common.hooks.base_google import get_field
3232

3333
if TYPE_CHECKING:
34-
from google.ads.googleads.v24.services.services.customer_service import CustomerServiceClient
35-
from google.ads.googleads.v24.services.services.google_ads_service import GoogleAdsServiceClient
36-
from google.ads.googleads.v24.services.services.google_ads_service.pagers import SearchPager
37-
from google.ads.googleads.v24.services.types.google_ads_service import GoogleAdsRow
34+
from collections.abc import Iterable
35+
36+
import proto
37+
from google.protobuf.message import Message as ProtobufMessage
3838

3939

4040
class GoogleAdsHook(BaseHook):
@@ -111,7 +111,8 @@ class GoogleAdsHook(BaseHook):
111111
112112
:param gcp_conn_id: The connection ID with the service account details.
113113
:param google_ads_conn_id: The connection ID with the details of Google Ads config.yaml file.
114-
:param api_version: The Google Ads API version to use.
114+
:param api_version: The Google Ads API version to use. If not set, the hook uses the default
115+
version of the installed ``google-ads`` library.
115116
"""
116117

117118
conn_name_attr = "google_ads_conn_id"
@@ -162,7 +163,7 @@ def __init__(
162163
self.google_ads_config: dict[str, Any] = {}
163164
self.authentication_method: Literal["service_account", "developer_token"] = "service_account"
164165

165-
def search(self, client_ids: list[str], query: str, **kwargs) -> list[GoogleAdsRow]:
166+
def search(self, client_ids: list[str], query: str, **kwargs) -> list[ProtobufMessage]:
166167
"""
167168
Pull data from the Google Ads API.
168169
@@ -185,7 +186,7 @@ def search(self, client_ids: list[str], query: str, **kwargs) -> list[GoogleAdsR
185186

186187
return data_native_pb
187188

188-
def search_proto_plus(self, client_ids: list[str], query: str, **kwargs) -> list[GoogleAdsRow]:
189+
def search_proto_plus(self, client_ids: list[str], query: str, **kwargs) -> list[proto.Message]:
189190
"""
190191
Pull data from the Google Ads API.
191192
@@ -226,9 +227,11 @@ def list_accessible_customers(self) -> list[str]:
226227
raise
227228

228229
@cached_property
229-
def _get_service(self) -> GoogleAdsServiceClient:
230+
def _get_service(self) -> Any:
230231
"""Connect and authenticate with the Google Ads API using a service account."""
231232
client = self._get_client
233+
if self.api_version is None:
234+
return client.get_service("GoogleAdsService")
232235
return client.get_service("GoogleAdsService", version=self.api_version)
233236

234237
@cached_property
@@ -247,7 +250,7 @@ def _get_client(self) -> GoogleAdsClient:
247250
raise
248251

249252
@cached_property
250-
def _get_customer_service(self) -> CustomerServiceClient:
253+
def _get_customer_service(self) -> Any:
251254
"""Connect and authenticate with the Google Ads API using a service account."""
252255
with NamedTemporaryFile("w", suffix=".json") as secrets_temp:
253256
self._get_config()
@@ -256,6 +259,8 @@ def _get_customer_service(self) -> CustomerServiceClient:
256259
self._update_config_with_secret(secrets_temp)
257260
try:
258261
client = GoogleAdsClient.load_from_dict(self.google_ads_config)
262+
if self.api_version is None:
263+
return client.get_service("CustomerService")
259264
return client.get_service("CustomerService", version=self.api_version)
260265
except GoogleAuthError as e:
261266
self.log.error("Google Auth Error: %s", e)
@@ -315,7 +320,7 @@ def _update_config_with_secret(self, secrets_temp: IO[str]) -> None:
315320

316321
self.google_ads_config["json_key_file_path"] = secrets_temp.name
317322

318-
def _search(self, client_ids: list[str], query: str, **kwargs) -> list[GoogleAdsRow]:
323+
def _search(self, client_ids: list[str], query: str, **kwargs) -> list[proto.Message]:
319324
"""
320325
Pull data from the Google Ads API.
321326
@@ -335,7 +340,7 @@ def _search(self, client_ids: list[str], query: str, **kwargs) -> list[GoogleAds
335340

336341
return self._extract_rows(iterators)
337342

338-
def _extract_rows(self, iterators: list[SearchPager]) -> list[GoogleAdsRow]:
343+
def _extract_rows(self, iterators: list[Iterable[proto.Message]]) -> list[proto.Message]:
339344
"""
340345
Convert Google Page Iterator (SearchPager) objects to Google Ads Rows.
341346

providers/google/src/airflow/providers/google/ads/operators/ads.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ class GoogleAdsListAccountsOperator(BaseOperator):
6262
If set as a sequence, the identities from the list must grant
6363
Service Account Token Creator IAM role to the directly preceding identity, with first
6464
account from the list granting this role to the originating account (templated).
65-
:param api_version: Optional Google Ads API version to use.
65+
:param api_version: Optional Google Ads API version to use. If not set, the operator uses the
66+
default version of the installed ``google-ads`` library.
6667
"""
6768

6869
template_fields: Sequence[str] = (

providers/google/src/airflow/providers/google/ads/transfers/ads_to_gcs.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ class GoogleAdsToGcsOperator(BaseOperator):
6161
If set as a sequence, the identities from the list must grant
6262
Service Account Token Creator IAM role to the directly preceding identity, with first
6363
account from the list granting this role to the originating account (templated).
64-
:param api_version: Optional Google Ads API version to use.
64+
:param api_version: Optional Google Ads API version to use. If not set, the operator uses the
65+
default version of the installed ``google-ads`` library.
6566
:param unwrap_single: If True, return the GCS URI as a string instead of a list.
6667
Defaults to False for backward compatibility.
6768
"""

providers/google/tests/unit/google/ads/hooks/test_ads.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,14 @@ def mock_hook(request):
7575
yield hook
7676

7777

78+
@pytest.fixture
79+
def mock_default_version_hook():
80+
with mock.patch(f"{BASEHOOK_PATCH_PATH}.get_connection") as conn:
81+
hook = GoogleAdsHook()
82+
conn.return_value.extra_dejson = EXTRAS_DEVELOPER_TOKEN
83+
yield hook
84+
85+
7886
@pytest.fixture(
7987
params=[
8088
{"input": EXTRAS_DEVELOPER_TOKEN, "expected_result": "developer_token"},
@@ -99,13 +107,25 @@ def test_get_customer_service(self, mock_client, mock_hook):
99107
client.assert_called_once_with(mock_hook.google_ads_config)
100108
client.return_value.get_service.assert_called_once_with("CustomerService", version=API_VERSION)
101109

110+
@mock.patch("airflow.providers.google.ads.hooks.ads.GoogleAdsClient")
111+
def test_get_customer_service_uses_client_default_version(self, mock_client, mock_default_version_hook):
112+
mock_default_version_hook._get_customer_service
113+
114+
mock_client.load_from_dict.return_value.get_service.assert_called_once_with("CustomerService")
115+
102116
@mock.patch("airflow.providers.google.ads.hooks.ads.GoogleAdsClient")
103117
def test_get_service(self, mock_client, mock_hook):
104118
mock_hook._get_service()
105119
client = mock_client.load_from_dict
106120
client.assert_called_once_with(mock_hook.google_ads_config)
107121
client.return_value.get_service.assert_called_once_with("GoogleAdsService", version=API_VERSION)
108122

123+
@mock.patch("airflow.providers.google.ads.hooks.ads.GoogleAdsClient")
124+
def test_get_service_uses_client_default_version(self, mock_client, mock_default_version_hook):
125+
mock_default_version_hook._get_service
126+
127+
mock_client.load_from_dict.return_value.get_service.assert_called_once_with("GoogleAdsService")
128+
109129
@mock.patch("airflow.providers.google.ads.hooks.ads.GoogleAdsClient")
110130
def test_search(self, mock_client, mock_hook):
111131
service = mock_client.load_from_dict.return_value.get_service.return_value

0 commit comments

Comments
 (0)