Skip to content

Commit

Permalink
🐛 Source Google Ads: added handling for 401 error while parsing respo…
Browse files Browse the repository at this point in the history
…nse. added metrics.cost_micros to ad_groups stream (airbytehq#33494)
  • Loading branch information
darynaishchenko authored and jatinyadav-cc committed Feb 26, 2024
1 parent f2ccade commit 58b09b8
Show file tree
Hide file tree
Showing 9 changed files with 95 additions and 16 deletions.
Expand Up @@ -67,9 +67,58 @@ acceptance_tests:
campaign_budget:
- name: campaign_budget.recommended_budget_estimated_change_weekly_interactions
bypass_reason: "Value can be updated by Google Ads"
- name: metrics.all_conversions
bypass_reason: "Value can be updated by Google Ads"
- name: metrics.all_conversions_from_interactions_rate
bypass_reason: "Value can be updated by Google Ads"
- name: metrics.all_conversions_value
bypass_reason: "Value can be updated by Google Ads"
- name: metrics.conversions
bypass_reason: "Value can be updated by Google Ads"
- name: metrics.conversions_from_interactions_rate
bypass_reason: "Value can be updated by Google Ads"
- name: metrics.conversions_value
bypass_reason: "Value can be updated by Google Ads"
- name: metrics.cost_per_all_conversions
bypass_reason: "Value can be updated by Google Ads"
- name: metrics.cost_per_conversion
bypass_reason: "Value can be updated by Google Ads"
- name: metrics.value_per_all_conversions
bypass_reason: "Value can be updated by Google Ads"
- name: metrics.value_per_conversion
bypass_reason: "Value can be updated by Google Ads"
campaign:
- name: campaign.optimization_score
bypass_reason: "Value can be updated by Google Ads"
ad_group_ad_legacy:
- name: metrics.all_conversions_from_interactions_rate
bypass_reason: "Value can be updated by Google Ads"
- name: metrics.all_conversions_value
bypass_reason: "Value can be updated by Google Ads"
- name: metrics.all_conversions
bypass_reason: "Value can be updated by Google Ads"
- name: metrics.conversions_from_interactions_rate
bypass_reason: "Value can be updated by Google Ads"
- name: metrics.conversions_value
bypass_reason: "Value can be updated by Google Ads"
- name: metrics.conversions
bypass_reason: "Value can be updated by Google Ads"
- name: metrics.cost_per_all_conversions
bypass_reason: "Value can be updated by Google Ads"
- name: metrics.cost_per_conversion
bypass_reason: "Value can be updated by Google Ads"
- name: metrics.cost_per_current_model_attributed_conversion
bypass_reason: "Value can be updated by Google Ads"
- name: metrics.current_model_attributed_conversions_value
bypass_reason: "Value can be updated by Google Ads"
- name: metrics.current_model_attributed_conversions
bypass_reason: "Value can be updated by Google Ads"
- name: metrics.value_per_all_conversions
bypass_reason: "Value can be updated by Google Ads"
- name: metrics.value_per_conversion
bypass_reason: "Value can be updated by Google Ads"
- name: metrics.value_per_current_model_attributed_conversion
bypass_reason: "Value can be updated by Google Ads"
full_refresh:
tests:
- config_path: "secrets/config.json"
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Expand Up @@ -11,7 +11,7 @@ data:
connectorSubtype: api
connectorType: source
definitionId: 253487c0-2246-43ba-a21f-5116b20a2c50
dockerImageTag: 3.0.1
dockerImageTag: 3.0.2
dockerRepository: airbyte/source-google-ads
documentationUrl: https://docs.airbyte.com/integrations/sources/google-ads
githubIssueLabel: source-google-ads
Expand Down
Expand Up @@ -14,6 +14,9 @@
"ad_group.campaign": {
"type": ["null", "string"]
},
"metrics.cost_micros": {
"type": ["null", "integer"]
},
"ad_group.cpc_bid_micros": {
"type": ["null", "integer"]
},
Expand Down
Expand Up @@ -16,7 +16,7 @@
from google.ads.googleads.errors import GoogleAdsException
from google.ads.googleads.v15.services.services.google_ads_service.pagers import SearchPager
from google.ads.googleads.v15.services.types.google_ads_service import SearchGoogleAdsResponse
from google.api_core.exceptions import InternalServerError, ServerError, ServiceUnavailable, TooManyRequests
from google.api_core.exceptions import InternalServerError, ServerError, ServiceUnavailable, TooManyRequests, Unauthenticated

from .google_ads import GoogleAds, logger
from .models import CustomerModel
Expand Down Expand Up @@ -65,7 +65,7 @@ def read_records(self, sync_mode, stream_slice: Optional[Mapping[str, Any]] = No
customer_id = stream_slice["customer_id"]
try:
yield from self.request_records_job(customer_id, self.get_query(stream_slice), stream_slice)
except GoogleAdsException as exception:
except (GoogleAdsException, Unauthenticated) as exception:
traced_exception(exception, customer_id, self.CATCH_CUSTOMER_NOT_ENABLED_ERROR)
except TimeoutError as exception:
# Prevent sync failure
Expand Down
Expand Up @@ -19,6 +19,7 @@
from google.ads.googleads.v15.errors.types.authorization_error import AuthorizationErrorEnum
from google.ads.googleads.v15.errors.types.quota_error import QuotaErrorEnum
from google.ads.googleads.v15.errors.types.request_error import RequestErrorEnum
from google.api_core.exceptions import Unauthenticated
from source_google_ads.google_ads import logger


Expand Down Expand Up @@ -53,12 +54,19 @@ def is_error_type(error_value, target_enum_value):
return int(error_value) == int(target_enum_value)


def traced_exception(ga_exception: GoogleAdsException, customer_id: str, catch_disabled_customer_error: bool):
def traced_exception(ga_exception: Union[GoogleAdsException, Unauthenticated], customer_id: str, catch_disabled_customer_error: bool):
"""Add user-friendly message for GoogleAdsException"""
messages = []
raise_exception = AirbyteTracedException
failure_type = FailureType.config_error

if isinstance(ga_exception, Unauthenticated):
message = (
f"Authentication failed for the customer '{customer_id}'. "
f"Please try to Re-authenticate your credentials on set up Google Ads page."
)
raise raise_exception.from_exception(failure_type=failure_type, exc=ga_exception, message=message) from ga_exception

for error in ga_exception.failure.errors:
# Get error codes
authorization_error = error.error_code.authorization_error
Expand Down
Expand Up @@ -11,10 +11,10 @@
from google.ads.googleads.errors import GoogleAdsException
from google.ads.googleads.v15.errors.types.errors import ErrorCode, GoogleAdsError, GoogleAdsFailure
from google.ads.googleads.v15.errors.types.request_error import RequestErrorEnum
from google.api_core.exceptions import DataLoss, InternalServerError, ResourceExhausted, TooManyRequests
from google.api_core.exceptions import DataLoss, InternalServerError, ResourceExhausted, TooManyRequests, Unauthenticated
from grpc import RpcError
from source_google_ads.google_ads import GoogleAds
from source_google_ads.streams import ClickView, Customer
from source_google_ads.streams import ClickView, Customer, CustomerLabel

# EXPIRED_PAGE_TOKEN exception will be raised when page token has expired.
exception = GoogleAdsException(
Expand Down Expand Up @@ -261,3 +261,21 @@ def test_parse_response(mocker, customers, config):
]

assert output == expected_output


def test_read_records_unauthenticated(mocker, customers, config):
credentials = config["credentials"]
api = GoogleAds(credentials=credentials)

mocker.patch.object(api, "parse_single_result", side_effect=Unauthenticated(message="Unauthenticated"))

stream_config = dict(
api=api,
customers=customers,
)
stream = CustomerLabel(**stream_config)
with pytest.raises(AirbyteTracedException) as exc_info:
list(stream.read_records(SyncMode.full_refresh, {"customer_id": "customer_id"}))

assert exc_info.value.message == ("Authentication failed for the customer 'customer_id'. "
"Please try to Re-authenticate your credentials on set up Google Ads page.")
1 change: 1 addition & 0 deletions docs/integrations/sources/google-ads.md
Expand Up @@ -278,6 +278,7 @@ Due to a limitation in the Google Ads API which does not allow getting performan

| Version | Date | Pull Request | Subject |
|:---------|:-----------|:---------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------|
| `3.0.2` | 2024-01-08 | [33494](https://github.com/airbytehq/airbyte/pull/33494) | Add handling for 401 error while parsing response. Add `metrics.cost_micros` field to Ad Group stream. |
| `3.0.1` | 2023-12-26 | [33769](https://github.com/airbytehq/airbyte/pull/33769) | Run a read function in a separate thread to enforce a time limit for its execution |
| `3.0.0` | 2023-12-07 | [33120](https://github.com/airbytehq/airbyte/pull/33120) | Upgrade API version to v15 |
| `2.0.4` | 2023-11-10 | [32414](https://github.com/airbytehq/airbyte/pull/32414) | Add backoff strategy for read_records method |
Expand Down

0 comments on commit 58b09b8

Please sign in to comment.