diff --git a/airbyte-integrations/connectors/source-bing-ads/acceptance-test-config.yml b/airbyte-integrations/connectors/source-bing-ads/acceptance-test-config.yml index 31ca98d25373..b09b9ebf71ef 100644 --- a/airbyte-integrations/connectors/source-bing-ads/acceptance-test-config.yml +++ b/airbyte-integrations/connectors/source-bing-ads/acceptance-test-config.yml @@ -40,6 +40,10 @@ acceptance_tests: bypass_reason: "Empty report; hourly data fetched is limited to 180 days" - name: account_impression_performance_report_hourly bypass_reason: "Empty report; hourly data fetched is limited to 180 days" + - name: app_install_ads + bypass_reason: "Can not populate; new campaign with link to app needed; feature is not available yet" + - name: app_install_ad_labels + bypass_reason: "Can not populate; depends on stream app_install_ads" timeout_seconds: 3600 full_refresh: tests: diff --git a/airbyte-integrations/connectors/source-bing-ads/integration_tests/configured_catalog.json b/airbyte-integrations/connectors/source-bing-ads/integration_tests/configured_catalog.json index 886beca85b64..426ddd613681 100644 --- a/airbyte-integrations/connectors/source-bing-ads/integration_tests/configured_catalog.json +++ b/airbyte-integrations/connectors/source-bing-ads/integration_tests/configured_catalog.json @@ -425,6 +425,66 @@ "sync_mode": "incremental", "cursor_field": ["TimePeriod"], "destination_sync_mode": "append" + }, + { + "stream": { + "name": "labels", + "json_schema": {}, + "supported_sync_modes": ["incremental", "full_refresh"] + }, + "sync_mode": "incremental", + "cursor_field": ["Modified Time"], + "destination_sync_mode": "append" + }, + { + "stream": { + "name": "app_install_ads", + "json_schema": {}, + "supported_sync_modes": ["incremental", "full_refresh"] + }, + "sync_mode": "incremental", + "cursor_field": ["TimePeriod"], + "destination_sync_mode": "append" + }, + { + "stream": { + "name": "app_install_ad_labels", + "json_schema": {}, + "supported_sync_modes": ["incremental", "full_refresh"] + }, + "sync_mode": "incremental", + "cursor_field": ["TimePeriod"], + "destination_sync_mode": "append" + }, + { + "stream": { + "name": "campaign_labels", + "json_schema": {}, + "supported_sync_modes": ["incremental", "full_refresh"] + }, + "sync_mode": "incremental", + "cursor_field": ["TimePeriod"], + "destination_sync_mode": "append" + }, + { + "stream": { + "name": "keyword_labels", + "json_schema": {}, + "supported_sync_modes": ["incremental", "full_refresh"] + }, + "sync_mode": "incremental", + "cursor_field": ["TimePeriod"], + "destination_sync_mode": "append" + }, + { + "stream": { + "name": "ad_group_labels", + "json_schema": {}, + "supported_sync_modes": ["incremental", "full_refresh"] + }, + "sync_mode": "incremental", + "cursor_field": ["TimePeriod"], + "destination_sync_mode": "append" } ] } diff --git a/airbyte-integrations/connectors/source-bing-ads/integration_tests/expected_records.jsonl b/airbyte-integrations/connectors/source-bing-ads/integration_tests/expected_records.jsonl index 1e6cc2f22a3a..d55e69dfb205 100644 --- a/airbyte-integrations/connectors/source-bing-ads/integration_tests/expected_records.jsonl +++ b/airbyte-integrations/connectors/source-bing-ads/integration_tests/expected_records.jsonl @@ -31,6 +31,10 @@ {"stream":"user_location_performance_report_daily","data":{"AccountName":"Daxtarity Inc.","AccountNumber":"F149GKV5","AccountId":180278106,"TimePeriod":"2021-06-22","CampaignName":"Test 2","CampaignId":413444833,"AdGroupName":"Airbyte","AdGroupId":1352400325389092,"Country":"Philippines","State":null,"MetroArea":null,"CurrencyCode":"USD","AdDistribution":"Search","Impressions":2,"Clicks":2,"Ctr":100.0,"AverageCpc":0.02,"Spend":0.04,"AveragePosition":0.0,"ProximityTargetLocation":null,"Radius":0,"Language":"English","City":null,"QueryIntentCountry":"Philippines","QueryIntentState":null,"QueryIntentCity":null,"QueryIntentDMA":null,"BidMatchType":"Broad","DeliveredMatchType":"Broad","Network":"Bing and Yahoo! search","TopVsOther":"Bing and Yahoo! search - Top","DeviceType":"Smartphone","DeviceOS":"Android","Assists":0,"Conversions":0,"ConversionRate":0.0,"Revenue":0.0,"ReturnOnAdSpend":0.0,"CostPerConversion":null,"CostPerAssist":null,"RevenuePerConversion":null,"RevenuePerAssist":null,"County":null,"PostalCode":null,"QueryIntentCounty":null,"QueryIntentPostalCode":null,"LocationId":149,"QueryIntentLocationId":149,"AllConversions":0,"AllRevenue":0.0,"AllConversionRate":0.0,"AllCostPerConversion":null,"AllReturnOnAdSpend":0.0,"AllRevenuePerConversion":null,"ViewThroughConversions":0,"Goal":null,"GoalType":null,"AbsoluteTopImpressionRatePercent":50.0,"TopImpressionRatePercent":100.0,"AverageCpm":20.0,"ConversionsQualified":0.0,"AllConversionsQualified":0.0,"ViewThroughConversionsQualified":null,"Neighborhood":null,"QueryIntentNeighborhood":null,"ViewThroughRevenue":0.0,"CampaignType":"Search & content","AssetGroupId":null,"AssetGroupName":null},"emitted_at":1698264446467} {"stream":"user_location_performance_report_weekly","data":{"AccountName":"Daxtarity Inc.","AccountNumber":"F149GKV5","AccountId":180278106,"TimePeriod":"2021-06-13","CampaignName":"Test 2","CampaignId":413444833,"AdGroupName":"Airbyte","AdGroupId":1352400325389092,"Country":"Indonesia","State":null,"MetroArea":null,"CurrencyCode":"USD","AdDistribution":"Search","Impressions":22,"Clicks":3,"Ctr":13.64,"AverageCpc":0.09,"Spend":0.26,"AveragePosition":0.0,"ProximityTargetLocation":null,"Radius":0,"Language":"Indonesian","City":null,"QueryIntentCountry":"Indonesia","QueryIntentState":null,"QueryIntentCity":null,"QueryIntentDMA":null,"BidMatchType":"Broad","DeliveredMatchType":"Broad","Network":"Bing and Yahoo! search","TopVsOther":"Bing and Yahoo! search - Top","DeviceType":"Computer","DeviceOS":"Windows","Assists":0,"Conversions":0,"ConversionRate":0.0,"Revenue":0.0,"ReturnOnAdSpend":0.0,"CostPerConversion":null,"CostPerAssist":null,"RevenuePerConversion":null,"RevenuePerAssist":null,"County":null,"PostalCode":null,"QueryIntentCounty":null,"QueryIntentPostalCode":null,"LocationId":91,"QueryIntentLocationId":91,"AllConversions":0,"AllRevenue":0.0,"AllConversionRate":0.0,"AllCostPerConversion":null,"AllReturnOnAdSpend":0.0,"AllRevenuePerConversion":null,"ViewThroughConversions":0,"Goal":null,"GoalType":null,"AbsoluteTopImpressionRatePercent":59.09,"TopImpressionRatePercent":100.0,"AverageCpm":11.82,"ConversionsQualified":0.0,"AllConversionsQualified":0.0,"ViewThroughConversionsQualified":null,"Neighborhood":null,"QueryIntentNeighborhood":null,"ViewThroughRevenue":0.0,"CampaignType":"Search & content","AssetGroupId":null,"AssetGroupName":null},"emitted_at":1698264629208} {"stream":"user_location_performance_report_monthly","data":{"AccountName":"Daxtarity Inc.","AccountNumber":"F149GKV5","AccountId":180278106,"TimePeriod":"2021-07-01","CampaignName":"Test 2","CampaignId":413444833,"AdGroupName":"Airbyte","AdGroupId":1352400325389092,"Country":"United States","State":"New York","MetroArea":"New York, NY","CurrencyCode":"USD","AdDistribution":"Search","Impressions":103,"Clicks":3,"Ctr":2.91,"AverageCpc":0.07,"Spend":0.21,"AveragePosition":0.0,"ProximityTargetLocation":null,"Radius":0,"Language":"English","City":"New York","QueryIntentCountry":"United States","QueryIntentState":null,"QueryIntentCity":null,"QueryIntentDMA":null,"BidMatchType":"Broad","DeliveredMatchType":"Phrase","Network":"Syndicated search partners","TopVsOther":"Syndicated search partners - Top","DeviceType":"Smartphone","DeviceOS":"Android","Assists":0,"Conversions":0,"ConversionRate":0.0,"Revenue":0.0,"ReturnOnAdSpend":0.0,"CostPerConversion":null,"CostPerAssist":null,"RevenuePerConversion":null,"RevenuePerAssist":null,"County":null,"PostalCode":null,"QueryIntentCounty":null,"QueryIntentPostalCode":null,"LocationId":59981,"QueryIntentLocationId":190,"AllConversions":0,"AllRevenue":0.0,"AllConversionRate":0.0,"AllCostPerConversion":null,"AllReturnOnAdSpend":0.0,"AllRevenuePerConversion":null,"ViewThroughConversions":0,"Goal":null,"GoalType":null,"AbsoluteTopImpressionRatePercent":3.88,"TopImpressionRatePercent":100.0,"AverageCpm":2.04,"ConversionsQualified":0.0,"AllConversionsQualified":0.0,"ViewThroughConversionsQualified":null,"Neighborhood":null,"QueryIntentNeighborhood":null,"ViewThroughRevenue":0.0,"CampaignType":"Search & content","AssetGroupId":null,"AssetGroupName":null},"emitted_at":1698264731347} -{"stream":"account_impression_performance_report_daily","data":{"AccountName":"Daxtarity Inc.","AccountNumber":"F149GKV5","AccountId":180278106,"TimePeriod":"2021-06-10","CurrencyCode":"USD","AdDistribution":"Search","Impressions":4,"Clicks":0,"Ctr":0.0,"AverageCpc":0.0,"Spend":0.0,"AveragePosition":0.0,"Conversions":0,"ConversionRate":null,"CostPerConversion":null,"LowQualityClicks":0,"LowQualityClicksPercent":null,"LowQualityImpressions":0,"LowQualityImpressionsPercent":0.0,"LowQualityConversions":0,"LowQualityConversionRate":null,"DeviceType":"Smartphone","ImpressionSharePercent":9.76,"ImpressionLostToBudgetPercent":0.0,"ImpressionLostToRankAggPercent":90.24,"PhoneImpressions":0,"PhoneCalls":0,"Ptr":null,"Network":"Bing and Yahoo! search","Assists":0,"Revenue":0.0,"ReturnOnAdSpend":null,"CostPerAssist":null,"RevenuePerConversion":null,"RevenuePerAssist":null,"AccountStatus":"Active","LowQualityGeneralClicks":0,"LowQualitySophisticatedClicks":0,"ExactMatchImpressionSharePercent":0.0,"ClickSharePercent":null,"AbsoluteTopImpressionSharePercent":8.33,"TopImpressionShareLostToRankPercent":88.89,"TopImpressionShareLostToBudgetPercent":0.0,"AbsoluteTopImpressionShareLostToRankPercent":91.67,"AbsoluteTopImpressionShareLostToBudgetPercent":0.0,"TopImpressionSharePercent":11.11,"AbsoluteTopImpressionRatePercent":75.0,"TopImpressionRatePercent":100.0,"AllConversions":0,"AllRevenue":0.0,"AllConversionRate":null,"AllCostPerConversion":null,"AllReturnOnAdSpend":null,"AllRevenuePerConversion":null,"ViewThroughConversions":0,"AudienceImpressionSharePercent":null,"AudienceImpressionLostToRankPercent":null,"AudienceImpressionLostToBudgetPercent":null,"AverageCpm":0.0,"ConversionsQualified":0.0,"LowQualityConversionsQualified":0.0,"AllConversionsQualified":0.0,"ViewThroughConversionsQualified":null,"ViewThroughRevenue":0.0,"VideoViews":0,"ViewThroughRate":0.0,"AverageCPV":null,"VideoViewsAt25Percent":0,"VideoViewsAt50Percent":0,"VideoViewsAt75Percent":0,"CompletedVideoViews":0,"VideoCompletionRate":null,"TotalWatchTimeInMS":0,"AverageWatchTimePerVideoView":null,"AverageWatchTimePerImpression":0.0,"Sales":0,"CostPerSale":null,"RevenuePerSale":null,"Installs":0,"CostPerInstall":null,"RevenuePerInstall":null},"emitted_at":1698339828071} -{"stream":"account_impression_performance_report_weekly","data":{"AccountName":"Daxtarity Inc.","AccountNumber":"F149GKV5","AccountId":180278106,"TimePeriod":"2021-06-20","CurrencyCode":"USD","AdDistribution":"Search","Impressions":27,"Clicks":3,"Ctr":11.11,"AverageCpc":0.09,"Spend":0.26,"AveragePosition":0.0,"Conversions":0,"ConversionRate":0.0,"CostPerConversion":null,"LowQualityClicks":0,"LowQualityClicksPercent":0.0,"LowQualityImpressions":19,"LowQualityImpressionsPercent":41.3,"LowQualityConversions":0,"LowQualityConversionRate":null,"DeviceType":"Smartphone","ImpressionSharePercent":1.05,"ImpressionLostToBudgetPercent":0.0,"ImpressionLostToRankAggPercent":98.95,"PhoneImpressions":0,"PhoneCalls":0,"Ptr":null,"Network":"Bing and Yahoo! search","Assists":0,"Revenue":0.0,"ReturnOnAdSpend":0.0,"CostPerAssist":null,"RevenuePerConversion":null,"RevenuePerAssist":null,"AccountStatus":"Active","LowQualityGeneralClicks":0,"LowQualitySophisticatedClicks":0,"ExactMatchImpressionSharePercent":0.0,"ClickSharePercent":0.86,"AbsoluteTopImpressionSharePercent":0.74,"TopImpressionShareLostToRankPercent":98.83,"TopImpressionShareLostToBudgetPercent":0.0,"AbsoluteTopImpressionShareLostToRankPercent":99.26,"AbsoluteTopImpressionShareLostToBudgetPercent":0.0,"TopImpressionSharePercent":1.17,"AbsoluteTopImpressionRatePercent":44.44,"TopImpressionRatePercent":70.37,"AllConversions":0,"AllRevenue":0.0,"AllConversionRate":0.0,"AllCostPerConversion":null,"AllReturnOnAdSpend":0.0,"AllRevenuePerConversion":null,"ViewThroughConversions":0,"AudienceImpressionSharePercent":null,"AudienceImpressionLostToRankPercent":null,"AudienceImpressionLostToBudgetPercent":null,"AverageCpm":9.63,"ConversionsQualified":0.0,"LowQualityConversionsQualified":0.0,"AllConversionsQualified":0.0,"ViewThroughConversionsQualified":null,"ViewThroughRevenue":0.0,"VideoViews":0,"ViewThroughRate":0.0,"AverageCPV":null,"VideoViewsAt25Percent":0,"VideoViewsAt50Percent":0,"VideoViewsAt75Percent":0,"CompletedVideoViews":0,"VideoCompletionRate":null,"TotalWatchTimeInMS":0,"AverageWatchTimePerVideoView":null,"AverageWatchTimePerImpression":0.0,"Sales":0,"CostPerSale":null,"RevenuePerSale":null,"Installs":0,"CostPerInstall":null,"RevenuePerInstall":null},"emitted_at":1698339846084} +{"stream":"account_impression_performance_report_daily","data":{"AccountName":"Daxtarity Inc.","AccountNumber":"F149GKV5","AccountId":180278106,"TimePeriod":"2021-06-10","CurrencyCode":"USD","AdDistribution":"Search","Impressions":4,"Clicks":0,"Ctr":0.0,"AverageCpc":0.0,"Spend":0.0,"AveragePosition":0.0,"Conversions":0,"ConversionRate":null,"CostPerConversion":null,"LowQualityClicks":0,"LowQualityClicksPercent":null,"LowQualityImpressions":0,"LowQualityImpressionsPercent":0.0,"LowQualityConversions":0,"LowQualityConversionRate":null,"DeviceType":"Smartphone","ImpressionSharePercent":null,"ImpressionLostToBudgetPercent":null,"ImpressionLostToRankAggPercent":null,"PhoneImpressions":0,"PhoneCalls":0,"Ptr":null,"Network":"Bing and Yahoo! search","Assists":0,"Revenue":0.0,"ReturnOnAdSpend":null,"CostPerAssist":null,"RevenuePerConversion":null,"RevenuePerAssist":null,"AccountStatus":"Active","LowQualityGeneralClicks":0,"LowQualitySophisticatedClicks":0,"ExactMatchImpressionSharePercent":null,"ClickSharePercent":null,"AbsoluteTopImpressionSharePercent":null,"TopImpressionShareLostToRankPercent":null,"TopImpressionShareLostToBudgetPercent":null,"AbsoluteTopImpressionShareLostToRankPercent":null,"AbsoluteTopImpressionShareLostToBudgetPercent":null,"TopImpressionSharePercent":null,"AbsoluteTopImpressionRatePercent":75.0,"TopImpressionRatePercent":100.0,"AllConversions":0,"AllRevenue":0.0,"AllConversionRate":null,"AllCostPerConversion":null,"AllReturnOnAdSpend":null,"AllRevenuePerConversion":null,"ViewThroughConversions":0,"AudienceImpressionSharePercent":null,"AudienceImpressionLostToRankPercent":null,"AudienceImpressionLostToBudgetPercent":null,"AverageCpm":0.0,"ConversionsQualified":0.0,"LowQualityConversionsQualified":0.0,"AllConversionsQualified":0.0,"ViewThroughConversionsQualified":null,"ViewThroughRevenue":0.0,"VideoViews":0,"ViewThroughRate":0.0,"AverageCPV":null,"VideoViewsAt25Percent":0,"VideoViewsAt50Percent":0,"VideoViewsAt75Percent":0,"CompletedVideoViews":0,"VideoCompletionRate":null,"TotalWatchTimeInMS":0,"AverageWatchTimePerVideoView":null,"AverageWatchTimePerImpression":0.0,"Sales":0,"CostPerSale":null,"RevenuePerSale":null,"Installs":0,"CostPerInstall":null,"RevenuePerInstall":null},"emitted_at":1698748305676} +{"stream":"account_impression_performance_report_weekly","data":{"AccountName":"Daxtarity Inc.","AccountNumber":"F149GKV5","AccountId":180278106,"TimePeriod":"2021-06-20","CurrencyCode":"USD","AdDistribution":"Search","Impressions":27,"Clicks":3,"Ctr":11.11,"AverageCpc":0.09,"Spend":0.26,"AveragePosition":0.0,"Conversions":0,"ConversionRate":0.0,"CostPerConversion":null,"LowQualityClicks":0,"LowQualityClicksPercent":0.0,"LowQualityImpressions":19,"LowQualityImpressionsPercent":41.3,"LowQualityConversions":0,"LowQualityConversionRate":null,"DeviceType":"Smartphone","ImpressionSharePercent":null,"ImpressionLostToBudgetPercent":null,"ImpressionLostToRankAggPercent":null,"PhoneImpressions":0,"PhoneCalls":0,"Ptr":null,"Network":"Bing and Yahoo! search","Assists":0,"Revenue":0.0,"ReturnOnAdSpend":0.0,"CostPerAssist":null,"RevenuePerConversion":null,"RevenuePerAssist":null,"AccountStatus":"Active","LowQualityGeneralClicks":0,"LowQualitySophisticatedClicks":0,"ExactMatchImpressionSharePercent":null,"ClickSharePercent":null,"AbsoluteTopImpressionSharePercent":null,"TopImpressionShareLostToRankPercent":null,"TopImpressionShareLostToBudgetPercent":null,"AbsoluteTopImpressionShareLostToRankPercent":null,"AbsoluteTopImpressionShareLostToBudgetPercent":null,"TopImpressionSharePercent":null,"AbsoluteTopImpressionRatePercent":44.44,"TopImpressionRatePercent":70.37,"AllConversions":0,"AllRevenue":0.0,"AllConversionRate":0.0,"AllCostPerConversion":null,"AllReturnOnAdSpend":0.0,"AllRevenuePerConversion":null,"ViewThroughConversions":0,"AudienceImpressionSharePercent":null,"AudienceImpressionLostToRankPercent":null,"AudienceImpressionLostToBudgetPercent":null,"AverageCpm":9.63,"ConversionsQualified":0.0,"LowQualityConversionsQualified":0.0,"AllConversionsQualified":0.0,"ViewThroughConversionsQualified":null,"ViewThroughRevenue":0.0,"VideoViews":0,"ViewThroughRate":0.0,"AverageCPV":null,"VideoViewsAt25Percent":0,"VideoViewsAt50Percent":0,"VideoViewsAt75Percent":0,"CompletedVideoViews":0,"VideoCompletionRate":null,"TotalWatchTimeInMS":0,"AverageWatchTimePerVideoView":null,"AverageWatchTimePerImpression":0.0,"Sales":0,"CostPerSale":null,"RevenuePerSale":null,"Installs":0,"CostPerInstall":null,"RevenuePerInstall":null},"emitted_at":1698757489131} {"stream":"account_impression_performance_report_monthly","data":{"AccountName":"Daxtarity Inc.","AccountNumber":"F149GKV5","AccountId":180278106,"TimePeriod":"2021-07-01","CurrencyCode":"USD","AdDistribution":"Search","Impressions":52,"Clicks":3,"Ctr":5.77,"AverageCpc":0.06,"Spend":0.19,"AveragePosition":0.0,"Conversions":0,"ConversionRate":0.0,"CostPerConversion":null,"LowQualityClicks":1,"LowQualityClicksPercent":25.0,"LowQualityImpressions":62,"LowQualityImpressionsPercent":54.39,"LowQualityConversions":0,"LowQualityConversionRate":0.0,"DeviceType":"Smartphone","ImpressionSharePercent":0.97,"ImpressionLostToBudgetPercent":0.09,"ImpressionLostToRankAggPercent":98.94,"PhoneImpressions":0,"PhoneCalls":0,"Ptr":null,"Network":"Bing and Yahoo! search","Assists":0,"Revenue":0.0,"ReturnOnAdSpend":0.0,"CostPerAssist":null,"RevenuePerConversion":null,"RevenuePerAssist":null,"AccountStatus":"Active","LowQualityGeneralClicks":0,"LowQualitySophisticatedClicks":1,"ExactMatchImpressionSharePercent":16.67,"ClickSharePercent":0.41,"AbsoluteTopImpressionSharePercent":0.37,"TopImpressionShareLostToRankPercent":98.86,"TopImpressionShareLostToBudgetPercent":0.04,"AbsoluteTopImpressionShareLostToRankPercent":99.58,"AbsoluteTopImpressionShareLostToBudgetPercent":0.04,"TopImpressionSharePercent":1.1,"AbsoluteTopImpressionRatePercent":32.69,"TopImpressionRatePercent":96.15,"AllConversions":0,"AllRevenue":0.0,"AllConversionRate":0.0,"AllCostPerConversion":null,"AllReturnOnAdSpend":0.0,"AllRevenuePerConversion":null,"ViewThroughConversions":0,"AudienceImpressionSharePercent":null,"AudienceImpressionLostToRankPercent":null,"AudienceImpressionLostToBudgetPercent":null,"AverageCpm":3.65,"ConversionsQualified":0.0,"LowQualityConversionsQualified":0.0,"AllConversionsQualified":0.0,"ViewThroughConversionsQualified":null,"ViewThroughRevenue":0.0,"VideoViews":0,"ViewThroughRate":0.0,"AverageCPV":null,"VideoViewsAt25Percent":0,"VideoViewsAt50Percent":0,"VideoViewsAt75Percent":0,"CompletedVideoViews":0,"VideoCompletionRate":null,"TotalWatchTimeInMS":0,"AverageWatchTimePerVideoView":null,"AverageWatchTimePerImpression":0.0,"Sales":0,"CostPerSale":null,"RevenuePerSale":null,"Installs":0,"CostPerInstall":null,"RevenuePerInstall":null},"emitted_at":1698339857372} +{"stream":"labels","data":{"Status":"Active","Id":10239203506495,"Client Id":null,"Modified Time":"04/27/2023 17:16:49.170","Description":null,"Label":"what a new label","Color":"#B0B20F","Account Id":180278106},"emitted_at":1698687185070} +{"stream":"campaign_labels","data":{"Status":"Active","Id":10239203506494,"Parent Id":413444833,"Campaign":null,"Client Id":null,"Modified Time":"04/27/2023 17:57:38.110","Account Id":180278106},"emitted_at":1698687214960} +{"stream":"keyword_labels","data":{"Status":"Active","Id":10239203506495,"Parent Id":84868925026027,"Client Id":null,"Modified Time":"04/27/2023 17:22:52.733","Account Id":180278106},"emitted_at":1698687224964} +{"stream":"ad_group_labels","data":{"Status":"Active","Id":10239203506494,"Parent Id":1350201453189474,"Campaign":null,"Ad Group":null,"Client Id":null,"Modified Time":"04/27/2023 18:00:14.970","Account Id":180278106},"emitted_at":1698751388918} diff --git a/airbyte-integrations/connectors/source-bing-ads/metadata.yaml b/airbyte-integrations/connectors/source-bing-ads/metadata.yaml index a991c8c554aa..c158a2afd5ad 100644 --- a/airbyte-integrations/connectors/source-bing-ads/metadata.yaml +++ b/airbyte-integrations/connectors/source-bing-ads/metadata.yaml @@ -16,7 +16,7 @@ data: connectorSubtype: api connectorType: source definitionId: 47f25999-dd5e-4636-8c39-e7cea2453331 - dockerImageTag: 1.4.0 + dockerImageTag: 1.5.0 dockerRepository: airbyte/source-bing-ads documentationUrl: https://docs.airbyte.com/integrations/sources/bing-ads githubIssueLabel: source-bing-ads diff --git a/airbyte-integrations/connectors/source-bing-ads/setup.py b/airbyte-integrations/connectors/source-bing-ads/setup.py index ad3e2ee1a218..c4fd433716e4 100644 --- a/airbyte-integrations/connectors/source-bing-ads/setup.py +++ b/airbyte-integrations/connectors/source-bing-ads/setup.py @@ -5,7 +5,7 @@ from setuptools import find_packages, setup -MAIN_REQUIREMENTS = ["airbyte-cdk", "bingads~=13.0.13", "urllib3<2.0"] +MAIN_REQUIREMENTS = ["airbyte-cdk", "bingads~=13.0.13", "urllib3<2.0", "pandas"] TEST_REQUIREMENTS = [ "requests-mock~=1.9.3", diff --git a/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/client.py b/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/client.py index 4d4dfd3c2176..b975d77050e0 100644 --- a/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/client.py +++ b/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/client.py @@ -1,13 +1,14 @@ # # Copyright (c) 2023 Airbyte, Inc., all rights reserved. # - +import os import socket import ssl import sys +import uuid from datetime import datetime, timedelta, timezone from functools import lru_cache -from typing import Any, Iterator, Mapping, Optional, Union +from typing import Any, Iterator, List, Mapping, Optional, Union from urllib.error import URLError import backoff @@ -19,10 +20,14 @@ from bingads.exceptions import OAuthTokenRequestException from bingads.service_client import ServiceClient from bingads.util import errorcode_of_exception +from bingads.v13.bulk import BulkServiceManager, DownloadParameters from bingads.v13.reporting.exceptions import ReportingDownloadException from bingads.v13.reporting.reporting_service_manager import ReportingServiceManager from suds import WebFault, sudsobject +FILE_TYPE = "Csv" +TIMEOUT_IN_MILLISECONDS = 3_600_000 + class Client: api_version: int = 13 @@ -54,7 +59,6 @@ def __init__( refresh_token: str = None, **kwargs: Mapping[str, Any], ) -> None: - self.authorization_data: Mapping[str, AuthorizationData] = {} self.refresh_token = refresh_token self.developer_token = developer_token @@ -236,3 +240,35 @@ def asdict(cls, suds_object: sudsobject.Object) -> Mapping[str, Any]: else: result[field] = val return result + + def _bulk_service_manager(self, customer_id: Optional[str] = None, account_id: Optional[str] = None): + return BulkServiceManager( + authorization_data=self._get_auth_data(customer_id, account_id), + poll_interval_in_milliseconds=5000, + environment=self.environment, + ) + + def get_bulk_entity( + self, + download_entities: List[str], + data_scope: List[str], + customer_id: Optional[str] = None, + account_id: Optional[str] = None, + start_date: Optional[str] = None, + ) -> str: + """ + Return path with zipped csv archive + """ + download_parameters = DownloadParameters( + # campaign_ids=None, + data_scope=data_scope, + download_entities=download_entities, + file_type=FILE_TYPE, + last_sync_time_in_utc=start_date, + result_file_directory=os.getcwd(), + result_file_name=str(uuid.uuid4()), + overwrite_result_file=True, # Set this value true if you want to overwrite the same file. + timeout_in_milliseconds=TIMEOUT_IN_MILLISECONDS, # You may optionally cancel the download after a specified time interval. + ) + bulk_service_manager = self._bulk_service_manager(customer_id=customer_id, account_id=account_id) + return bulk_service_manager.download_file(download_parameters) diff --git a/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/schemas/ad_group_labels.json b/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/schemas/ad_group_labels.json new file mode 100644 index 000000000000..6cb4d85c3353 --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/schemas/ad_group_labels.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "Account Id": { + "type": ["null", "integer"] + }, + "Ad Group": { + "type": ["null", "string"] + }, + "Campaign": { + "type": ["null", "string"] + }, + "Client Id": { + "type": ["null", "string"] + }, + "Id": { + "type": ["null", "integer"] + }, + "Parent Id": { + "type": ["null", "integer"] + }, + "Modified Time": { + "type": ["null", "string"] + }, + "Status": { + "type": ["null", "string"] + } + } +} diff --git a/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/schemas/app_install_ad_labels.json b/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/schemas/app_install_ad_labels.json new file mode 100644 index 000000000000..37bd0ad224a4 --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/schemas/app_install_ad_labels.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "Account Id": { + "type": ["null", "integer"] + }, + "Client Id": { + "type": ["null", "string"] + }, + "Id": { + "type": ["null", "integer"] + }, + "Parent Id": { + "type": ["null", "integer"] + }, + "Modified Time": { + "type": ["null", "string"] + }, + "Status": { + "type": ["null", "string"] + } + } +} diff --git a/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/schemas/app_install_ads.json b/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/schemas/app_install_ads.json new file mode 100644 index 000000000000..b409f06d62cd --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/schemas/app_install_ads.json @@ -0,0 +1,69 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "Ad Group": { + "type": ["null", "string"] + }, + "App Id": { + "type": ["null", "integer"] + }, + "Campaign": { + "type": ["null", "string"] + }, + "Client Id": { + "type": ["null", "integer"] + }, + "Custom Parameter": { + "type": ["null", "string"] + }, + "Device Preference": { + "type": ["null", "string"] + }, + "Editorial Appeal Status": { + "type": ["null", "string"] + }, + "Editorial Location": { + "type": ["null", "string"] + }, + "Editorial Reason Code": { + "type": ["null", "string"] + }, + "Editorial Status": { + "type": ["null", "string"] + }, + "Editorial Term": { + "type": ["null", "string"] + }, + "Final Url": { + "type": ["null", "string"] + }, + "Final Url Suffix": { + "type": ["null", "string"] + }, + "Id": { + "type": ["null", "integer"] + }, + "Modified Time": { + "type": ["null", "string"] + }, + "Parent Id": { + "type": ["null", "integer"] + }, + "Publisher Countries": { + "type": ["null", "string"] + }, + "Status": { + "type": ["null", "string"] + }, + "Text": { + "type": ["null", "string"] + }, + "Title": { + "type": ["null", "string"] + }, + "Tracking Template": { + "type": ["null", "string"] + } + } +} diff --git a/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/schemas/campaign_labels.json b/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/schemas/campaign_labels.json new file mode 100644 index 000000000000..e5fdf2701bef --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/schemas/campaign_labels.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "Account Id": { + "type": ["null", "integer"] + }, + "Campaign": { + "type": ["null", "string"] + }, + "Client Id": { + "type": ["null", "string"] + }, + "Id": { + "type": ["null", "integer"] + }, + "Parent Id": { + "type": ["null", "integer"] + }, + "Modified Time": { + "type": ["null", "string"] + }, + "Status": { + "type": ["null", "string"] + } + } +} diff --git a/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/schemas/keyword_labels.json b/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/schemas/keyword_labels.json new file mode 100644 index 000000000000..37bd0ad224a4 --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/schemas/keyword_labels.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "Account Id": { + "type": ["null", "integer"] + }, + "Client Id": { + "type": ["null", "string"] + }, + "Id": { + "type": ["null", "integer"] + }, + "Parent Id": { + "type": ["null", "integer"] + }, + "Modified Time": { + "type": ["null", "string"] + }, + "Status": { + "type": ["null", "string"] + } + } +} diff --git a/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/schemas/labels.json b/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/schemas/labels.json new file mode 100644 index 000000000000..a23858825318 --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/schemas/labels.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "Account Id": { + "type": ["null", "integer"] + }, + "Color": { + "type": ["null", "string"] + }, + "Client Id": { + "type": ["null", "string"] + }, + "Description": { + "type": ["null", "string"] + }, + "Id": { + "type": ["null", "integer"] + }, + "Label": { + "type": ["null", "string"] + }, + "Modified Time": { + "type": ["null", "string"] + }, + "Status": { + "type": ["null", "string"] + } + } +} diff --git a/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/source.py b/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/source.py index d0e130ac8807..904885e366d5 100644 --- a/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/source.py +++ b/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/source.py @@ -19,6 +19,7 @@ AccountPerformanceReportMonthly, AccountPerformanceReportWeekly, Accounts, + AdGroupLabels, AdGroupPerformanceReportDaily, AdGroupPerformanceReportHourly, AdGroupPerformanceReportMonthly, @@ -33,7 +34,10 @@ AgeGenderAudienceReportHourly, AgeGenderAudienceReportMonthly, AgeGenderAudienceReportWeekly, + AppInstallAdLabels, + AppInstallAds, BudgetSummaryReport, + CampaignLabels, CampaignPerformanceReportDaily, CampaignPerformanceReportHourly, CampaignPerformanceReportMonthly, @@ -43,10 +47,12 @@ GeographicPerformanceReportHourly, GeographicPerformanceReportMonthly, GeographicPerformanceReportWeekly, + KeywordLabels, KeywordPerformanceReportDaily, KeywordPerformanceReportHourly, KeywordPerformanceReportMonthly, KeywordPerformanceReportWeekly, + Labels, SearchQueryPerformanceReportDaily, SearchQueryPerformanceReportHourly, SearchQueryPerformanceReportMonthly, @@ -79,9 +85,15 @@ def streams(self, config: Mapping[str, Any]) -> List[Stream]: streams = [ Accounts(client, config), AdGroups(client, config), + AdGroupLabels(client, config), + AppInstallAds(client, config), + AppInstallAdLabels(client, config), Ads(client, config), Campaigns(client, config), BudgetSummaryReport(client, config), + Labels(client, config), + KeywordLabels(client, config), + CampaignLabels(client, config), ] reports = ( diff --git a/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/streams.py b/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/streams.py index b9aecdc7d10c..0ee6c5cd2026 100644 --- a/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/streams.py +++ b/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/streams.py @@ -1,18 +1,22 @@ # # Copyright (c) 2023 Airbyte, Inc., all rights reserved. # - - +import os import ssl import time from abc import ABC, abstractmethod -from typing import Any, Iterable, List, Mapping, MutableMapping, Optional, Union +from datetime import timezone +from typing import Any, Iterable, List, Mapping, MutableMapping, Optional, Tuple, Union from urllib.error import URLError +import pandas as pd +import pendulum from airbyte_cdk.models import SyncMode -from airbyte_cdk.sources.streams import Stream +from airbyte_cdk.sources.streams import IncrementalMixin, Stream +from airbyte_cdk.sources.utils.transform import TransformConfig, TypeTransformer from bingads.service_client import ServiceClient from bingads.v13.reporting.reporting_service_manager import ReportingServiceManager +from numpy import nan from source_bing_ads.client import Client from source_bing_ads.reports import ( ALL_CONVERSION_FIELDS, @@ -29,7 +33,7 @@ from suds import sudsobject -class BingAdsStream(Stream, ABC): +class BingAdsBaseStream(Stream, ABC): primary_key: Optional[Union[str, List[str], List[List[str]]]] = None def __init__(self, client: Client, config: Mapping[str, Any]) -> None: @@ -37,6 +41,8 @@ def __init__(self, client: Client, config: Mapping[str, Any]) -> None: self.client = client self.config = config + +class BingAdsStream(BingAdsBaseStream, ABC): @property @abstractmethod def data_field(self) -> str: @@ -145,6 +151,175 @@ def read_records( yield from [] +class BingAdsBulkStream(BingAdsBaseStream, IncrementalMixin, ABC): + + transformer: TypeTransformer = TypeTransformer(TransformConfig.DefaultSchemaNormalization) + cursor_field = "Modified Time" + _state = {} + + @property + @abstractmethod + def data_scope(self) -> List[str]: + """ + Defines scopes or types of data to download. Docs: https://learn.microsoft.com/en-us/advertising/bulk-service/datascope?view=bingads-13 + """ + + @property + @abstractmethod + def download_entities(self) -> List[str]: + """ + Defines the entities that should be downloaded. Docs: https://learn.microsoft.com/en-us/advertising/bulk-service/downloadentity?view=bingads-13 + """ + + def stream_slices( + self, + **kwargs: Mapping[str, Any], + ) -> Iterable[Optional[Mapping[str, Any]]]: + for account in Accounts(self.client, self.config).read_records(SyncMode.full_refresh): + yield {"account_id": account["Id"], "customer_id": account["ParentCustomerId"]} + + yield from [] + + @property + def state(self) -> Mapping[str, Any]: + return self._state + + @state.setter + def state(self, value: Mapping[str, Any]): + self._state.update({str(value["Account Id"]): {self.cursor_field: value[self.cursor_field]}}) + + def get_start_date(self, stream_state: Mapping[str, Any] = None, account_id: str = None): + """ + Start_date in request can be provided only if it is sooner than 30 days from now + """ + min_available_date = pendulum.now().subtract(days=30).astimezone(tz=timezone.utc) + start_date = self.client.reports_start_date + if stream_state.get(account_id, {}).get(self.cursor_field): + start_date = pendulum.from_format(stream_state[account_id][self.cursor_field], "MM/DD/YYYY HH:mm:ss.SSS") + return None if start_date < min_available_date else min_available_date + + def read_records( + self, + sync_mode: SyncMode, + stream_slice: Mapping[str, Any] = None, + stream_state: Mapping[str, Any] = None, + **kwargs: Mapping[str, Any], + ) -> Iterable[Mapping[str, Any]]: + stream_state = stream_state or {} + account_id = str(stream_slice.get("account_id")) if stream_slice else None + customer_id = str(stream_slice.get("customer_id")) if stream_slice else None + + report_file_path = self.client.get_bulk_entity( + data_scope=self.data_scope, + download_entities=self.download_entities, + customer_id=customer_id, + account_id=account_id, + start_date=self.get_start_date(stream_state, account_id), + ) + for record in self.read_with_chunks(report_file_path): + record = self.transform(record, stream_slice) + yield record + self.state = record + + yield from [] + + def read_with_chunks(self, path: str, chunk_size: int = 1024) -> Iterable[Tuple[int, Mapping[str, Any]]]: + try: + with open(path, "r") as data: + chunks = pd.read_csv(data, chunksize=chunk_size, iterator=True, dialect="unix", dtype=object) + for chunk in chunks: + chunk = chunk.replace({nan: None}).to_dict(orient="records") + for row in chunk: + if row.get("Type") not in ("Format Version", "Account"): + yield row + except pd.errors.EmptyDataError as e: + self.logger.info(f"Empty data received. {e}") + yield from [] + except IOError as ioe: + self.logger.fatal( + f"The IO/Error occurred while reading tmp data. Called: {path}. Stream: {self.name}", + ) + raise ioe + finally: + # remove binary tmp file, after data is read + os.remove(path) + + def transform(self, record: MutableMapping[str, Any], stream_slice: Mapping[str, Any], **kwargs) -> MutableMapping[str, Any]: + """ + Bing Ads Bulk API returns all available properties for all entities. + This method filter out only available properties. + """ + actual_record = {key: value for key, value in record.items() if key in self.get_json_schema()["properties"].keys()} + actual_record["Account Id"] = stream_slice.get("account_id") + return actual_record + + +class AppInstallAds(BingAdsBulkStream): + """ + https://learn.microsoft.com/en-us/advertising/bulk-service/app-install-ad?view=bingads-13 + """ + + data_scope = ["EntityData"] + download_entities = ["AppInstallAds"] + + primary_key = "Id" + + +class AppInstallAdLabels(BingAdsBulkStream): + """ + https://learn.microsoft.com/en-us/advertising/bulk-service/app-install-ad-label?view=bingads-13 + """ + + data_scope = ["EntityData"] + download_entities = ["AppInstallAdLabels"] + + primary_key = "Id" + + +class Labels(BingAdsBulkStream): + """ + https://learn.microsoft.com/en-us/advertising/bulk-service/label?view=bingads-13 + """ + + data_scope = ["EntityData"] + download_entities = ["Labels"] + + primary_key = "Id" + + +class KeywordLabels(BingAdsBulkStream): + """ + https://learn.microsoft.com/en-us/advertising/bulk-service/keyword-label?view=bingads-13 + """ + + data_scope = ["EntityData"] + download_entities = ["KeywordLabels"] + + primary_key = "Id" + + +class CampaignLabels(BingAdsBulkStream): + """ + https://learn.microsoft.com/en-us/advertising/bulk-service/campaign-label?view=bingads-13 + """ + + data_scope = ["EntityData"] + download_entities = ["CampaignLabels"] + + primary_key = "Id" + + +class AdGroupLabels(BingAdsBulkStream): + """ + https://learn.microsoft.com/en-us/advertising/bulk-service/ad-group-label?view=bingads-13 + """ + + data_scope = ["EntityData"] + download_entities = ["AdGroupLabels"] + + primary_key = "Id" + + class Accounts(BingAdsStream): """ Searches for accounts that the current authenticated user can access. diff --git a/airbyte-integrations/connectors/source-bing-ads/unit_tests/test_source.py b/airbyte-integrations/connectors/source-bing-ads/unit_tests/test_source.py index f0776ea9d015..ddcacdea9ace 100644 --- a/airbyte-integrations/connectors/source-bing-ads/unit_tests/test_source.py +++ b/airbyte-integrations/connectors/source-bing-ads/unit_tests/test_source.py @@ -2,7 +2,6 @@ # Copyright (c) 2023 Airbyte, Inc., all rights reserved. # -import json from unittest.mock import patch import pytest @@ -15,9 +14,14 @@ @pytest.fixture(name="config") def config_fixture(): """Generates streams settings from a config file""" - CONFIG_FILE = "secrets/config.json" - with open(CONFIG_FILE, "r") as f: - return json.loads(f.read()) + return { + "tenant_id": "common", + "developer_token": "fake_developer_token", + "refresh_token": "fake_refresh_token", + "client_id": "fake_client_id", + "reports_start_date": "2020-01-01", + "lookback_window": 0, + } @pytest.fixture(name="logger_mock") @@ -28,7 +32,7 @@ def logger_mock_fixture(): @patch.object(source_bing_ads.source, "Client") def test_streams_config_based(mocked_client, config): streams = SourceBingAds().streams(config) - assert len(streams) == 45 + assert len(streams) == 51 @patch.object(source_bing_ads.source, "Client") diff --git a/docs/integrations/sources/bing-ads.md b/docs/integrations/sources/bing-ads.md index f0cb4182124e..20d8dc01f654 100644 --- a/docs/integrations/sources/bing-ads.md +++ b/docs/integrations/sources/bing-ads.md @@ -74,10 +74,16 @@ The Bing Ads source connector supports the following streams. For more informati ### Basic streams -- [accounts](https://docs.microsoft.com/en-us/advertising/customer-management-service/searchaccounts?view=bingads-13) -- [ad_groups](https://docs.microsoft.com/en-us/advertising/campaign-management-service/getadgroupsbycampaignid?view=bingads-13) -- [ads](https://docs.microsoft.com/en-us/advertising/campaign-management-service/getadsbyadgroupid?view=bingads-13) -- [campaigns](https://docs.microsoft.com/en-us/advertising/campaign-management-service/getcampaignsbyaccountid?view=bingads-13) +- [Accounts](https://docs.microsoft.com/en-us/advertising/customer-management-service/searchaccounts?view=bingads-13) +- [Ad Groups](https://docs.microsoft.com/en-us/advertising/campaign-management-service/getadgroupsbycampaignid?view=bingads-13) +- [Ad Group Labels](https://learn.microsoft.com/en-us/advertising/bulk-service/ad-group-label?view=bingads-13) +- [Ads](https://docs.microsoft.com/en-us/advertising/campaign-management-service/getadsbyadgroupid?view=bingads-13) +- [App Install Ads](https://learn.microsoft.com/en-us/advertising/bulk-service/app-install-ad?view=bingads-13) +- [App Install Ad Labels](https://learn.microsoft.com/en-us/advertising/bulk-service/app-install-ad-label?view=bingads-13) +- [Campaigns](https://docs.microsoft.com/en-us/advertising/campaign-management-service/getcampaignsbyaccountid?view=bingads-13) +- [Campaign Labels](https://learn.microsoft.com/en-us/advertising/bulk-service/campaign-label?view=bingads-13) +- [Keyword Labels](https://learn.microsoft.com/en-us/advertising/bulk-service/keyword-label?view=bingads-13) +- [Labels](https://learn.microsoft.com/en-us/advertising/bulk-service/label?view=bingads-13) ### Report Streams @@ -135,7 +141,8 @@ The Bing Ads API limits the number of requests for all Microsoft Advertising cli | Version | Date | Pull Request | Subject | |:--------|:-----------|:---------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------| -| 1.4.0 | 2023-10-27 | [31885](https://github.com/airbytehq/airbyte/pull/31885) | Add new stream: `AccountImpressionPerformanceReport` (daily, hourly, weekly, monthly) | +| 1.5.0 | 2023-10-30 | [31952](https://github.com/airbytehq/airbyte/pull/31952) | Add new streams `Labels`, `App install ads`, `Keyword Labels`, `Campaign Labels`, `App Install Ad Labels`, `Ad Group Labels` | +| 1.4.0 | 2023-10-27 | [31885](https://github.com/airbytehq/airbyte/pull/31885) | Add new stream: `AccountImpressionPerformanceReport` (daily, hourly, weekly, monthly) | | 1.3.0 | 2023-10-26 | [31837](https://github.com/airbytehq/airbyte/pull/31837) | Add new stream: `UserLocationPerformanceReport` (daily, hourly, weekly, monthly) | | 1.2.0 | 2023-10-24 | [31783](https://github.com/airbytehq/airbyte/pull/31783) | Add new stream: `SearchQueryPerformanceReport` (daily, hourly, weekly, monthly) | | 1.1.0 | 2023-10-24 | [31712](https://github.com/airbytehq/airbyte/pull/31712) | Add new stream: `AgeGenderAudienceReport` (daily, hourly, weekly, monthly) |