From 3e07935eedf2ca9a600018ac780470ec294410a2 Mon Sep 17 00:00:00 2001 From: Dylan Sprayberry <28106103+dsprayberry@users.noreply.github.com> Date: Tue, 11 Apr 2023 09:56:21 -0400 Subject: [PATCH] TDL-22682 Update API version and FIELDS_UNACCEPTED_BY_API (#61) * Update API Version; Move and Update FIELDS_UNACCEPTED_BY_API * Get Tests Happy * Version bump and changelog Co-authored-by: Leslie VanDeMark --- CHANGELOG.md | 6 ++++++ setup.py | 2 +- tap_linkedin_ads/client.py | 5 +++-- tap_linkedin_ads/schema.py | 14 ++++++++++++++ tap_linkedin_ads/streams.py | 22 ++-------------------- tests/test_all_fields.py | 7 ++++--- 6 files changed, 30 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23b9b1a..3677b76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 2.1.0 + * Bump to API version `202302` + * Move and update `FIELDS_UNACCEPTED_BY_API` + * Tests updated for version bump + * [#61](https://github.com/singer-io/tap-linkedin-ads/pull/61) + ## 2.0.1 * Adds query tunneling to allow large number of accounts to be synced [#60](https://github.com/singer-io/tap-linkedin-ads/pull/60) diff --git a/setup.py b/setup.py index 75564fd..fb56d58 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ from setuptools import setup, find_packages setup(name='tap-linkedin-ads', - version='2.0.1', + version='2.1.0', description='Singer.io tap for extracting data from the LinkedIn Marketing Ads API API 2.0', author='jeff.huth@bytecode.io', classifiers=['Programming Language :: Python :: 3 :: Only'], diff --git a/tap_linkedin_ads/client.py b/tap_linkedin_ads/client.py index 67a5b3f..3248426 100644 --- a/tap_linkedin_ads/client.py +++ b/tap_linkedin_ads/client.py @@ -11,6 +11,7 @@ BASE_URL = 'https://api.linkedin.com/rest' LINKEDIN_TOKEN_URI = 'https://www.linkedin.com/oauth/v2/accessToken' INTROSPECTION_URI = 'https://www.linkedin.com/oauth/v2/introspectToken' +LINKEDIN_VERSION = '202302' # set default timeout of 300 seconds REQUEST_TIMEOUT = 300 @@ -293,7 +294,7 @@ def check_accounts(self, config): headers['User-Agent'] = self.__user_agent headers['Authorization'] = 'Bearer {}'.format(self.__access_token) headers['Accept'] = 'application/json' - headers['LinkedIn-Version'] = "202207" + headers['LinkedIn-Version'] = LINKEDIN_VERSION if config.get('accounts'): account_list = config['accounts'].replace(" ", "").split(",") @@ -348,7 +349,7 @@ def request(self, method, url=None, path=None, **kwargs): kwargs['headers'] = {} kwargs['headers']['Authorization'] = 'Bearer {}'.format(self.__access_token) kwargs['headers']['Accept'] = 'application/json' - kwargs['headers']['LinkedIn-Version'] = "202207" + kwargs['headers']['LinkedIn-Version'] = LINKEDIN_VERSION kwargs['headers']['Cache-Control'] = "no-cache" if self.__user_agent: diff --git a/tap_linkedin_ads/schema.py b/tap_linkedin_ads/schema.py index aed421b..8fba591 100644 --- a/tap_linkedin_ads/schema.py +++ b/tap_linkedin_ads/schema.py @@ -6,6 +6,15 @@ # Reference: # https://github.com/singer-io/getting-started/blob/master/docs/DISCOVERY_MODE.md#Metadata +# The following fields of ads_analytics (...by_campaign and ...by_creative) were previously in beta and are not available on +# API version 202302. Requesting them results in a 403. +# https://docs.microsoft.com/en-us/linkedin/marketing/integrations/ads-reporting/ads-reporting?view=li-lms-2023-02&tabs=http#accuracy +FIELDS_UNACCEPTED_BY_API = { + "average_daily_reach_metrics", + "average_previous_seven_day_reach_metrics", + "average_previous_thirty_day_reach_metrics", +} + def get_abs_path(path): return os.path.join(os.path.dirname(os.path.realpath(__file__)), path) @@ -17,6 +26,11 @@ def get_schemas(): schema_path = get_abs_path('schemas/{}.json'.format(stream_name)) with open(schema_path, encoding='utf-8') as file: schema = json.load(file) + + if stream_name in ('ad_analytics_by_campaign', 'ad_analytics_by_creative'): + for field in FIELDS_UNACCEPTED_BY_API: + metadata.delete(schema, 'properties', field) + schemas[stream_name] = schema mdata = metadata.new() diff --git a/tap_linkedin_ads/streams.py b/tap_linkedin_ads/streams.py index c80e56a..e13ee36 100644 --- a/tap_linkedin_ads/streams.py +++ b/tap_linkedin_ads/streams.py @@ -18,24 +18,7 @@ 'startAt', 'endAt', 'creative', - 'creativeId' -} - -# As mentioned here some fields of ads_analytics are currently in beta: -# https://docs.microsoft.com/en-us/linkedin/marketing/integrations/ads-reporting/ads-reporting?view=li-lms-2022-08&tabs=http#accuracy -FIELDS_UNACCEPTED_BY_API = { - "ad_analytics_by_creative": { - "averageDailyReachMetrics", - "averagePreviousSevenDayReachMetrics", - "averagePreviousThirtyDayReachMetrics", - "approximateUniqueImpressions" - }, - "ad_analytics_by_campaign": { - "averageDailyReachMetrics", - "averagePreviousSevenDayReachMetrics", - "averagePreviousThirtyDayReachMetrics", - "approximateUniqueImpressions" - } + 'creativeId', } def write_bookmark(state, value, stream_name): @@ -488,8 +471,7 @@ def sync_ad_analytics(self, client, catalog, last_datetime, date_window_size, pa # API accepts these fields in the parameter and returns its value in the response. valid_selected_fields = [snake_case_to_camel_case(field) for field in selected_fields(catalog.get_stream(self.tap_stream_id)) - if snake_case_to_camel_case(field) not in FIELDS_UNAVAILABLE_FOR_AD_ANALYTICS.union( - FIELDS_UNACCEPTED_BY_API.get(self.tap_stream_id, set()))] + if snake_case_to_camel_case(field) not in FIELDS_UNAVAILABLE_FOR_AD_ANALYTICS] # When testing the API, if the fields in `field` all return `0` then # the API returns its empty response. diff --git a/tests/test_all_fields.py b/tests/test_all_fields.py index f4a74c7..6f86039 100644 --- a/tests/test_all_fields.py +++ b/tests/test_all_fields.py @@ -27,19 +27,20 @@ "total_budget_ends_at", "total_budget", "reference_person_id", - "notified_on_new_features_enabled", }, "ad_analytics_by_creative": { + "average_daily_reach_metrics", "average_previous_seven_day_reach_metrics", "average_previous_thirty_day_reach_metrics", + #BUG: TDL-22692 "approximate_unique_impressions", - "average_daily_reach_metrics" }, "ad_analytics_by_campaign": { + "average_daily_reach_metrics" "average_previous_seven_day_reach_metrics", "average_previous_thirty_day_reach_metrics", + #BUG: TDL-22692 "approximate_unique_impressions", - "average_daily_reach_metrics" }, }