Skip to content

Commit 741e672

Browse files
Source Google Ads: make streams incremental (#10315)
* make streams as incremental * refactor Source * fix typing * fix primary_key * fix primary key in configured_catalog * bump the version * fix primary key in catalog * updated spec and def yaml Co-authored-by: auganbay <[email protected]>
1 parent 828ebc0 commit 741e672

File tree

11 files changed

+78
-43
lines changed

11 files changed

+78
-43
lines changed

airbyte-config/init/src/main/resources/seed/source_definitions.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@
252252
- name: Google Ads
253253
sourceDefinitionId: 253487c0-2246-43ba-a21f-5116b20a2c50
254254
dockerRepository: airbyte/source-google-ads
255-
dockerImageTag: 0.1.26
255+
dockerImageTag: 0.1.27
256256
documentationUrl: https://docs.airbyte.io/integrations/sources/google-ads
257257
icon: google-adwords.svg
258258
sourceType: api

airbyte-config/init/src/main/resources/seed/source_specs.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2306,7 +2306,7 @@
23062306
supportsNormalization: false
23072307
supportsDBT: false
23082308
supported_destination_sync_modes: []
2309-
- dockerImage: "airbyte/source-google-ads:0.1.26"
2309+
- dockerImage: "airbyte/source-google-ads:0.1.27"
23102310
spec:
23112311
documentationUrl: "https://docs.airbyte.com/integrations/sources/google-ads"
23122312
connectionSpecification:

airbyte-integrations/connectors/source-google-ads/Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ RUN pip install .
1313

1414
ENTRYPOINT ["python", "/airbyte/integration_code/main.py"]
1515

16-
LABEL io.airbyte.version=0.1.26
16+
LABEL io.airbyte.version=0.1.27
1717
LABEL io.airbyte.name=airbyte/source-google-ads

airbyte-integrations/connectors/source-google-ads/integration_tests/configured_catalog.json

+29-16
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"json_schema": {},
3131
"supported_sync_modes": ["full_refresh", "incremental"],
3232
"source_defined_cursor": true,
33+
"source_defined_primary_key": [["click_view.gclid"], ["segments.date"]],
3334
"default_cursor_field": ["segments.date"]
3435
},
3536
"sync_mode": "incremental",
@@ -100,41 +101,53 @@
100101
"stream": {
101102
"name": "ad_group_ads",
102103
"json_schema": {},
103-
"supported_sync_modes": ["full_refresh"],
104-
"source_defined_primary_key": [["ad_group_ad.ad.id"]]
104+
"supported_sync_modes": ["full_refresh", "incremental"],
105+
"source_defined_cursor": true,
106+
"source_defined_primary_key": [["ad_group_ad.ad.id"], ["segments.date"]],
107+
"default_cursor_field": ["segments.date"]
105108
},
106-
"sync_mode": "full_refresh",
107-
"destination_sync_mode": "overwrite"
109+
"sync_mode": "incremental",
110+
"destination_sync_mode": "overwrite",
111+
"cursor_field": ["segments.date"]
108112
},
109113
{
110114
"stream": {
111115
"name": "ad_groups",
112116
"json_schema": {},
113-
"supported_sync_modes": ["full_refresh"],
114-
"source_defined_primary_key": [["ad_group.id"]]
117+
"supported_sync_modes": ["full_refresh", "incremental"],
118+
"source_defined_cursor": true,
119+
"default_cursor_field": ["segments.date"],
120+
"source_defined_primary_key": [["ad_group.id"], ["segments.date"]]
115121
},
116-
"sync_mode": "full_refresh",
117-
"destination_sync_mode": "overwrite"
122+
"sync_mode": "incremental",
123+
"destination_sync_mode": "overwrite",
124+
"cursor_field": ["segments.date"]
118125
},
119126
{
120127
"stream": {
121128
"name": "accounts",
122129
"json_schema": {},
123-
"supported_sync_modes": ["full_refresh"],
124-
"source_defined_primary_key": [["customer.id"]]
130+
"supported_sync_modes": ["full_refresh", "incremental"],
131+
"source_defined_cursor": true,
132+
"default_cursor_field": ["segments.date"],
133+
"source_defined_primary_key": [["customer.id"], ["segments.date"]]
125134
},
126-
"sync_mode": "full_refresh",
127-
"destination_sync_mode": "overwrite"
135+
"sync_mode": "incremental",
136+
"destination_sync_mode": "overwrite",
137+
"cursor_field": ["segments.date"]
128138
},
129139
{
130140
"stream": {
131141
"name": "campaigns",
132142
"json_schema": {},
133-
"supported_sync_modes": ["full_refresh"],
134-
"source_defined_primary_key": [["campaign.id"]]
143+
"supported_sync_modes": ["full_refresh", "incremental"],
144+
"source_defined_cursor": true,
145+
"default_cursor_field": ["segments.date"],
146+
"source_defined_primary_key": [["campaign.id"], ["segments.date"]]
135147
},
136-
"sync_mode": "full_refresh",
137-
"destination_sync_mode": "overwrite"
148+
"sync_mode": "incremental",
149+
"destination_sync_mode": "overwrite",
150+
"cursor_field": ["segments.date"]
138151
},
139152
{
140153
"stream": {

airbyte-integrations/connectors/source-google-ads/source_google_ads/schemas/accounts.json

+4
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@
6464
},
6565
"customer.tracking_url_template": {
6666
"type": ["null", "string"]
67+
},
68+
"segments.date": {
69+
"type": ["null", "string"],
70+
"format": "date"
6771
}
6872
}
6973
}

