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 7728fca85880..8a89d4511f33 100644 --- a/airbyte-integrations/connectors/source-bing-ads/acceptance-test-config.yml +++ b/airbyte-integrations/connectors/source-bing-ads/acceptance-test-config.yml @@ -25,6 +25,14 @@ acceptance_tests: expect_records: path: "integration_tests/expected_records.jsonl" empty_streams: + - name: product_dimension_performance_report_hourly + bypass_reason: "Test Account doesn't have Merchant Center configured to add Products, testing in integration test" + - name: product_dimension_performance_report_daily + bypass_reason: "Test Account doesn't have Merchant Center configured to add Products, testing in integration test" + - name: product_dimension_performance_report_weekly + bypass_reason: "Test Account doesn't have Merchant Center configured to add Products, testing in integration test" + - name: product_dimension_performance_report_monthly + bypass_reason: "Test Account doesn't have Merchant Center configured to add Products, testing in integration test" - name: account_performance_report_hourly bypass_reason: "Hourly reports are disabled, because sync is too long" - name: ad_group_performance_report_hourly @@ -94,6 +102,14 @@ acceptance_tests: expect_records: path: "integration_tests/expected_records_no_start_date.jsonl" empty_streams: + - name: product_dimension_performance_report_hourly + bypass_reason: "Test Account doesn't have Merchant Center configured to add Products, testing in integration test" + - name: product_dimension_performance_report_daily + bypass_reason: "Test Account doesn't have Merchant Center configured to add Products, testing in integration test" + - name: product_dimension_performance_report_weekly + bypass_reason: "Test Account doesn't have Merchant Center configured to add Products, testing in integration test" + - name: product_dimension_performance_report_monthly + bypass_reason: "Test Account doesn't have Merchant Center configured to add Products, testing in integration test" - 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 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 cdf122f10f7b..83f21c6ff1c4 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 @@ -18,6 +18,16 @@ "sync_mode": "full_refresh", "destination_sync_mode": "append" }, + { + "stream": { + "name": "budget", + "json_schema": {}, + "supported_sync_modes": ["full_refresh", "incremental"] + }, + "sync_mode": "incremental", + "cursor_field": ["Modified Time"], + "destination_sync_mode": "append" + }, { "stream": { "name": "campaigns", @@ -585,6 +595,46 @@ "sync_mode": "incremental", "cursor_field": ["TimePeriod"], "destination_sync_mode": "append" + }, + { + "stream": { + "name": "product_dimension_performance_report_hourly", + "json_schema": {}, + "supported_sync_modes": ["incremental", "full_refresh"] + }, + "sync_mode": "incremental", + "cursor_field": ["TimePeriod"], + "destination_sync_mode": "append" + }, + { + "stream": { + "name": "product_dimension_performance_report_daily", + "json_schema": {}, + "supported_sync_modes": ["incremental", "full_refresh"] + }, + "sync_mode": "incremental", + "cursor_field": ["TimePeriod"], + "destination_sync_mode": "append" + }, + { + "stream": { + "name": "product_dimension_performance_report_weekly", + "json_schema": {}, + "supported_sync_modes": ["incremental", "full_refresh"] + }, + "sync_mode": "incremental", + "cursor_field": ["TimePeriod"], + "destination_sync_mode": "append" + }, + { + "stream": { + "name": "product_dimension_performance_report_monthly", + "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 ba3379edc241..2da8a26461d3 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 @@ -27,3 +27,4 @@ {"stream":"user_location_performance_report_weekly","data":{"AccountName":"Airbyte","AccountNumber":"F149MJ18","AccountId":180519267,"TimePeriod":"2023-12-17","CampaignName":"Airbyte test","CampaignId":531016227,"AdGroupName":"keywords","AdGroupId":1356799861840328,"Country":"United Arab Emirates","State":"Dubai","MetroArea":null,"CurrencyCode":"USD","AdDistribution":"Audience","Impressions":1,"Clicks":0,"Ctr":0.0,"AverageCpc":0.0,"Spend":0.0,"AveragePosition":0.0,"ProximityTargetLocation":null,"Radius":0,"Language":"English","City":"Dubai","QueryIntentCountry":"United Arab Emirates","QueryIntentState":null,"QueryIntentCity":null,"QueryIntentDMA":null,"BidMatchType":"Broad","DeliveredMatchType":"Exact","Network":"Audience","TopVsOther":"Audience network","DeviceType":"Smartphone","DeviceOS":"Android","Assists":0,"Conversions":0,"ConversionRate":null,"Revenue":0.0,"ReturnOnAdSpend":null,"CostPerConversion":null,"CostPerAssist":null,"RevenuePerConversion":null,"RevenuePerAssist":null,"County":null,"PostalCode":null,"QueryIntentCounty":null,"QueryIntentPostalCode":null,"LocationId":154645,"QueryIntentLocationId":218,"AllConversions":0,"AllRevenue":0.0,"AllConversionRate":null,"AllCostPerConversion":null,"AllReturnOnAdSpend":null,"AllRevenuePerConversion":null,"ViewThroughConversions":0,"Goal":null,"GoalType":null,"AbsoluteTopImpressionRatePercent":0.0,"TopImpressionRatePercent":0.0,"AverageCpm":0.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":1704833830043} {"stream":"account_impression_performance_report_daily","data":{"AccountName":"Airbyte","AccountNumber":"F149MJ18","AccountId":180519267,"TimePeriod":"2023-12-18","CurrencyCode":"USD","AdDistribution":"Search","Impressions":22,"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":6,"LowQualityImpressionsPercent":21.43,"LowQualityConversions":0,"LowQualityConversionRate":null,"DeviceType":"Computer","ImpressionSharePercent":34.92,"ImpressionLostToBudgetPercent":1.59,"ImpressionLostToRankAggPercent":63.49,"PhoneImpressions":0,"PhoneCalls":0,"Ptr":null,"Network":"Syndicated search partners","Assists":0,"Revenue":0.0,"ReturnOnAdSpend":null,"CostPerAssist":null,"RevenuePerConversion":null,"RevenuePerAssist":null,"AccountStatus":"Active","LowQualityGeneralClicks":0,"LowQualitySophisticatedClicks":0,"ExactMatchImpressionSharePercent":5.26,"ClickSharePercent":null,"AbsoluteTopImpressionSharePercent":10.2,"TopImpressionShareLostToRankPercent":68.0,"TopImpressionShareLostToBudgetPercent":0.0,"AbsoluteTopImpressionShareLostToRankPercent":89.8,"AbsoluteTopImpressionShareLostToBudgetPercent":0.0,"TopImpressionSharePercent":32.0,"AbsoluteTopImpressionRatePercent":22.73,"TopImpressionRatePercent":72.73,"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":0.0,"TotalWatchTimeInMS":0,"AverageWatchTimePerVideoView":null,"AverageWatchTimePerImpression":0.0,"Sales":0,"CostPerSale":null,"RevenuePerSale":null,"Installs":0,"CostPerInstall":null,"RevenuePerInstall":null},"emitted_at":1704833886551} {"stream":"account_impression_performance_report_weekly","data":{"AccountName":"Airbyte","AccountNumber":"F149MJ18","AccountId":180519267,"TimePeriod":"2023-12-17","CurrencyCode":"USD","AdDistribution":"Search","Impressions":639,"Clicks":14,"Ctr":2.19,"AverageCpc":0.12,"Spend":1.74,"AveragePosition":0.0,"Conversions":0,"ConversionRate":0.0,"CostPerConversion":null,"LowQualityClicks":6,"LowQualityClicksPercent":30.0,"LowQualityImpressions":53,"LowQualityImpressionsPercent":7.66,"LowQualityConversions":0,"LowQualityConversionRate":0.0,"DeviceType":"Computer","ImpressionSharePercent":13.57,"ImpressionLostToBudgetPercent":17.96,"ImpressionLostToRankAggPercent":68.47,"PhoneImpressions":0,"PhoneCalls":0,"Ptr":null,"Network":"Syndicated search partners","Assists":0,"Revenue":0.0,"ReturnOnAdSpend":0.0,"CostPerAssist":null,"RevenuePerConversion":null,"RevenuePerAssist":null,"AccountStatus":"Active","LowQualityGeneralClicks":0,"LowQualitySophisticatedClicks":6,"ExactMatchImpressionSharePercent":17.65,"ClickSharePercent":1.28,"AbsoluteTopImpressionSharePercent":3.2,"TopImpressionShareLostToRankPercent":74.15,"TopImpressionShareLostToBudgetPercent":18.25,"AbsoluteTopImpressionShareLostToRankPercent":78.51,"AbsoluteTopImpressionShareLostToBudgetPercent":18.29,"TopImpressionSharePercent":7.6,"AbsoluteTopImpressionRatePercent":22.69,"TopImpressionRatePercent":53.99,"AllConversions":0,"AllRevenue":0.0,"AllConversionRate":0.0,"AllCostPerConversion":null,"AllReturnOnAdSpend":0.0,"AllRevenuePerConversion":null,"ViewThroughConversions":0,"AudienceImpressionSharePercent":null,"AudienceImpressionLostToRankPercent":null,"AudienceImpressionLostToBudgetPercent":null,"AverageCpm":2.72,"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":0.0,"TotalWatchTimeInMS":0,"AverageWatchTimePerVideoView":null,"AverageWatchTimePerImpression":0.0,"Sales":0,"CostPerSale":null,"RevenuePerSale":null,"Installs":0,"CostPerInstall":null,"RevenuePerInstall":null},"emitted_at":1704833908003} +{"stream": "budget", "data": {"Type": "Budget", "Status": "Active", "Id": 10239202868095, "Parent Id": 180519267, "Client Id": null, "Modified Time": "2024-02-28T17:52:08.900+00:00", "Budget Id": null, "Budget Name": "Test Shared Budget", "Budget": 2.0, "Budget Type": "DailyBudgetStandard", "Account Id": 180519267}, "emitted_at": 1709228203331} diff --git a/airbyte-integrations/connectors/source-bing-ads/integration_tests/expected_records_no_start_date.jsonl b/airbyte-integrations/connectors/source-bing-ads/integration_tests/expected_records_no_start_date.jsonl index d9be9afa0293..6547e46fa055 100644 --- a/airbyte-integrations/connectors/source-bing-ads/integration_tests/expected_records_no_start_date.jsonl +++ b/airbyte-integrations/connectors/source-bing-ads/integration_tests/expected_records_no_start_date.jsonl @@ -3,3 +3,4 @@ {"stream": "ad_group_labels", "data": {"Status": "Active", "Id": 10239203506495, "Parent Id": 1350201453189474, "Campaign": null, "Ad Group": null, "Client Id": null, "Modified Time": "2023-04-27T18:00:14.970+00:00", "Account Id": 180278106}, "emitted_at": 1701982478843} {"stream": "labels", "data": {"Status": "Active", "Id": 10239203506496, "Client Id": null, "Modified Time": "2023-04-27T17:16:53.430+00:00", "Description": null, "Label": "campaign label 2", "Color": "#D8558B", "Account Id": 180278106}, "emitted_at": 1701982532098} {"stream": "campaign_labels", "data": {"Status": "Active", "Id": 10239203506495, "Parent Id": 413732450, "Campaign": null, "Client Id": null, "Modified Time": "2023-04-27T17:57:21.497+00:00", "Account Id": 180278106}, "emitted_at": 1701982600348} +{"stream": "budget", "data": {"Type": "Budget", "Status": "Active", "Id": 10239202868095, "Parent Id": 180519267, "Client Id": null, "Modified Time": "2024-02-28T17:52:08.900+00:00", "Budget Id": null, "Budget Name": "Test Shared Budget", "Budget": 2.0, "Budget Type": "DailyBudgetStandard", "Account Id": 180519267}, "emitted_at": 1709228203331} diff --git a/airbyte-integrations/connectors/source-bing-ads/metadata.yaml b/airbyte-integrations/connectors/source-bing-ads/metadata.yaml index d0bf4b329ece..8d4a1a9bb186 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: 2.1.4 + dockerImageTag: 2.2.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/pyproject.toml b/airbyte-integrations/connectors/source-bing-ads/pyproject.toml index 17bb4f851c66..043177bac838 100644 --- a/airbyte-integrations/connectors/source-bing-ads/pyproject.toml +++ b/airbyte-integrations/connectors/source-bing-ads/pyproject.toml @@ -3,7 +3,7 @@ requires = [ "poetry-core>=1.0.0",] build-backend = "poetry.core.masonry.api" [tool.poetry] -version = "2.1.4" +version = "2.2.0" name = "source-bing-ads" description = "Source implementation for Bing Ads." authors = [ "Airbyte ",] diff --git a/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/bulk_streams.py b/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/bulk_streams.py index 440b0a3607a0..159aba01bf79 100644 --- a/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/bulk_streams.py +++ b/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/bulk_streams.py @@ -196,3 +196,12 @@ class AdGroupLabels(BingAdsBulkStream): data_scope = ["EntityData"] download_entities = ["AdGroupLabels"] + + +class Budget(BingAdsBulkStream): + """ + https://learn.microsoft.com/en-us/advertising/bulk-service/budget?view=bingads-13&viewFallbackFrom=bingads-13 + """ + + data_scope = ["EntityData"] + download_entities = ["Budgets"] diff --git a/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/report_streams.py b/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/report_streams.py index e1b98a4ec17c..f6e84836c099 100644 --- a/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/report_streams.py +++ b/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/report_streams.py @@ -670,6 +670,45 @@ class UserLocationPerformanceReportMonthly(UserLocationPerformanceReport): report_aggregation = "Monthly" +class ProductDimensionPerformanceReport(BingAdsReportingServicePerformanceStream, ABC): + """ + https://learn.microsoft.com/en-us/advertising/reporting-service/productdimensionperformancereportrequest?view=bingads-13 + """ + + report_name: str = "ProductDimensionPerformanceReport" + report_schema_name = "product_dimension_performance_report" + primary_key = None + + @property + def report_columns(self) -> Iterable[str]: + """AccountId is not in reporting columns for this report""" + properties = list(self.get_json_schema().get("properties", {}).keys()) + properties.remove("AccountId") + return properties + + def transform(self, record: MutableMapping[str, Any], stream_slice: Mapping[str, Any], **kwargs) -> MutableMapping[str, Any]: + record = super().transform(record, stream_slice) + record["AccountId"] = stream_slice["account_id"] + return record + + +class ProductDimensionPerformanceReportHourly(HourlyReportTransformerMixin, ProductDimensionPerformanceReport): + report_aggregation = "Hourly" + report_schema_name = "product_dimension_performance_report_hourly" + + +class ProductDimensionPerformanceReportDaily(ProductDimensionPerformanceReport): + report_aggregation = "Daily" + + +class ProductDimensionPerformanceReportWeekly(ProductDimensionPerformanceReport): + report_aggregation = "Weekly" + + +class ProductDimensionPerformanceReportMonthly(ProductDimensionPerformanceReport): + report_aggregation = "Monthly" + + class CustomReport(BingAdsReportingServicePerformanceStream, ABC): transformer: TypeTransformer = TypeTransformer(TransformConfig.DefaultSchemaNormalization) custom_report_columns = [] diff --git a/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/schemas/budget.json b/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/schemas/budget.json new file mode 100644 index 000000000000..a6af3367098b --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/schemas/budget.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "Account Id": { + "type": ["null", "integer"] + }, + "Type": { + "type": ["null", "string"] + }, + "Status": { + "type": ["null", "string"] + }, + "Id": { + "type": ["null", "integer"] + }, + "Parent Id": { + "type": ["null", "integer"] + }, + "Client Id": { + "type": ["null", "integer"] + }, + "Modified Time": { + "type": ["null", "string"], + "format": "date-time", + "airbyte_type": "timestamp_with_timezone" + }, + "Budget Id": { + "type": ["null", "integer"] + }, + "Budget Name": { + "type": ["null", "string"] + }, + "Budget": { + "type": ["null", "number"] + }, + "Budget Type": { + "type": ["null", "string"] + } + } +} diff --git a/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/schemas/product_dimension_performance_report.json b/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/schemas/product_dimension_performance_report.json new file mode 100644 index 000000000000..c4acd7baea44 --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/schemas/product_dimension_performance_report.json @@ -0,0 +1,259 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "AccountId": { + "type": ["null", "integer"] + }, + "TimePeriod": { + "type": ["null", "string"], + "format": "date" + }, + "AccountName": { + "type": ["null", "string"] + }, + "AccountNumber": { + "type": ["null", "string"] + }, + "AdGroupName": { + "type": ["null", "string"] + }, + "AdGroupId": { + "type": ["null", "integer"] + }, + "CampaignStatus": { + "type": ["null", "string"] + }, + "AccountStatus": { + "type": ["null", "string"] + }, + "AdGroupStatus": { + "type": ["null", "string"] + }, + "Network": { + "type": ["null", "string"] + }, + "AdId": { + "type": ["null", "integer"] + }, + "CampaignId": { + "type": ["null", "integer"] + }, + "CampaignName": { + "type": ["null", "string"] + }, + "CurrencyCode": { + "type": ["null", "string"] + }, + "DeviceType": { + "type": ["null", "string"] + }, + "Language": { + "type": ["null", "string"] + }, + "MerchantProductId": { + "type": ["null", "string"] + }, + "Title": { + "type": ["null", "string"] + }, + "Condition": { + "type": ["null", "string"] + }, + "Brand": { + "type": ["null", "string"] + }, + "Price": { + "type": ["null", "number"] + }, + "Impressions": { + "type": ["null", "integer"] + }, + "Clicks": { + "type": ["null", "integer"] + }, + "Ctr": { + "type": ["null", "number"] + }, + "AverageCpc": { + "type": ["null", "number"] + }, + "Spend": { + "type": ["null", "number"] + }, + "Conversions": { + "type": ["null", "integer"] + }, + "ConversionRate": { + "type": ["null", "number"] + }, + "Revenue": { + "type": ["null", "number"] + }, + "RevenuePerConversion": { + "type": ["null", "number"] + }, + "SellerName": { + "type": ["null", "string"] + }, + "OfferLanguage": { + "type": ["null", "string"] + }, + "CountryOfSale": { + "type": ["null", "string"] + }, + "AdStatus": { + "type": ["null", "string"] + }, + "AdDistribution": { + "type": ["null", "string"] + }, + "ClickTypeId": { + "type": ["null", "string"] + }, + "TotalClicksOnAdElements": { + "type": ["null", "number"] + }, + "ClickType": { + "type": ["null", "string"] + }, + "ReturnOnAdSpend": { + "type": ["null", "number"] + }, + "BidStrategyType": { + "type": ["null", "string"] + }, + "LocalStoreCode": { + "type": ["null", "string"] + }, + "StoreId": { + "type": ["null", "string"] + }, + "AssistedClicks": { + "type": ["null", "string"] + }, + "AssistedConversions": { + "type": ["null", "string"] + }, + "AllConversions": { + "type": ["null", "integer"] + }, + "AllRevenue": { + "type": ["null", "number"] + }, + "AllConversionRate": { + "type": ["null", "number"] + }, + "AllCostPerConversion": { + "type": ["null", "number"] + }, + "AllReturnOnAdSpend": { + "type": ["null", "number"] + }, + "AllRevenuePerConversion": { + "type": ["null", "number"] + }, + "CostPerConversion": { + "type": ["null", "number"] + }, + "ViewThroughConversions": { + "type": ["null", "integer"] + }, + "Goal": { + "type": ["null", "string"] + }, + "GoalType": { + "type": ["null", "string"] + }, + "ProductBought": { + "type": ["null", "string"] + }, + "QuantityBought": { + "type": ["null", "string"] + }, + "AverageCpm": { + "type": ["null", "number"] + }, + "ConversionsQualified": { + "type": ["null", "number"] + }, + "AssistedConversionsQualified": { + "type": ["null", "string"] + }, + "ViewThroughConversionsQualified": { + "type": ["null", "number"] + }, + "ProductBoughtTitle": { + "type": ["null", "string"] + }, + "GTIN": { + "type": ["null", "string"] + }, + "MPN": { + "type": ["null", "string"] + }, + "ViewThroughRevenue": { + "type": ["null", "number"] + }, + "Sales": { + "type": ["null", "integer"] + }, + "CostPerSale": { + "type": ["null", "number"] + }, + "RevenuePerSale": { + "type": ["null", "number"] + }, + "Installs": { + "type": ["null", "integer"] + }, + "CostPerInstall": { + "type": ["null", "number"] + }, + "RevenuePerInstall": { + "type": ["null", "number"] + }, + "CampaignType": { + "type": ["null", "string"] + }, + "AssetGroupId": { + "type": ["null", "string"] + }, + "AssetGroupName": { + "type": ["null", "string"] + }, + "AssetGroupStatus": { + "type": ["null", "string"] + }, + "CustomLabel0": { + "type": ["null", "string"] + }, + "CustomLabel1": { + "type": ["null", "string"] + }, + "CustomLabel2": { + "type": ["null", "string"] + }, + "CustomLabel3": { + "type": ["null", "string"] + }, + "CustomLabel4": { + "type": ["null", "string"] + }, + "ProductType1": { + "type": ["null", "string"] + }, + "ProductType2": { + "type": ["null", "string"] + }, + "ProductType3": { + "type": ["null", "string"] + }, + "ProductType4": { + "type": ["null", "string"] + }, + "ProductType5": { + "type": ["null", "string"] + } + } +} diff --git a/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/schemas/product_dimension_performance_report_hourly.json b/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/schemas/product_dimension_performance_report_hourly.json new file mode 100644 index 000000000000..81fd12773d99 --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/source_bing_ads/schemas/product_dimension_performance_report_hourly.json @@ -0,0 +1,260 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "AccountId": { + "type": ["null", "integer"] + }, + "TimePeriod": { + "type": ["null", "string"], + "format": "date-time", + "airbyte_type": "timestamp_with_timezone" + }, + "AccountName": { + "type": ["null", "string"] + }, + "AccountNumber": { + "type": ["null", "string"] + }, + "AdGroupName": { + "type": ["null", "string"] + }, + "AdGroupId": { + "type": ["null", "integer"] + }, + "CampaignStatus": { + "type": ["null", "string"] + }, + "AccountStatus": { + "type": ["null", "string"] + }, + "AdGroupStatus": { + "type": ["null", "string"] + }, + "Network": { + "type": ["null", "string"] + }, + "AdId": { + "type": ["null", "integer"] + }, + "CampaignId": { + "type": ["null", "integer"] + }, + "CampaignName": { + "type": ["null", "string"] + }, + "CurrencyCode": { + "type": ["null", "string"] + }, + "DeviceType": { + "type": ["null", "string"] + }, + "Language": { + "type": ["null", "string"] + }, + "MerchantProductId": { + "type": ["null", "string"] + }, + "Title": { + "type": ["null", "string"] + }, + "Condition": { + "type": ["null", "string"] + }, + "Brand": { + "type": ["null", "string"] + }, + "Price": { + "type": ["null", "number"] + }, + "Impressions": { + "type": ["null", "integer"] + }, + "Clicks": { + "type": ["null", "integer"] + }, + "Ctr": { + "type": ["null", "number"] + }, + "AverageCpc": { + "type": ["null", "number"] + }, + "Spend": { + "type": ["null", "number"] + }, + "Conversions": { + "type": ["null", "integer"] + }, + "ConversionRate": { + "type": ["null", "number"] + }, + "Revenue": { + "type": ["null", "number"] + }, + "RevenuePerConversion": { + "type": ["null", "number"] + }, + "SellerName": { + "type": ["null", "string"] + }, + "OfferLanguage": { + "type": ["null", "string"] + }, + "CountryOfSale": { + "type": ["null", "string"] + }, + "AdStatus": { + "type": ["null", "string"] + }, + "AdDistribution": { + "type": ["null", "string"] + }, + "ClickTypeId": { + "type": ["null", "string"] + }, + "TotalClicksOnAdElements": { + "type": ["null", "number"] + }, + "ClickType": { + "type": ["null", "string"] + }, + "ReturnOnAdSpend": { + "type": ["null", "number"] + }, + "BidStrategyType": { + "type": ["null", "string"] + }, + "LocalStoreCode": { + "type": ["null", "string"] + }, + "StoreId": { + "type": ["null", "string"] + }, + "AssistedClicks": { + "type": ["null", "string"] + }, + "AssistedConversions": { + "type": ["null", "string"] + }, + "AllConversions": { + "type": ["null", "integer"] + }, + "AllRevenue": { + "type": ["null", "number"] + }, + "AllConversionRate": { + "type": ["null", "number"] + }, + "AllCostPerConversion": { + "type": ["null", "number"] + }, + "AllReturnOnAdSpend": { + "type": ["null", "number"] + }, + "AllRevenuePerConversion": { + "type": ["null", "number"] + }, + "CostPerConversion": { + "type": ["null", "number"] + }, + "ViewThroughConversions": { + "type": ["null", "integer"] + }, + "Goal": { + "type": ["null", "string"] + }, + "GoalType": { + "type": ["null", "string"] + }, + "ProductBought": { + "type": ["null", "string"] + }, + "QuantityBought": { + "type": ["null", "string"] + }, + "AverageCpm": { + "type": ["null", "number"] + }, + "ConversionsQualified": { + "type": ["null", "number"] + }, + "AssistedConversionsQualified": { + "type": ["null", "string"] + }, + "ViewThroughConversionsQualified": { + "type": ["null", "number"] + }, + "ProductBoughtTitle": { + "type": ["null", "string"] + }, + "GTIN": { + "type": ["null", "string"] + }, + "MPN": { + "type": ["null", "string"] + }, + "ViewThroughRevenue": { + "type": ["null", "number"] + }, + "Sales": { + "type": ["null", "integer"] + }, + "CostPerSale": { + "type": ["null", "number"] + }, + "RevenuePerSale": { + "type": ["null", "number"] + }, + "Installs": { + "type": ["null", "integer"] + }, + "CostPerInstall": { + "type": ["null", "number"] + }, + "RevenuePerInstall": { + "type": ["null", "number"] + }, + "CampaignType": { + "type": ["null", "string"] + }, + "AssetGroupId": { + "type": ["null", "string"] + }, + "AssetGroupName": { + "type": ["null", "string"] + }, + "AssetGroupStatus": { + "type": ["null", "string"] + }, + "CustomLabel0": { + "type": ["null", "string"] + }, + "CustomLabel1": { + "type": ["null", "string"] + }, + "CustomLabel2": { + "type": ["null", "string"] + }, + "CustomLabel3": { + "type": ["null", "string"] + }, + "CustomLabel4": { + "type": ["null", "string"] + }, + "ProductType1": { + "type": ["null", "string"] + }, + "ProductType2": { + "type": ["null", "string"] + }, + "ProductType3": { + "type": ["null", "string"] + }, + "ProductType4": { + "type": ["null", "string"] + }, + "ProductType5": { + "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 37c2b9bc5d2b..7323a89c46c5 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 @@ -10,7 +10,16 @@ from airbyte_cdk.sources.streams import Stream from airbyte_cdk.utils import AirbyteTracedException from source_bing_ads.base_streams import Accounts, AdGroups, Ads, Campaigns -from source_bing_ads.bulk_streams import AdGroupLabels, AppInstallAdLabels, AppInstallAds, CampaignLabels, KeywordLabels, Keywords, Labels +from source_bing_ads.bulk_streams import ( + AdGroupLabels, + AppInstallAdLabels, + AppInstallAds, + Budget, + CampaignLabels, + KeywordLabels, + Keywords, + Labels, +) from source_bing_ads.client import Client from source_bing_ads.report_streams import ( # noqa: F401 AccountImpressionPerformanceReportDaily, @@ -56,6 +65,10 @@ KeywordPerformanceReportHourly, KeywordPerformanceReportMonthly, KeywordPerformanceReportWeekly, + ProductDimensionPerformanceReportDaily, + ProductDimensionPerformanceReportHourly, + ProductDimensionPerformanceReportMonthly, + ProductDimensionPerformanceReportWeekly, SearchQueryPerformanceReportDaily, SearchQueryPerformanceReportHourly, SearchQueryPerformanceReportMonthly, @@ -131,6 +144,7 @@ def streams(self, config: Mapping[str, Any]) -> List[Stream]: AppInstallAds(client, config), AppInstallAdLabels(client, config), Ads(client, config), + Budget(client, config), Campaigns(client, config), BudgetSummaryReport(client, config), Labels(client, config), @@ -150,6 +164,7 @@ def streams(self, config: Mapping[str, Any]) -> List[Stream]: "CampaignPerformanceReport", "CampaignImpressionPerformanceReport", "GeographicPerformanceReport", + "ProductDimensionPerformanceReport", "SearchQueryPerformanceReport", "UserLocationPerformanceReport", ) diff --git a/airbyte-integrations/connectors/source-bing-ads/unit_tests/integrations/base_test.py b/airbyte-integrations/connectors/source-bing-ads/unit_tests/integrations/base_test.py new file mode 100644 index 000000000000..6554e3684009 --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/unit_tests/integrations/base_test.py @@ -0,0 +1,60 @@ +# Copyright (c) 2023 Airbyte, Inc., all rights reserved. +import json +from pathlib import Path +from typing import Any, Dict, Optional, Tuple, Union +from unittest import TestCase +from unittest.mock import MagicMock, patch + +from airbyte_cdk.models import SyncMode +from airbyte_cdk.test.catalog_builder import CatalogBuilder +from airbyte_cdk.test.entrypoint_wrapper import EntrypointOutput, read +from airbyte_cdk.test.mock_http import HttpMocker +from airbyte_cdk.test.state_builder import StateBuilder +from airbyte_protocol.models import AirbyteStateMessage +from bingads.v13.bulk import BulkServiceManager +from bingads.v13.reporting.reporting_service_manager import ReportingServiceManager +from client_builder import build_request, response_with_status +from config_builder import ConfigBuilder +from source_bing_ads.source import SourceBingAds +from suds.transport.https import HttpAuthenticated +from suds_response_mock import mock_http_authenticated_send + + +class BaseTest(TestCase): + + @property + def service_manager(self) -> Union[ReportingServiceManager, BulkServiceManager]: + pass + + def _download_file(self, file: Optional[str] = None) -> Path: + pass + + @property + def _config(self) -> dict[str, Any]: + return ConfigBuilder().build() + + def _state(self, file: str, stream_name: str) -> list[AirbyteStateMessage]: + state_file = Path(__file__).parent.parent / f"resource/state/{file}.json" + with open(state_file, "r") as f: + state = json.loads(f.read()) + return StateBuilder().with_stream_state(stream_name, state).build() + + def auth_client(self, http_mocker: HttpMocker) -> None: + http_mocker.post( + request=build_request(self._config), + responses=response_with_status("oauth", 200) + ) + + def read_stream( + self, + stream_name: str, + sync_mode: SyncMode, + config: Dict[str, Any], + stream_data_file: str = None, + state: Optional[Dict[str, Any]] = None, + expecting_exception: bool = False, + ) -> Tuple[EntrypointOutput, MagicMock]: + with patch.object(HttpAuthenticated, "send", mock_http_authenticated_send): + with patch.object(self.service_manager, "download_file", return_value=self._download_file(stream_data_file)) as service_call_mock: + catalog = CatalogBuilder().with_stream(stream_name, sync_mode).build() + return read(SourceBingAds(), config, catalog, state, expecting_exception), service_call_mock diff --git a/airbyte-integrations/connectors/source-bing-ads/unit_tests/integrations/client_builder.py b/airbyte-integrations/connectors/source-bing-ads/unit_tests/integrations/client_builder.py new file mode 100644 index 000000000000..5efab804e556 --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/unit_tests/integrations/client_builder.py @@ -0,0 +1,31 @@ +# Copyright (c) 2023 Airbyte, Inc., all rights reserved. + +import json +from typing import Any, Dict + +from airbyte_cdk.test.mock_http import HttpRequest, HttpResponse +from airbyte_cdk.test.mock_http.response_builder import find_template + + +def response_with_status(resource: str, status_code: int) -> HttpResponse: + return HttpResponse(json.dumps(find_template(resource, __file__)), status_code) + + +def build_request(config: Dict[str, Any]) -> HttpRequest: + body = ( + f"client_id={config['client_id']}" + f"&client_secret={config['client_secret']}" + "&grant_type=refresh_token" + f"&refresh_token={config['refresh_token']}" + "&environment=production&scope=https%3A%2F%2Fads.microsoft.com%2Fmsads.manage+offline_access&oauth_scope=msads.manage" + f"&tenant={config['tenant_id']}" + ) + + return HttpRequest( + url="https://login.microsoftonline.com/common/oauth2/v2.0/token", + query_params={}, + body=body, + headers={ + "Content-Type": "application/x-www-form-urlencoded" + }, + ) diff --git a/airbyte-integrations/connectors/source-bing-ads/unit_tests/integrations/config_builder.py b/airbyte-integrations/connectors/source-bing-ads/unit_tests/integrations/config_builder.py new file mode 100644 index 000000000000..a31eaff68e6f --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/unit_tests/integrations/config_builder.py @@ -0,0 +1,44 @@ +# Copyright (c) 2023 Airbyte, Inc., all rights reserved. + +import datetime +from typing import Any, Dict, List + +from airbyte_cdk.test.mock_http.response_builder import find_template + +TENNANT_ID = "common" +DEVELOPER_TOKEN = "test-token" +REFRESH_TOKEN = "test-refresh-token" +CLIENT_ID = "test-client-id" +CLIENT_SECRET = "test-client-secret" +LOOKBACK_WINDOW = 0 + + +class ConfigBuilder: + def __init__(self) -> None: + oauth_fixture: Dict[str, Any] = find_template("oauth", __file__) + self._access_token: str = oauth_fixture["access_token"] + self._refresh_token: str = oauth_fixture["refresh_token"] + self._client_id: str = CLIENT_ID + self._client_secret: str = CLIENT_SECRET + self._refresh_token: str = REFRESH_TOKEN + self._developer_token: str = DEVELOPER_TOKEN + self._tenant_id: str = TENNANT_ID + self._report_start_date: str = None + self._lookback_window: int = LOOKBACK_WINDOW + + def with_reports_start_date(self, start_date: str) -> "ConfigBuilder": + self._report_start_date = start_date + return self + + def build(self) -> Dict[str, Any]: + config = { + "tenant_id": self._tenant_id, + "developer_token": self._developer_token, + "refresh_token": self._refresh_token, + "client_id": self._client_id, + "client_secret": self._client_secret, + "lookback_window": self._lookback_window, + } + if self._report_start_date: + config["reports_start_date"] = self._report_start_date + return config diff --git a/airbyte-integrations/connectors/source-bing-ads/unit_tests/integrations/suds_response_mock.py b/airbyte-integrations/connectors/source-bing-ads/unit_tests/integrations/suds_response_mock.py new file mode 100644 index 000000000000..224a810a402a --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/unit_tests/integrations/suds_response_mock.py @@ -0,0 +1,146 @@ +# Copyright (c) 2023 Airbyte, Inc., all rights reserved. + +from suds.transport import Reply, Request +from suds.transport.https import HttpAuthenticated + +SEARCH_ACCOUNTS_RESPONSE = b""" + + 6f0a329e-4cb4-4c79-9c08-2dfe601ba05a + + + + + + + 251186883 + USD + ClearFinancialStatus + 180535609 + English + 0 + 2023-08-11T08:24:26.603 + DEMO-ACCOUNT + F149W3B6 + 251186883 + + + 138225488 + Pause + AAAAAH10c1A= + Santiago + 2 + + + + + + + + San Francisco + US + 149694999 + 350 29th avenue + + + + 94121 + CA + + Daxtarity Inc. + + Inactive + + Expert + + + + + +""" + +GET_USER_RESPONSE = b""" + + 762354725472 + + + + + + + City + USD + 12345678 + Test Line + Test Line + Test Line + Test Line + 0671 + State + 12327485 + Test + + 50005 + 7365 + test@mail.com + test + 73456-343 + 83563 + 1232346573 + 736537 + 2645 + 45353 + + 234627 + 276342574 + Title Job + 234722342 + 2024-01-01T01:01:10.327 + 827462346 + + Name First + Name Last + Test + + test + test + test? + test + 2736452 + test + + + key + value + + + token + + + + 8324628 + 726542 + + 180535609 + + + 180535609 + + http://link + + + + + +""" + + +def mock_http_authenticated_send(transport: HttpAuthenticated, request: Request) -> Reply: + if request.headers.get('SOAPAction').decode() == '"GetUser"': + return Reply(code=200, headers={}, message=GET_USER_RESPONSE) + + if request.headers.get('SOAPAction').decode() == '"SearchAccounts"': + return Reply(code=200, headers={}, message=SEARCH_ACCOUNTS_RESPONSE) + + raise Exception(f"Unexpected SOAPAction provided for mock SOAP client: {request.headers.get('SOAPAction').decode()}") diff --git a/airbyte-integrations/connectors/source-bing-ads/unit_tests/integrations/test_budget_stream.py b/airbyte-integrations/connectors/source-bing-ads/unit_tests/integrations/test_budget_stream.py new file mode 100644 index 000000000000..5f7bd4802613 --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/unit_tests/integrations/test_budget_stream.py @@ -0,0 +1,54 @@ +# Copyright (c) 2023 Airbyte, Inc., all rights reserved. +import pendulum +from airbyte_cdk.models import SyncMode +from airbyte_cdk.test.mock_http import HttpMocker +from freezegun import freeze_time +from test_bulk_stream import TestBulkStream + + +class TestBudgetStream(TestBulkStream): + stream_name = "budget" + account_id = "180535609" + cursor_field = "Modified Time" + + @HttpMocker() + def test_return_records_from_given_csv_file(self, http_mocker: HttpMocker): + self.auth_client(http_mocker) + output, _ = self.read_stream(self.stream_name, SyncMode.full_refresh, self._config, "budget") + assert len(output.records) == 1 + + @HttpMocker() + def test_return_logged_info_for_empty_csv_file(self, http_mocker: HttpMocker): + self.auth_client(http_mocker) + output, _ = self.read_stream(self.stream_name, SyncMode.full_refresh, self._config, "budget_empty") + assert len(output.records) == 0 + assert len(output.logs) == 10 + + @HttpMocker() + def test_transform_records(self, http_mocker: HttpMocker): + self.auth_client(http_mocker) + output, _ = self.read_stream(self.stream_name, SyncMode.full_refresh, self._config, "budget") + assert output.records + for record in output.records: + assert "Account Id" in record.record.data.keys() + assert isinstance(record.record.data["Account Id"], int) + + @HttpMocker() + def test_incremental_read_cursor_value_matches_value_from_most_recent_record(self, http_mocker: HttpMocker): + self.auth_client(http_mocker) + output, _ = self.read_stream(self.stream_name, SyncMode.incremental, self._config, "budget_with_cursor_value") + assert len(output.records) == 8 + assert output.most_recent_state.get(self.stream_name, {}).get(self.account_id, {}) == {self.cursor_field: "2024-01-01T12:54:12.028+00:00"} + + @HttpMocker() + @freeze_time("204-02-26") # mock current time as stream data available for 30 days only + def test_incremental_read_with_state(self, http_mocker: HttpMocker): + state = self._state("budget_state", self.stream_name) + self.auth_client(http_mocker) + output, service_call_mock = self.read_stream(self.stream_name, SyncMode.incremental, self._config, "budget_with_state", state) + assert len(output.records) == 8 + assert output.most_recent_state.get(self.stream_name, {}).get(self.account_id, {}) == {self.cursor_field: "2024-01-30T12:54:12.028+00:00"} + + previous_state = state[0].stream.stream_state.dict() + # gets DownloadParams object + assert service_call_mock.call_args.args[0].last_sync_time_in_utc == pendulum.parse(previous_state[self.account_id][self.cursor_field]) diff --git a/airbyte-integrations/connectors/source-bing-ads/unit_tests/integrations/test_bulk_stream.py b/airbyte-integrations/connectors/source-bing-ads/unit_tests/integrations/test_bulk_stream.py new file mode 100644 index 000000000000..9435295921b2 --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/unit_tests/integrations/test_bulk_stream.py @@ -0,0 +1,28 @@ +# Copyright (c) 2023 Airbyte, Inc., all rights reserved. + +from pathlib import Path +from typing import Optional + +from base_test import BaseTest +from bingads.v13.bulk.bulk_service_manager import BulkServiceManager + + +class TestBulkStream(BaseTest): + + @property + def service_manager(self) -> BulkServiceManager: + return BulkServiceManager + + def _download_file(self, file: Optional[str] = None) -> Path: + """ + Returns path to temporary file of downloaded data that will be use in read. + Base file should be named as {file_name}.cvs in resource/response folder. + """ + if file: + path_to_tmp_file = Path(__file__).parent.parent / f"resource/response/{file}_tmp.csv" + path_to_file_base = Path(__file__).parent.parent / f"resource/response/{file}.csv" + with open(path_to_file_base, "r") as f1, open(path_to_tmp_file, "w") as f2: + for line in f1: + f2.write(line) + return path_to_tmp_file + return Path(__file__).parent.parent / "resource/response/non-existing-file.csv" diff --git a/airbyte-integrations/connectors/source-bing-ads/unit_tests/integrations/test_product_dimension_performance_report.py b/airbyte-integrations/connectors/source-bing-ads/unit_tests/integrations/test_product_dimension_performance_report.py new file mode 100644 index 000000000000..89968b69c67b --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/unit_tests/integrations/test_product_dimension_performance_report.py @@ -0,0 +1,43 @@ +# Copyright (c) 2023 Airbyte, Inc., all rights reserved. + +from test_report_stream import TestSuiteReportStream + + +class TestProductDimensionPerformanceReportDailyStream(TestSuiteReportStream): + stream_name = "product_dimension_performance_report_daily" + report_file = "product_dimension_performance_report_daily" + records_number = 8 + state_file = "product_dimension_performance_report_daily_state" + incremental_report_file = "product_dimension_performance_report_daily_incremental" + first_read_state = {"product_dimension_performance_report_daily": {"180535609": {"TimePeriod": "2023-12-17"}}} + second_read_state = {"product_dimension_performance_report_daily": {"180535609": {"TimePeriod": "2023-12-25"}}} + + +class TestProductDimensionPerformanceReportHourlyStream(TestSuiteReportStream): + stream_name = "product_dimension_performance_report_hourly" + report_file = "product_dimension_performance_report_hourly" + records_number = 8 + state_file = "product_dimension_performance_report_hourly_state" + incremental_report_file = "product_dimension_performance_report_hourly_incremental" + first_read_state = {"product_dimension_performance_report_hourly": {"180535609": {"TimePeriod": "2023-11-11T01:00:00+00:00"}}} + second_read_state = {"product_dimension_performance_report_hourly": {"180535609": {"TimePeriod": "2023-11-12T01:00:00+00:00"}}} + + +class TestProductDimensionPerformanceReportWeeklyStream(TestSuiteReportStream): + stream_name = "product_dimension_performance_report_weekly" + report_file = "product_dimension_performance_report_weekly" + records_number = 8 + state_file = "product_dimension_performance_report_weekly_state" + incremental_report_file = "product_dimension_performance_report_weekly_incremental" + first_read_state = {"product_dimension_performance_report_weekly": {"180535609": {"TimePeriod": "2023-12-17"}}} + second_read_state = {"product_dimension_performance_report_weekly": {"180535609": {"TimePeriod": "2023-12-25"}}} + + +class TestProductDimensionPerformanceReportMonthlyStream(TestSuiteReportStream): + stream_name = "product_dimension_performance_report_monthly" + report_file = "product_dimension_performance_report_monthly" + records_number = 8 + state_file = "product_dimension_performance_report_monthly_state" + incremental_report_file = "product_dimension_performance_report_monthly_incremental" + first_read_state = {"product_dimension_performance_report_monthly": {"180535609": {"TimePeriod": "2023-12-01"}}} + second_read_state = {"product_dimension_performance_report_monthly": {"180535609": {"TimePeriod": "2024-01-01"}}} diff --git a/airbyte-integrations/connectors/source-bing-ads/unit_tests/integrations/test_report_stream.py b/airbyte-integrations/connectors/source-bing-ads/unit_tests/integrations/test_report_stream.py new file mode 100644 index 000000000000..0678ec45aed8 --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/unit_tests/integrations/test_report_stream.py @@ -0,0 +1,112 @@ +# Copyright (c) 2023 Airbyte, Inc., all rights reserved. + +from pathlib import Path +from typing import Any, Optional + +import pendulum +from airbyte_cdk.models import SyncMode +from airbyte_cdk.test.mock_http import HttpMocker +from base_test import BaseTest +from bingads.v13.reporting.reporting_service_manager import ReportingServiceManager +from config_builder import ConfigBuilder + + +class TestReportStream(BaseTest): + start_date = "2024-01-01" + + @property + def service_manager(self) -> ReportingServiceManager: + return ReportingServiceManager + + @property + def _config(self) -> dict[str, Any]: + return ConfigBuilder().with_reports_start_date(self.start_date).build() + + def _download_file(self, file: Optional[str] = None) -> Path: + """ + Returns path to temporary file of downloaded data that will be use in read. + Base file should be named as {file_name}.cvs in resource/response folder. + """ + if file: + path_to_tmp_file = Path(__file__).parent.parent / f"resource/response/{file}.csv" + return path_to_tmp_file + return Path(__file__).parent.parent / "resource/response/non-existing-file.csv" + + +class TestSuiteReportStream(TestReportStream): + stream_name: str = None + report_file: str + records_number: int + state_file: str + incremental_report_file: str + first_read_state: dict + second_read_state: dict + transform_field: str = "AccountId" + account_id: str = "180535609" + cursor_field = "TimePeriod" + + def setUp(self): + if not self.stream_name: + self.skipTest("Skipping TestSuiteReportStream") + + @HttpMocker() + def test_return_records_from_given_csv_file(self, http_mocker: HttpMocker): + self.auth_client(http_mocker) + output, _ = self.read_stream( + self.stream_name, + SyncMode.full_refresh, + self._config, + self.report_file + ) + assert len(output.records) == self.records_number + + @HttpMocker() + def test_transform_records_from_given_csv_file(self, http_mocker: HttpMocker): + self.auth_client(http_mocker) + output, _ = self.read_stream( + self.stream_name, + SyncMode.full_refresh, + self._config, + self.report_file + ) + + assert len(output.records) == self.records_number + for record in output.records: + assert self.transform_field in record.record.data.keys() + + @HttpMocker() + def test_incremental_read_returns_records(self, http_mocker: HttpMocker): + self.auth_client(http_mocker) + output, _ = self.read_stream( + self.stream_name, + SyncMode.incremental, + self._config, + self.report_file + ) + assert len(output.records) == self.records_number + assert output.most_recent_state == self.first_read_state + + @HttpMocker() + def test_incremental_read_with_state_returns_records(self, http_mocker: HttpMocker): + state = self._state(self.state_file, self.stream_name) + self.auth_client(http_mocker) + output, service_call_mock = self.read_stream( + self.stream_name, + SyncMode.incremental, + self._config, + self.incremental_report_file, + state + ) + assert len(output.records) == self.records_number + + actual_cursor = output.most_recent_state.get(self.stream_name).get(self.account_id) + expected_cursor = self.second_read_state.get(self.stream_name).get(self.account_id) + assert actual_cursor == expected_cursor + + provided_state = state[0].stream.stream_state.dict()[self.account_id][self.cursor_field] + # gets ReportDownloadParams object + request_start_date = service_call_mock.call_args.args[0].report_request.Time.CustomDateRangeStart + year = request_start_date.Year + month = request_start_date.Month + day = request_start_date.Day + assert pendulum.DateTime(year, month, day, tzinfo=pendulum.UTC) == pendulum.parse(provided_state) diff --git a/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/http/response/oauth.json b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/http/response/oauth.json new file mode 100644 index 000000000000..217f1d0f12da --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/http/response/oauth.json @@ -0,0 +1,5 @@ +{ + "access_token": "access_token", + "expires_in": 111111, + "refresh_token": "refresh_token" +} diff --git a/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/budget.csv b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/budget.csv new file mode 100644 index 000000000000..764d241d6722 --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/budget.csv @@ -0,0 +1,3 @@ +Type,Status,Id,Parent Id,Client Id,Modified Time,Budget Id,Budget Name,Budget,Budget Type,Name +Format Version,,,,,,,,,,6.0 +Budget,Active,-20,0,23645271,,,My Shared Budget,50,DailyBudgetStandard, \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/budget_empty.csv b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/budget_empty.csv new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/budget_with_cursor_value.csv b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/budget_with_cursor_value.csv new file mode 100644 index 000000000000..cad3c6249d2c --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/budget_with_cursor_value.csv @@ -0,0 +1,10 @@ +Type,Status,Id,Parent Id,Client Id,Modified Time,Budget Id,Budget Name,Budget,Budget Type,Name +Format Version,,,,,,,,,,6.0 +Budget,Active,-20,0,23645271,01/01/2024 12:12:12.02837,,My Shared Budget,50,DailyBudgetStandard, +Budget,Active,-20,0,23645271,01/01/2024 12:13:12.02837,,My Shared Budget,50,DailyBudgetStandard, +Budget,Active,-20,0,23645271,01/01/2024 12:14:12.02837,,My Shared Budget,50,DailyBudgetStandard, +Budget,Active,-20,0,23645271,01/01/2024 12:15:12.02837,,My Shared Budget,50,DailyBudgetStandard, +Budget,Active,-20,0,23645271,01/01/2024 12:17:12.02837,,My Shared Budget,50,DailyBudgetStandard, +Budget,Active,-20,0,23645271,01/01/2024 12:23:12.02837,,My Shared Budget,50,DailyBudgetStandard, +Budget,Active,-20,0,23645271,01/01/2024 12:43:12.02837,,My Shared Budget,50,DailyBudgetStandard, +Budget,Active,-20,0,23645271,01/01/2024 12:54:12.02837,,My Shared Budget,50,DailyBudgetStandard, \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/budget_with_state.csv b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/budget_with_state.csv new file mode 100644 index 000000000000..6dcbbf10b211 --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/budget_with_state.csv @@ -0,0 +1,10 @@ +Type,Status,Id,Parent Id,Client Id,Modified Time,Budget Id,Budget Name,Budget,Budget Type,Name +Format Version,,,,,,,,,,6.0 +Budget,Active,-20,0,23645271,01/30/2024 12:12:12.02837,,My Shared Budget,50,DailyBudgetStandard, +Budget,Active,-20,0,23645271,01/30/2024 12:13:12.02837,,My Shared Budget,50,DailyBudgetStandard, +Budget,Active,-20,0,23645271,01/30/2024 12:14:12.02837,,My Shared Budget,50,DailyBudgetStandard, +Budget,Active,-20,0,23645271,01/30/2024 12:15:12.02837,,My Shared Budget,50,DailyBudgetStandard, +Budget,Active,-20,0,23645271,01/30/2024 12:17:12.02837,,My Shared Budget,50,DailyBudgetStandard, +Budget,Active,-20,0,23645271,01/30/2024 12:23:12.02837,,My Shared Budget,50,DailyBudgetStandard, +Budget,Active,-20,0,23645271,01/30/2024 12:43:12.02837,,My Shared Budget,50,DailyBudgetStandard, +Budget,Active,-20,0,23645271,01/30/2024 12:54:12.02837,,My Shared Budget,50,DailyBudgetStandard, \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/product_dimension_performance_report_daily.csv b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/product_dimension_performance_report_daily.csv new file mode 100644 index 000000000000..727529543895 --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/product_dimension_performance_report_daily.csv @@ -0,0 +1,9 @@ +TimePeriod,AccountName,AccountNumber,AdGroupName,AdGroupId,CampaignStatus,AccountStatus,AdGroupStatus,Network,AdId,CampaignId,CampaignName,CurrencyCode,DeviceType,Language,MerchantProductId,Title,Condition,Brand,Price,Impressions,Clicks,Ctr,AverageCpc,Spend,Conversions,ConversionRate,Revenue,RevenuePerConversion,SellerName,OfferLanguage,CountryOfSale,AdStatus,AdDistribution,ClickTypeId,TotalClicksOnAdElements,ClickType,ReturnOnAdSpend,BidStrategyType,LocalStoreCode,StoreId,AssistedClicks,AssistedConversions,AllConversions,AllRevenue,AllConversionRate,AllCostPerConversion,AllReturnOnAdSpend,AllRevenuePerConversion,CostPerConversion,ViewThroughConversions,Goal,GoalType,ProductBought,QuantityBought,AverageCpm,ConversionsQualified,AssistedConversionsQualified,ViewThroughConversionsQualified,ProductBoughtTitle,GTIN,MPN,ViewThroughRevenue,Sales,CostPerSale,RevenuePerSale,Installs,CostPerInstall,RevenuePerInstall,CampaignType,AssetGroupId,AssetGroupName,AssetGroupStatus,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-17,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-17,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-17,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-17,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-17,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-17,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-17,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-17,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/product_dimension_performance_report_daily_incremental.csv b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/product_dimension_performance_report_daily_incremental.csv new file mode 100644 index 000000000000..ea0830b65172 --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/product_dimension_performance_report_daily_incremental.csv @@ -0,0 +1,9 @@ +TimePeriod,AccountName,AccountNumber,AdGroupName,AdGroupId,CampaignStatus,AccountStatus,AdGroupStatus,Network,AdId,CampaignId,CampaignName,CurrencyCode,DeviceType,Language,MerchantProductId,Title,Condition,Brand,Price,Impressions,Clicks,Ctr,AverageCpc,Spend,Conversions,ConversionRate,Revenue,RevenuePerConversion,SellerName,OfferLanguage,CountryOfSale,AdStatus,AdDistribution,ClickTypeId,TotalClicksOnAdElements,ClickType,ReturnOnAdSpend,BidStrategyType,LocalStoreCode,StoreId,AssistedClicks,AssistedConversions,AllConversions,AllRevenue,AllConversionRate,AllCostPerConversion,AllReturnOnAdSpend,AllRevenuePerConversion,CostPerConversion,ViewThroughConversions,Goal,GoalType,ProductBought,QuantityBought,AverageCpm,ConversionsQualified,AssistedConversionsQualified,ViewThroughConversionsQualified,ProductBoughtTitle,GTIN,MPN,ViewThroughRevenue,Sales,CostPerSale,RevenuePerSale,Installs,CostPerInstall,RevenuePerInstall,CampaignType,AssetGroupId,AssetGroupName,AssetGroupStatus,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-18,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-19,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-20,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-21,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-22,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-11-11T01:15:00+00:00,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-24,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-25,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/product_dimension_performance_report_hourly.csv b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/product_dimension_performance_report_hourly.csv new file mode 100644 index 000000000000..a4bc888df397 --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/product_dimension_performance_report_hourly.csv @@ -0,0 +1,9 @@ +TimePeriod,AccountName,AccountNumber,AdGroupName,AdGroupId,CampaignStatus,AccountStatus,AdGroupStatus,Network,AdId,CampaignId,CampaignName,CurrencyCode,DeviceType,Language,MerchantProductId,Title,Condition,Brand,Price,Impressions,Clicks,Ctr,AverageCpc,Spend,Conversions,ConversionRate,Revenue,RevenuePerConversion,SellerName,OfferLanguage,CountryOfSale,AdStatus,AdDistribution,ClickTypeId,TotalClicksOnAdElements,ClickType,ReturnOnAdSpend,BidStrategyType,LocalStoreCode,StoreId,AssistedClicks,AssistedConversions,AllConversions,AllRevenue,AllConversionRate,AllCostPerConversion,AllReturnOnAdSpend,AllRevenuePerConversion,CostPerConversion,ViewThroughConversions,Goal,GoalType,ProductBought,QuantityBought,AverageCpm,ConversionsQualified,AssistedConversionsQualified,ViewThroughConversionsQualified,ProductBoughtTitle,GTIN,MPN,ViewThroughRevenue,Sales,CostPerSale,RevenuePerSale,Installs,CostPerInstall,RevenuePerInstall,CampaignType,AssetGroupId,AssetGroupName,AssetGroupStatus,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-11-11|01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-11-11|01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-11-11|01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-11-11|01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-11-11|01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-11-11|01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-11-11|01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-11-11|01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/product_dimension_performance_report_hourly_incremental.csv b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/product_dimension_performance_report_hourly_incremental.csv new file mode 100644 index 000000000000..0c5dfa2da5bd --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/product_dimension_performance_report_hourly_incremental.csv @@ -0,0 +1,9 @@ +TimePeriod,AccountName,AccountNumber,AdGroupName,AdGroupId,CampaignStatus,AccountStatus,AdGroupStatus,Network,AdId,CampaignId,CampaignName,CurrencyCode,DeviceType,Language,MerchantProductId,Title,Condition,Brand,Price,Impressions,Clicks,Ctr,AverageCpc,Spend,Conversions,ConversionRate,Revenue,RevenuePerConversion,SellerName,OfferLanguage,CountryOfSale,AdStatus,AdDistribution,ClickTypeId,TotalClicksOnAdElements,ClickType,ReturnOnAdSpend,BidStrategyType,LocalStoreCode,StoreId,AssistedClicks,AssistedConversions,AllConversions,AllRevenue,AllConversionRate,AllCostPerConversion,AllReturnOnAdSpend,AllRevenuePerConversion,CostPerConversion,ViewThroughConversions,Goal,GoalType,ProductBought,QuantityBought,AverageCpm,ConversionsQualified,AssistedConversionsQualified,ViewThroughConversionsQualified,ProductBoughtTitle,GTIN,MPN,ViewThroughRevenue,Sales,CostPerSale,RevenuePerSale,Installs,CostPerInstall,RevenuePerInstall,CampaignType,AssetGroupId,AssetGroupName,AssetGroupStatus,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-11-12|01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-11-12|01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-11-12|01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-11-12|01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-11-12|01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-11-12|01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-11-12|01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-11-12|01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/product_dimension_performance_report_monthly.csv b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/product_dimension_performance_report_monthly.csv new file mode 100644 index 000000000000..ebe759d45fe3 --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/product_dimension_performance_report_monthly.csv @@ -0,0 +1,9 @@ +TimePeriod,AccountName,AccountNumber,AdGroupName,AdGroupId,CampaignStatus,AccountStatus,AdGroupStatus,Network,AdId,CampaignId,CampaignName,CurrencyCode,DeviceType,Language,MerchantProductId,Title,Condition,Brand,Price,Impressions,Clicks,Ctr,AverageCpc,Spend,Conversions,ConversionRate,Revenue,RevenuePerConversion,SellerName,OfferLanguage,CountryOfSale,AdStatus,AdDistribution,ClickTypeId,TotalClicksOnAdElements,ClickType,ReturnOnAdSpend,BidStrategyType,LocalStoreCode,StoreId,AssistedClicks,AssistedConversions,AllConversions,AllRevenue,AllConversionRate,AllCostPerConversion,AllReturnOnAdSpend,AllRevenuePerConversion,CostPerConversion,ViewThroughConversions,Goal,GoalType,ProductBought,QuantityBought,AverageCpm,ConversionsQualified,AssistedConversionsQualified,ViewThroughConversionsQualified,ProductBoughtTitle,GTIN,MPN,ViewThroughRevenue,Sales,CostPerSale,RevenuePerSale,Installs,CostPerInstall,RevenuePerInstall,CampaignType,AssetGroupId,AssetGroupName,AssetGroupStatus,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/product_dimension_performance_report_monthly_incremental.csv b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/product_dimension_performance_report_monthly_incremental.csv new file mode 100644 index 000000000000..abb1a3247ba7 --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/product_dimension_performance_report_monthly_incremental.csv @@ -0,0 +1,9 @@ +TimePeriod,AccountName,AccountNumber,AdGroupName,AdGroupId,CampaignStatus,AccountStatus,AdGroupStatus,Network,AdId,CampaignId,CampaignName,CurrencyCode,DeviceType,Language,MerchantProductId,Title,Condition,Brand,Price,Impressions,Clicks,Ctr,AverageCpc,Spend,Conversions,ConversionRate,Revenue,RevenuePerConversion,SellerName,OfferLanguage,CountryOfSale,AdStatus,AdDistribution,ClickTypeId,TotalClicksOnAdElements,ClickType,ReturnOnAdSpend,BidStrategyType,LocalStoreCode,StoreId,AssistedClicks,AssistedConversions,AllConversions,AllRevenue,AllConversionRate,AllCostPerConversion,AllReturnOnAdSpend,AllRevenuePerConversion,CostPerConversion,ViewThroughConversions,Goal,GoalType,ProductBought,QuantityBought,AverageCpm,ConversionsQualified,AssistedConversionsQualified,ViewThroughConversionsQualified,ProductBoughtTitle,GTIN,MPN,ViewThroughRevenue,Sales,CostPerSale,RevenuePerSale,Installs,CostPerInstall,RevenuePerInstall,CampaignType,AssetGroupId,AssetGroupName,AssetGroupStatus,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2024-01-01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2024-01-01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2024-01-01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2024-01-01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2024-01-01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2024-01-01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2024-01-01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2024-01-01,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/product_dimension_performance_report_weekly.csv b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/product_dimension_performance_report_weekly.csv new file mode 100644 index 000000000000..727529543895 --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/product_dimension_performance_report_weekly.csv @@ -0,0 +1,9 @@ +TimePeriod,AccountName,AccountNumber,AdGroupName,AdGroupId,CampaignStatus,AccountStatus,AdGroupStatus,Network,AdId,CampaignId,CampaignName,CurrencyCode,DeviceType,Language,MerchantProductId,Title,Condition,Brand,Price,Impressions,Clicks,Ctr,AverageCpc,Spend,Conversions,ConversionRate,Revenue,RevenuePerConversion,SellerName,OfferLanguage,CountryOfSale,AdStatus,AdDistribution,ClickTypeId,TotalClicksOnAdElements,ClickType,ReturnOnAdSpend,BidStrategyType,LocalStoreCode,StoreId,AssistedClicks,AssistedConversions,AllConversions,AllRevenue,AllConversionRate,AllCostPerConversion,AllReturnOnAdSpend,AllRevenuePerConversion,CostPerConversion,ViewThroughConversions,Goal,GoalType,ProductBought,QuantityBought,AverageCpm,ConversionsQualified,AssistedConversionsQualified,ViewThroughConversionsQualified,ProductBoughtTitle,GTIN,MPN,ViewThroughRevenue,Sales,CostPerSale,RevenuePerSale,Installs,CostPerInstall,RevenuePerInstall,CampaignType,AssetGroupId,AssetGroupName,AssetGroupStatus,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-17,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-17,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-17,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-17,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-17,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-17,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-17,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-17,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/product_dimension_performance_report_weekly_incremental.csv b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/product_dimension_performance_report_weekly_incremental.csv new file mode 100644 index 000000000000..ea0830b65172 --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/response/product_dimension_performance_report_weekly_incremental.csv @@ -0,0 +1,9 @@ +TimePeriod,AccountName,AccountNumber,AdGroupName,AdGroupId,CampaignStatus,AccountStatus,AdGroupStatus,Network,AdId,CampaignId,CampaignName,CurrencyCode,DeviceType,Language,MerchantProductId,Title,Condition,Brand,Price,Impressions,Clicks,Ctr,AverageCpc,Spend,Conversions,ConversionRate,Revenue,RevenuePerConversion,SellerName,OfferLanguage,CountryOfSale,AdStatus,AdDistribution,ClickTypeId,TotalClicksOnAdElements,ClickType,ReturnOnAdSpend,BidStrategyType,LocalStoreCode,StoreId,AssistedClicks,AssistedConversions,AllConversions,AllRevenue,AllConversionRate,AllCostPerConversion,AllReturnOnAdSpend,AllRevenuePerConversion,CostPerConversion,ViewThroughConversions,Goal,GoalType,ProductBought,QuantityBought,AverageCpm,ConversionsQualified,AssistedConversionsQualified,ViewThroughConversionsQualified,ProductBoughtTitle,GTIN,MPN,ViewThroughRevenue,Sales,CostPerSale,RevenuePerSale,Installs,CostPerInstall,RevenuePerInstall,CampaignType,AssetGroupId,AssetGroupName,AssetGroupStatus,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-18,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-19,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-20,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-21,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-22,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-11-11T01:15:00+00:00,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-24,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 +2023-12-25,TestAccount,123456,TestGroup,212344,Active,Active,,,12345,123456778,USD,Computer,English,123455,Title,,,45,45,0,0,0,0,0,0,0,0,0,TestName,English,USA,Active,,12124,1233,,,,,5675,0,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,23,234,CustomLabel0,CustomLabel1,CustomLabel2,CustomLabel3,CustomLabel4,ProductType1,ProductType2,ProductType3,ProductType4,ProductType5 \ No newline at end of file diff --git a/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/state/budget_state.json b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/state/budget_state.json new file mode 100644 index 000000000000..7c234188ff04 --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/state/budget_state.json @@ -0,0 +1,5 @@ +{ + "180535609": { + "Modified Time": "2024-01-29T12:54:12.028+00:00" + } +} diff --git a/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/state/product_dimension_performance_report_daily_state.json b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/state/product_dimension_performance_report_daily_state.json new file mode 100644 index 000000000000..daa759dfec58 --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/state/product_dimension_performance_report_daily_state.json @@ -0,0 +1,5 @@ +{ + "180535609": { + "TimePeriod": "2023-11-11" + } +} diff --git a/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/state/product_dimension_performance_report_hourly_state.json b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/state/product_dimension_performance_report_hourly_state.json new file mode 100644 index 000000000000..daa759dfec58 --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/state/product_dimension_performance_report_hourly_state.json @@ -0,0 +1,5 @@ +{ + "180535609": { + "TimePeriod": "2023-11-11" + } +} diff --git a/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/state/product_dimension_performance_report_monthly_state.json b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/state/product_dimension_performance_report_monthly_state.json new file mode 100644 index 000000000000..864a77903e29 --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/state/product_dimension_performance_report_monthly_state.json @@ -0,0 +1,5 @@ +{ + "180535609": { + "TimePeriod": "2023-12-01" + } +} diff --git a/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/state/product_dimension_performance_report_weekly_state.json b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/state/product_dimension_performance_report_weekly_state.json new file mode 100644 index 000000000000..daa759dfec58 --- /dev/null +++ b/airbyte-integrations/connectors/source-bing-ads/unit_tests/resource/state/product_dimension_performance_report_weekly_state.json @@ -0,0 +1,5 @@ +{ + "180535609": { + "TimePeriod": "2023-11-11" + } +} diff --git a/airbyte-integrations/connectors/source-bing-ads/unit_tests/test_bulk_streams.py b/airbyte-integrations/connectors/source-bing-ads/unit_tests/test_bulk_streams.py index c69f77e9bea2..9a96becee8c6 100644 --- a/airbyte-integrations/connectors/source-bing-ads/unit_tests/test_bulk_streams.py +++ b/airbyte-integrations/connectors/source-bing-ads/unit_tests/test_bulk_streams.py @@ -103,6 +103,14 @@ def test_bulk_stream_read_with_chunks_app_install_ad_labels(mocked_client, confi } +@patch.object(source_bing_ads.source, "Client") +def test_bulk_stream_read_with_chunks_ioe_error(mocked_client, config, caplog): + app_install_ads = AppInstallAdLabels(mocked_client, config) + with pytest.raises(IOError): + list(app_install_ads.read_with_chunks(path=Path(__file__).parent / "non-existing-file.csv")) + assert "The IO/Error occurred while reading tmp data" in caplog.text + + @patch.object(source_bing_ads.source, "Client") @freeze_time("2023-11-01T12:00:00.000+00:00") @pytest.mark.parametrize( 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 210ccf1031be..5293baef52b0 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 @@ -16,7 +16,7 @@ @patch.object(source_bing_ads.source, "Client") def test_streams_config_based(mocked_client, config): streams = SourceBingAds().streams(config) - assert len(streams) == 60 + assert len(streams) == 65 @patch.object(source_bing_ads.source, "Client") diff --git a/docs/integrations/sources/bing-ads.md b/docs/integrations/sources/bing-ads.md index 3cedce121288..2214886b6137 100644 --- a/docs/integrations/sources/bing-ads.md +++ b/docs/integrations/sources/bing-ads.md @@ -113,6 +113,7 @@ The Bing Ads source connector supports the following streams. For more informati - [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) +- [Budget](https://learn.microsoft.com/en-us/advertising/bulk-service/budget?view=bingads-13&viewFallbackFrom=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) - [Keywords](https://learn.microsoft.com/en-us/advertising/bulk-service/keyword?view=bingads-13) @@ -166,6 +167,10 @@ The Bing Ads source connector supports the following streams. For more informati - [User Location Performance Report Daily](https://learn.microsoft.com/en-us/advertising/reporting-service/userlocationperformancereportrequest?view=bingads-13) - [User Location Performance Report Weekly](https://learn.microsoft.com/en-us/advertising/reporting-service/userlocationperformancereportrequest?view=bingads-13) - [User Location Performance Report Monthly](https://learn.microsoft.com/en-us/advertising/reporting-service/userlocationperformancereportrequest?view=bingads-13) +- [Product Dimension Performance Report Hourly](https://learn.microsoft.com/en-us/advertising/reporting-service/productdimensionperformancereportrequest?view=bingads-13) +- [Product Dimension Performance Report Daily](https://learn.microsoft.com/en-us/advertising/reporting-service/productdimensionperformancereportrequest?view=bingads-13) +- [Product Dimension Performance Report Weekly](https://learn.microsoft.com/en-us/advertising/reporting-service/productdimensionperformancereportrequest?view=bingads-13) +- [Product Dimension Performance Report Monthly](https://learn.microsoft.com/en-us/advertising/reporting-service/productdimensionperformancereportrequest?view=bingads-13) - [Search Query Performance Report Hourly](https://learn.microsoft.com/en-us/advertising/reporting-service/searchqueryperformancereportrequest?view=bingads-13) - [Search Query Performance Report Daily](https://learn.microsoft.com/en-us/advertising/reporting-service/searchqueryperformancereportrequest?view=bingads-13) - [Search Query Performance Report Weekly](https://learn.microsoft.com/en-us/advertising/reporting-service/searchqueryperformancereportrequest?view=bingads-13) @@ -226,7 +231,8 @@ The Bing Ads API limits the number of requests for all Microsoft Advertising cli | Version | Date | Pull Request | Subject | |:--------|:-----------|:---------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------| -| 2.1.4 | 2024-02-12 | [35179](https://github.com/airbytehq/airbyte/pull/35179) | Manage dependencies with Poetry. | +| 2.2.0 | 2024-02-13 | [35201](https://github.com/airbytehq/airbyte/pull/35201) | New streams Budget and | +| 2.1.4 | 2024-02-12 | [35179](https://github.com/airbytehq/airbyte/pull/35179) | Manage dependencies with Poetry. | | 2.1.3 | 2024-01-31 | [34712](https://github.com/airbytehq/airbyte/pull/34712) | Fix duplicated records for report-based streams | | 2.1.2 | 2024-01-09 | [34045](https://github.com/airbytehq/airbyte/pull/34045) | Speed up record transformation | | 2.1.1 | 2023-12-15 | [33500](https://github.com/airbytehq/airbyte/pull/33500) | Fix state setter when state was provided |