Source code for airflow.providers.google.ads.hooks.ads
## 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__future__importannotationsfromfunctoolsimportcached_propertyfromtempfileimportNamedTemporaryFilefromtypingimportIO,TYPE_CHECKING,Anyfromgoogle.ads.googleads.clientimportGoogleAdsClientfromgoogle.ads.googleads.errorsimportGoogleAdsExceptionfromgoogle.auth.exceptionsimportGoogleAuthErrorfromairflow.exceptionsimportAirflowExceptionfromairflow.hooks.baseimportBaseHookfromairflow.providers.google.common.hooks.base_googleimportget_fieldifTYPE_CHECKING:fromgoogle.ads.googleads.v15.services.services.customer_serviceimportCustomerServiceClientfromgoogle.ads.googleads.v15.services.services.google_ads_serviceimportGoogleAdsServiceClientfromgoogle.ads.googleads.v15.services.types.google_ads_serviceimportGoogleAdsRowfromgoogle.api_core.page_iteratorimportGRPCIterator
[docs]classGoogleAdsHook(BaseHook):"""Interact with Google Ads API. This hook requires two connections: - gcp_conn_id - provides service account details (like any other GCP connection) - google_ads_conn_id - which contains information from Google Ads config.yaml file in the ``extras``. Example of the ``extras``: .. code-block:: json { "google_ads_client": { "developer_token": "{{ INSERT_TOKEN }}", "json_key_file_path": null, "impersonated_email": "{{ INSERT_IMPERSONATED_EMAIL }}" } } The ``json_key_file_path`` is resolved by the hook using credentials from gcp_conn_id. https://developers.google.com/google-ads/api/docs/client-libs/python/oauth-service .. seealso:: For more information on how Google Ads authentication flow works take a look at: https://developers.google.com/google-ads/api/docs/client-libs/python/oauth-service .. seealso:: For more information on the Google Ads API, take a look at the API docs: https://developers.google.com/google-ads/api/docs/start :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. """
[docs]defsearch(self,client_ids:list[str],query:str,page_size:int=10000,**kwargs)->list[GoogleAdsRow]:"""Pull data from the Google Ads API. Native protobuf message instances are returned (those seen in versions prior to 10.0.0 of the google-ads library). This method is for backwards compatibility with older versions of the google_ads_hook. Check out the search_proto_plus method to get API results in the new default format of the google-ads library since v10.0.0 that behave more like conventional python object (using proto-plus-python). :param client_ids: Google Ads client ID(s) to query the API for. :param query: Google Ads Query Language query. :param page_size: Number of results to return per page. Max 10000. :return: Google Ads API response, converted to Google Ads Row objects. """data_proto_plus=self._search(client_ids,query,page_size,**kwargs)data_native_pb=[row._pbforrowindata_proto_plus]returndata_native_pb
[docs]defsearch_proto_plus(self,client_ids:list[str],query:str,page_size:int=10000,**kwargs)->list[GoogleAdsRow]:"""Pull data from the Google Ads API. Instances of proto-plus-python message are returned, which behave more like conventional Python objects. :param client_ids: Google Ads client ID(s) to query the API for. :param query: Google Ads Query Language query. :param page_size: Number of results to return per page. Max 10000. :return: Google Ads API response, converted to Google Ads Row objects """returnself._search(client_ids,query,page_size,**kwargs)
[docs]deflist_accessible_customers(self)->list[str]:"""List resource names of customers. The resulting list of customers is based on your OAuth credentials. The request returns a list of all accounts that you are able to act upon directly given your current credentials. This will not necessarily include all accounts within the account hierarchy; rather, it will only include accounts where your authenticated user has been added with admin or other rights in the account. ..seealso:: https://developers.google.com/google-ads/api/reference/rpc :return: List of names of customers """try:accessible_customers=self._get_customer_service.list_accessible_customers()returnaccessible_customers.resource_namesexceptGoogleAdsExceptionasex:forerrorinex.failure.errors:self.log.error('\tError with message "%s".',error.message)iferror.location:forfield_path_elementinerror.location.field_path_elements:self.log.error("\t\tOn field: %s",field_path_element.field_name)raise
@cached_propertydef_get_service(self)->GoogleAdsServiceClient:"""Connect and authenticate with the Google Ads API using a service account."""client=self._get_clientreturnclient.get_service("GoogleAdsService",version=self.api_version)@cached_propertydef_get_client(self)->GoogleAdsClient:withNamedTemporaryFile("w",suffix=".json")assecrets_temp:self._get_config()self._update_config_with_secret(secrets_temp)try:client=GoogleAdsClient.load_from_dict(self.google_ads_config)returnclientexceptGoogleAuthErrorase:self.log.error("Google Auth Error: %s",e)raise@cached_propertydef_get_customer_service(self)->CustomerServiceClient:"""Connect and authenticate with the Google Ads API using a service account."""withNamedTemporaryFile("w",suffix=".json")assecrets_temp:self._get_config()self._update_config_with_secret(secrets_temp)try:client=GoogleAdsClient.load_from_dict(self.google_ads_config)returnclient.get_service("CustomerService",version=self.api_version)exceptGoogleAuthErrorase:self.log.error("Google Auth Error: %s",e)raisedef_get_config(self)->None:"""Set up Google Ads config from Connection. This pulls the connections from db, and uses it to set up ``google_ads_config``. """conn=self.get_connection(self.google_ads_conn_id)if"google_ads_client"notinconn.extra_dejson:raiseAirflowException("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:"""Set up Google Cloud config secret from Connection. This pulls the connection, saves the contents to a temp file, and point the config to the path containing the secret. Note that the secret must be passed as a file path for Google Ads API. """extras=self.get_connection(self.gcp_conn_id).extra_dejsonsecret=get_field(extras,"keyfile_dict")ifnotsecret:raiseKeyError("secret_conn.extra_dejson does not contain keyfile_dict")secrets_temp.write(secret)secrets_temp.flush()self.google_ads_config["json_key_file_path"]=secrets_temp.namedef_search(self,client_ids:list[str],query:str,page_size:int=10000,**kwargs)->list[GoogleAdsRow]:"""Pull data from the Google Ads API. :param client_ids: Google Ads client ID(s) to query the API for. :param query: Google Ads Query Language query. :param page_size: Number of results to return per page. Max 10000. :return: Google Ads API response, converted to Google Ads Row objects """service=self._get_serviceiterators=[]forclient_idinclient_ids:iterator=service.search(request={"customer_id":client_id,"query":query,"page_size":page_size})iterators.append(iterator)self.log.info("Fetched Google Ads Iterators")returnself._extract_rows(iterators)def_extract_rows(self,iterators:list[GRPCIterator])->list[GoogleAdsRow]:"""Convert Google Page Iterator (GRPCIterator) objects to Google Ads Rows. :param iterators: List of Google Page Iterator (GRPCIterator) objects :return: API response for all clients in the form of Google Ads Row object(s) """try:self.log.info("Extracting data from returned Google Ads Iterators")return[rowforiteratoriniteratorsforrowiniterator]exceptGoogleAdsExceptionase:self.log.error("Request ID %s failed with status %s and includes the following errors:",e.request_id,e.error.code().name,)forerrorine.failure.errors:self.log.error("\tError with message: %s.",error.message)iferror.location:forfield_path_elementinerror.location.field_path_elements:self.log.error("\t\tOn field: %s",field_path_element.field_name)raise