airbyte-integrations/connectors/source-google-ads/source_google_ads/schemas/ad_group_ads.json

+4
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,10 @@
565565
},
566566
"ad_group_ad.status": {
567567
"type": ["null", "string"]
568+
},
569+
"segments.date": {
570+
"type": ["null", "string"],
571+
"format": "date"
568572
}
569573
}
570574
}

airbyte-integrations/connectors/source-google-ads/source_google_ads/schemas/ad_groups.json

+4
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@
9494
"items": {
9595
"type": "string"
9696
}
97+
},
98+
"segments.date": {
99+
"type": ["null", "string"],
100+
"format": "date"
97101
}
98102
}
99103
}

airbyte-integrations/connectors/source-google-ads/source_google_ads/schemas/campaigns.json

+4
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,10 @@
238238
},
239239
"campaign.video_brand_safety_suitability": {
240240
"type": ["null", "string"]
241+
},
242+
"segments.date": {
243+
"type": ["null", "string"],
244+
"format": "date"
241245
}
242246
}
243247
}

airbyte-integrations/connectors/source-google-ads/source_google_ads/source.py

+20-16
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,19 @@ def get_credentials(config: Mapping[str, Any]) -> Mapping[str, Any]:
4646
return credentials
4747

4848
@staticmethod
49-
def get_account_info(google_api) -> dict:
50-
accounts_streams = Accounts(api=google_api)
49+
def get_incremental_stream_config(google_api: GoogleAds, config: Mapping[str, Any], tz: Union[timezone, str] = "local"):
50+
incremental_stream_config = dict(
51+
api=google_api,
52+
conversion_window_days=config["conversion_window_days"],
53+
start_date=config["start_date"],
54+
time_zone=tz,
55+
end_date=config.get("end_date"),
56+
)
57+
return incremental_stream_config
58+
59+
def get_account_info(self, google_api: GoogleAds, config: Mapping[str, Any]) -> dict:
60+
incremental_stream_config = self.get_incremental_stream_config(google_api, config)
61+
accounts_streams = Accounts(**incremental_stream_config)
5162
for stream_slice in accounts_streams.stream_slices(sync_mode=SyncMode.full_refresh):
5263
return next(accounts_streams.read_records(sync_mode=SyncMode.full_refresh, stream_slice=stream_slice), {})
5364

@@ -74,7 +85,7 @@ def check_connection(self, logger: AirbyteLogger, config: Mapping[str, Any]) ->
7485
try:
7586
logger.info("Checking the config")
7687
google_api = GoogleAds(credentials=self.get_credentials(config), customer_id=config["customer_id"])
77-
account_info = self.get_account_info(google_api)
88+
account_info = self.get_account_info(google_api, config)
7889
is_manager_account = self.is_manager_account(account_info)
7990

8091
# Check custom query request validity by sending metric request with non-existant time window
@@ -95,22 +106,15 @@ def check_connection(self, logger: AirbyteLogger, config: Mapping[str, Any]) ->
95106

