toddy86 commented on a change in pull request #7692: [AIRFLOW-6732] Add GoogleAdsHook and GoogleAdsToGcsOperator URL: https://github.com/apache/airflow/pull/7692#discussion_r391466638
########## File path: airflow/providers/google/marketing_platform/hooks/google_ads.py ########## @@ -0,0 +1,151 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +""" +This module contains Google Ad hook. +""" +from tempfile import NamedTemporaryFile +from typing import IO, Any, Dict, Generator, List + +from google.ads.google_ads.client import GoogleAdsClient +from google.ads.google_ads.errors import GoogleAdsException +from google.ads.google_ads.v2.types import GoogleAdsRow +from google.api_core.page_iterator import GRPCIterator +from google.auth.exceptions import GoogleAuthError + +from airflow import AirflowException +from airflow.hooks.base_hook import BaseHook + + +class GoogleAdsHook(BaseHook): + """ + Hook for the Google Ads API + + :param gcp_conn_id: The connection ID with the service account details. + :type gcp_conn_id: str + :param google_ads_conn_id: The connection ID with the details of Google Ads config.yaml file. + :type google_ads_conn_id: str + + :return: list of Google Ads Row object(s) + :rtype: list[GoogleAdsRow] + """ + + def __init__( + self, + gcp_conn_id: str = "google_cloud_default", + google_ads_conn_id: str = "google_ads_default", + ) -> None: + self.gcp_conn_id = gcp_conn_id + self.google_ads_conn_id = google_ads_conn_id + self.gcp_conn_id = gcp_conn_id + self.google_ads_config: Dict[str, Any] = {} + + def _get_service(self) -> GoogleAdsClient: + """Connects and authenticates with the Google Ads API using a service account""" + + try: + with NamedTemporaryFile("w", suffix=".json") as secrets_temp: + self._get_config() + self._update_config_with_secret(secrets_temp) + client = GoogleAdsClient.load_from_dict(self.google_ads_config) + return client.get_service("GoogleAdsService", version="v2") + + except GoogleAuthError as e: + self.log.error("Google Auth Error: %s", e) + raise + + def _get_config(self) -> None: + """ + Gets google ads connection from meta db and sets google_ads_config attribute with returned config file + """ + + conn = self.get_connection(self.google_ads_conn_id) + if "google_ads_client" not in conn.extra_dejson: + raise AirflowException("google_ads_client not found in extra field") + + self.google_ads_config = conn.extra_dejson["google_ads_client"] + + def _update_config_with_secret(self, secrets_temp: IO[str]) -> None: + """ + Gets GCP secret from connection and saves the contents to the temp file + Updates google ads config with file path of the temp file containing the secret + Note, the secret must be passed as a file path for Google Ads API + """ + + secret_conn = self.get_connection(self.gcp_conn_id) + secret = secret_conn.extra_dejson["extra__google_cloud_platform__keyfile_dict"] + secrets_temp.write(secret) + secrets_temp.flush() + + self.google_ads_config["path_to_private_key_file"] = secrets_temp.name + + def search( + self, client_ids: List[str], query: str, page_size: int = 10000, **kwargs + ) -> List[GoogleAdsRow]: + """ + Pulls data from the Google Ads API + Review comment: @michalslowikowski00 When I reviewed the docstrings of the other google.marketing_platform hooks I didn't note any links to API docs, so I didn't include it for consistency. But makes sense to me to have these in there, so i will add it in. ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: [email protected] With regards, Apache Git Services