96107
def streams(self, config: Mapping[str, Any]) -> List[Stream]:
97108
google_api = GoogleAds(credentials=self.get_credentials(config), customer_id=config["customer_id"])
98-
account_info = self.get_account_info(google_api)
109+
account_info = self.get_account_info(google_api, config)
99110
time_zone = self.get_time_zone(account_info)
100-
end_date = config.get("end_date")
101-
incremental_stream_config = dict(
102-
api=google_api,
103-
conversion_window_days=config["conversion_window_days"],
104-
start_date=config["start_date"],
105-
time_zone=time_zone,
106-
end_date=end_date,
107-
)
111+
incremental_stream_config = self.get_incremental_stream_config(google_api, config, tz=time_zone)
108112

109113
streams = [
110-
AdGroupAds(api=google_api),
111-
AdGroups(api=google_api),
112-
Accounts(api=google_api),
113-
Campaigns(api=google_api),
114+
AdGroupAds(**incremental_stream_config),
115+
AdGroups(**incremental_stream_config),
116+
Accounts(**incremental_stream_config),
117+
Campaigns(**incremental_stream_config),
114118
ClickView(**incremental_stream_config),
115119
]
116120

airbyte-integrations/connectors/source-google-ads/source_google_ads/streams.py

+9-8
Original file line numberDiff line numberDiff line change
@@ -215,36 +215,36 @@ def get_query(self, stream_slice: Mapping[str, Any] = None) -> str:
215215
return query
216216

217217

218-
class Accounts(GoogleAdsStream):
218+
class Accounts(IncrementalGoogleAdsStream):
219219
"""
220220
Accounts stream: https://developers.google.com/google-ads/api/fields/v8/customer
221221
"""
222222

223-
primary_key = "customer.id"
223+
primary_key = ["customer.id", "segments.date"]
224224

225225

226-
class Campaigns(GoogleAdsStream):
226+
class Campaigns(IncrementalGoogleAdsStream):
227227
"""
228228
Campaigns stream: https://developers.google.com/google-ads/api/fields/v8/campaign
229229
"""
230230

231-
primary_key = "campaign.id"
231+
primary_key = ["campaign.id", "segments.date"]
232232

233233

234-
class AdGroups(GoogleAdsStream):
234+
class AdGroups(IncrementalGoogleAdsStream):
235235
"""
236236
AdGroups stream: https://developers.google.com/google-ads/api/fields/v8/ad_group
237237
"""
238238

239-
primary_key = "ad_group.id"
239+
primary_key = ["ad_group.id", "segments.date"]
240240

241241

242-
class AdGroupAds(GoogleAdsStream):
242+
class AdGroupAds(IncrementalGoogleAdsStream):
243243
"""
244244
AdGroups stream: https://developers.google.com/google-ads/api/fields/v8/ad_group_ad
245245
"""
246246

247-
primary_key = "ad_group_ad.ad.id"
247+
primary_key = ["ad_group_ad.ad.id", "segments.date"]
248248

249249

250250
class AccountPerformanceReport(IncrementalGoogleAdsStream):
@@ -306,5 +306,6 @@ class ClickView(IncrementalGoogleAdsStream):
306306
ClickView stream: https://developers.google.com/google-ads/api/reference/rpc/v8/ClickView
307307
"""
308308

309+
primary_key = ["click_view.gclid", "segments.date"]
309310
days_of_data_storage = 90
310311
range_days = 1

docs/integrations/sources/google-ads.md

+1
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ This source is constrained by whatever API limits are set for the Google Ads tha
102102

103103
| Version | Date | Pull Request | Subject |
104104
| :--- | :--- | :--- | :--- |
105+
| `0.1.27` | 2022-02-16 | [10315](https://github.com/airbytehq/airbyte/pull/10315) | Make `ad_group_ads` and other streams support incremental sync. |
105106
| `0.1.26` | 2022-02-11 | [10150](https://github.com/airbytehq/airbyte/pull/10150) | Add support for multiple customer IDs. |
106107
| `0.1.25` | 2022-02-04 | [9812](https://github.com/airbytehq/airbyte/pull/9812) | Handle `EXPIRED_PAGE_TOKEN` exception and retry with updated state. |
107108
| `0.1.24` | 2022-02-04 | [9996](https://github.com/airbytehq/airbyte/pull/9996) | Use Google Ads API version V9. |

0 commit comments

Comments
 (0)