From a4165cdb3ad734f00881ed861f45ec8f5468ec87 Mon Sep 17 00:00:00 2001 From: Abhishek Gupta Date: Tue, 11 Dec 2018 12:04:17 +0530 Subject: [PATCH 01/18] camelcase instead of classify --- lib/bing/ads/api/v12/data/report_request.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/bing/ads/api/v12/data/report_request.rb b/lib/bing/ads/api/v12/data/report_request.rb index 7bb2297..d205b57 100644 --- a/lib/bing/ads/api/v12/data/report_request.rb +++ b/lib/bing/ads/api/v12/data/report_request.rb @@ -27,7 +27,7 @@ class << self def prepare(type, report_request_raw) report_request_raw[:columns] = prepare_columns( columns: report_request_raw[:columns], - type: type.to_s.classify + type: type.to_s.camelcase ) report_request_raw[:scope] = prepare_scope( @@ -48,7 +48,7 @@ def prepare(type, report_request_raw) :attributes! => { report_request: { "xmlns:i" => "http://www.w3.org/2001/XMLSchema-instance", - "i:type" => "#{namespace_identifier}:#{type.to_s.classify}ReportRequest" + "i:type" => "#{namespace_identifier}:#{type.to_s.camelcase}ReportRequest" } } } From 124e729c007dac6e505c1bbc32e53014a3b10b53 Mon Sep 17 00:00:00 2001 From: Prashant Vithani Date: Mon, 4 Mar 2019 14:14:27 +0530 Subject: [PATCH 02/18] Handle the case when the url is nil for no data from bing --- lib/bing/ads/api/v12/services/reporting.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/bing/ads/api/v12/services/reporting.rb b/lib/bing/ads/api/v12/services/reporting.rb index a34a5c5..555c7ea 100644 --- a/lib/bing/ads/api/v12/services/reporting.rb +++ b/lib/bing/ads/api/v12/services/reporting.rb @@ -35,6 +35,9 @@ def report_url(report_request_id) end def report_body(report_request_id) + report_url = report_url(report_request_id) + return if report_url.nil? + HttpClient.download(report_url(report_request_id)) end From a3132af799f2c88b43d85cdbeeecd969a0c92e47 Mon Sep 17 00:00:00 2001 From: Srivathsan MS Date: Wed, 23 Oct 2019 23:03:16 +0530 Subject: [PATCH 03/18] Bing API v13 upgrade --- lib/bing/ads.rb | 8 +-- lib/bing/ads/api/v11/constants.rb | 29 --------- lib/bing/ads/api/v11/constants/wsdl.yml | 10 ---- lib/bing/ads/api/v11/services/bulk.rb | 38 ------------ lib/bing/ads/api/{v11.rb => v13.rb} | 6 +- lib/bing/ads/api/v13/constants.rb | 31 ++++++++++ lib/bing/ads/api/v13/constants/bulk.yml | 5 ++ .../constants/campaign_management.yml | 4 -- .../api/{v11 => v13}/constants/languages.yml | 0 .../ads/api/{v11 => v13}/constants/limits.yml | 0 .../api/{v11 => v13}/constants/time_zones.yml | 8 +-- lib/bing/ads/api/v13/constants/wsdl.yml | 10 ++++ lib/bing/ads/api/{v11 => v13}/data.rb | 1 + .../ads/api/{v11 => v13}/data/ad_group.rb | 49 ++++++++------- lib/bing/ads/api/v13/data/bulk_request.rb | 52 ++++++++++++++++ .../ads/api/{v11 => v13}/data/campaign.rb | 14 +++-- .../api/{v11 => v13}/data/expanded_text_ad.rb | 39 ++++++------ lib/bing/ads/api/{v11 => v13}/data/keyword.rb | 12 ++-- .../api/{v11 => v13}/data/report_request.rb | 37 ++++++------ lib/bing/ads/api/{v11 => v13}/services.rb | 0 .../ads/api/{v11 => v13}/services/base.rb | 8 +-- lib/bing/ads/api/v13/services/bulk.rb | 59 +++++++++++++++++++ .../services/campaign_management.rb | 32 +++++----- .../services/customer_management.rb | 4 +- .../api/{v11 => v13}/services/reporting.rb | 9 ++- lib/bing/ads/version.rb | 2 +- 26 files changed, 271 insertions(+), 196 deletions(-) delete mode 100644 lib/bing/ads/api/v11/constants.rb delete mode 100644 lib/bing/ads/api/v11/constants/wsdl.yml delete mode 100644 lib/bing/ads/api/v11/services/bulk.rb rename lib/bing/ads/api/{v11.rb => v13.rb} (74%) create mode 100644 lib/bing/ads/api/v13/constants.rb create mode 100644 lib/bing/ads/api/v13/constants/bulk.yml rename lib/bing/ads/api/{v11 => v13}/constants/campaign_management.yml (97%) rename lib/bing/ads/api/{v11 => v13}/constants/languages.yml (100%) rename lib/bing/ads/api/{v11 => v13}/constants/limits.yml (100%) rename lib/bing/ads/api/{v11 => v13}/constants/time_zones.yml (92%) create mode 100644 lib/bing/ads/api/v13/constants/wsdl.yml rename lib/bing/ads/api/{v11 => v13}/data.rb (82%) rename lib/bing/ads/api/{v11 => v13}/data/ad_group.rb (64%) create mode 100644 lib/bing/ads/api/v13/data/bulk_request.rb rename lib/bing/ads/api/{v11 => v13}/data/campaign.rb (82%) rename lib/bing/ads/api/{v11 => v13}/data/expanded_text_ad.rb (52%) rename lib/bing/ads/api/{v11 => v13}/data/keyword.rb (90%) rename lib/bing/ads/api/{v11 => v13}/data/report_request.rb (82%) rename lib/bing/ads/api/{v11 => v13}/services.rb (100%) rename lib/bing/ads/api/{v11 => v13}/services/base.rb (97%) create mode 100644 lib/bing/ads/api/v13/services/bulk.rb rename lib/bing/ads/api/{v11 => v13}/services/campaign_management.rb (93%) rename lib/bing/ads/api/{v11 => v13}/services/customer_management.rb (92%) rename lib/bing/ads/api/{v11 => v13}/services/reporting.rb (86%) diff --git a/lib/bing/ads.rb b/lib/bing/ads.rb index 7f27a01..73dc34c 100644 --- a/lib/bing/ads.rb +++ b/lib/bing/ads.rb @@ -8,14 +8,14 @@ require 'bing/ads/api/errors' require 'bing/ads/api/soap_client' require 'bing/ads/api/http_client' -require 'bing/ads/api/v11' -require 'bing/ads/api/v11/constants' -require 'bing/ads/api/v11/data' -require 'bing/ads/api/v11/services' require 'bing/ads/api/v12' require 'bing/ads/api/v12/constants' require 'bing/ads/api/v12/data' require 'bing/ads/api/v12/services' +require 'bing/ads/api/v13' +require 'bing/ads/api/v13/constants' +require 'bing/ads/api/v13/data' +require 'bing/ads/api/v13/services' require 'bing/ads/utils' require 'bing/ads/version' diff --git a/lib/bing/ads/api/v11/constants.rb b/lib/bing/ads/api/v11/constants.rb deleted file mode 100644 index 8a85fe1..0000000 --- a/lib/bing/ads/api/v11/constants.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -module Bing - module Ads - module API - # Bing::Ads::API::V11::Constants - module Constants - root_v11_path = File.expand_path('../', __FILE__) - - campaign_management_path = File.join(root_v11_path, 'constants', 'campaign_management.yml') - languages_path = File.join(root_v11_path, 'constants', 'languages.yml') - limits_path = File.join(root_v11_path, 'constants', 'limits.yml') - time_zones_path = File.join(root_v11_path, 'constants', 'time_zones.yml') - wsdl_path = File.join(root_v11_path, 'constants', 'wsdl.yml') - - Persey.init(:default) do - source :yaml, campaign_management_path, :campaign_management - source :yaml, languages_path, :languages - source :yaml, limits_path, :limits - source :yaml, time_zones_path, :time_zones - source :yaml, wsdl_path, :wsdl - env :default - end - - Bing::Ads::API::V11.constants = Persey.config - end - end - end -end diff --git a/lib/bing/ads/api/v11/constants/wsdl.yml b/lib/bing/ads/api/v11/constants/wsdl.yml deleted file mode 100644 index 224bf79..0000000 --- a/lib/bing/ads/api/v11/constants/wsdl.yml +++ /dev/null @@ -1,10 +0,0 @@ -sandbox: - customer_management: https://clientcenter.api.sandbox.bingads.microsoft.com/Api/CustomerManagement/v11/CustomerManagementService.svc?singleWsdl - campaign_management: "https://campaign.api.sandbox.bingads.microsoft.com/Api/Advertiser/CampaignManagement/V11/CampaignManagementService.svc?singleWsdl" - reporting: "https://api.sandbox.bingads.microsoft.com/Api/Advertiser/Reporting/V11/ReportingService.svc?singleWsdl" - bulk: 'https://bulk.api.sandbox.bingads.microsoft.com/Api/Advertiser/CampaignManagement/V11/BulkService.svc?singleWsdl' -production: - customer_management: https://clientcenter.api.bingads.microsoft.com/Api/CustomerManagement/v11/CustomerManagementService.svc?singleWsdl - campaign_management: "https://campaign.api.bingads.microsoft.com/Api/Advertiser/CampaignManagement/V11/CampaignManagementService.svc?singleWsdl" - reporting: "https://api.bingads.microsoft.com/Api/Advertiser/Reporting/V11/ReportingService.svc?singleWsdl" - bulk: 'https://bulk.api.bingads.microsoft.com/Api/Advertiser/CampaignManagement/V11/BulkService.svc?singleWsdl' diff --git a/lib/bing/ads/api/v11/services/bulk.rb b/lib/bing/ads/api/v11/services/bulk.rb deleted file mode 100644 index c47322b..0000000 --- a/lib/bing/ads/api/v11/services/bulk.rb +++ /dev/null @@ -1,38 +0,0 @@ -module Bing - module Ads - module API - module V11 - module Services - # Bing::Ads::API::V11::Services::Bulk - class Bulk < Base - def initialize(options = {}) - super(options) - end - - def get_bulk_upload_url(account_id = nil, response_mode = 'ErrorsOnly') - account_id ||= @account_id - - response = call(__method__, response_mode: response_mode, account_id: account_id) - response_body = response_body(response, __method__) - response_body.slice(:request_id, :upload_url) - end - - def get_bulk_upload_status(request_id) - response = call(__method__, request_id: request_id) - response_body = response_body(response, __method__) - response_body.slice(:errors, :percent_complete, :request_status, :result_file_url) - end - - # TODO operations: https://msdn.microsoft.com/en-us/library/bing-ads-bulk-service-operations.aspx - - private - - def service_name - 'bulk' - end - end - end - end - end - end -end diff --git a/lib/bing/ads/api/v11.rb b/lib/bing/ads/api/v13.rb similarity index 74% rename from lib/bing/ads/api/v11.rb rename to lib/bing/ads/api/v13.rb index c9d1a57..2624f5b 100644 --- a/lib/bing/ads/api/v11.rb +++ b/lib/bing/ads/api/v13.rb @@ -1,9 +1,9 @@ module Bing module Ads module API - # Bing::Ads::API::V11 - module V11 - NAMESPACE_IDENTIFIER = :v11 + # Bing::Ads::API::V13 + module V13 + NAMESPACE_IDENTIFIER = :v13 def self.constants @_config || fail('Error loading bing ads gem') diff --git a/lib/bing/ads/api/v13/constants.rb b/lib/bing/ads/api/v13/constants.rb new file mode 100644 index 0000000..e1590f1 --- /dev/null +++ b/lib/bing/ads/api/v13/constants.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Bing + module Ads + module API + # Bing::Ads::API::V13::Constants + module Constants + root_v13_path = File.expand_path('../', __FILE__) + + campaign_management_path = File.join(root_v13_path, 'constants', 'campaign_management.yml') + bulk_path = File.join(root_v13_path, 'constants', 'bulk.yml') + languages_path = File.join(root_v13_path, 'constants', 'languages.yml') + limits_path = File.join(root_v13_path, 'constants', 'limits.yml') + time_zones_path = File.join(root_v13_path, 'constants', 'time_zones.yml') + wsdl_path = File.join(root_v13_path, 'constants', 'wsdl.yml') + + Persey.init(:default) do + source :yaml, campaign_management_path, :campaign_management + source :yaml, bulk_path, :bulk + source :yaml, languages_path, :languages + source :yaml, limits_path, :limits + source :yaml, time_zones_path, :time_zones + source :yaml, wsdl_path, :wsdl + env :default + end + + Bing::Ads::API::V13.constants = Persey.config + end + end + end +end diff --git a/lib/bing/ads/api/v13/constants/bulk.yml b/lib/bing/ads/api/v13/constants/bulk.yml new file mode 100644 index 0000000..c1cf0d5 --- /dev/null +++ b/lib/bing/ads/api/v13/constants/bulk.yml @@ -0,0 +1,5 @@ +download_entities: + campaigns: 1 + ad_groups: 2 + ads: 3 + keywords: 4 diff --git a/lib/bing/ads/api/v11/constants/campaign_management.yml b/lib/bing/ads/api/v13/constants/campaign_management.yml similarity index 97% rename from lib/bing/ads/api/v11/constants/campaign_management.yml rename to lib/bing/ads/api/v13/constants/campaign_management.yml index 47cf1db..a92256d 100644 --- a/lib/bing/ads/api/v11/constants/campaign_management.yml +++ b/lib/bing/ads/api/v13/constants/campaign_management.yml @@ -52,9 +52,6 @@ campaign_status: budget_paused: BudgetPaused budget_and_manual_paused: BudgetAndManualPaused deleted: Deleted -pricing_model: - cpc: Cpc - cpm: Cpm keyword_editorial_statuses: active: Active disapproved: Disapproved @@ -66,7 +63,6 @@ keyword_statuses: inactive: Inactive match_types: broad: Broad - content: Content exact: Exact phrase: Phrase network: diff --git a/lib/bing/ads/api/v11/constants/languages.yml b/lib/bing/ads/api/v13/constants/languages.yml similarity index 100% rename from lib/bing/ads/api/v11/constants/languages.yml rename to lib/bing/ads/api/v13/constants/languages.yml diff --git a/lib/bing/ads/api/v11/constants/limits.yml b/lib/bing/ads/api/v13/constants/limits.yml similarity index 100% rename from lib/bing/ads/api/v11/constants/limits.yml rename to lib/bing/ads/api/v13/constants/limits.yml diff --git a/lib/bing/ads/api/v11/constants/time_zones.yml b/lib/bing/ads/api/v13/constants/time_zones.yml similarity index 92% rename from lib/bing/ads/api/v11/constants/time_zones.yml rename to lib/bing/ads/api/v13/constants/time_zones.yml index e7bd987..30f7374 100644 --- a/lib/bing/ads/api/v11/constants/time_zones.yml +++ b/lib/bing/ads/api/v13/constants/time_zones.yml @@ -1,7 +1,7 @@ abu_dhabi_muscat: 'AbuDhabiMuscat' adelaide: 'Adelaide' alaska: 'Alaska' -almaty_novosibirsk: 'Almaty_Novosibirsk' +almaty_novosibirsk: 'AlmatyNovosibirsk' amsterdam_berlin_bern_rome_stockholm_vienna: 'AmsterdamBerlinBernRomeStockholmVienna' arizona: 'Arizona' astana_dhaka: 'AstanaDhaka' @@ -51,11 +51,11 @@ kathmandu: 'Kathmandu' krasnoyarsk: 'Krasnoyarsk' kuala_lumpur_singapore: 'KualaLumpurSingapore' kuwait_riyadh: 'KuwaitRiyadh' -magadan_solomon_island_new_caledonia: 'MagadanSolomonIslandNewCaledonia' +solomon_island_new_caledonia: 'SolomonIslandNewCaledonia' mid_atlantic: 'MidAtlantic' -midway_islandand_samoa: 'MidwayIslandand_Samoa' +midway_islandand_samoa: 'MidwayIslandAndSamoa' moscow_st_petersburg_volgograd: 'MoscowStPetersburgVolgograd' -mountain_time_u_s_canada: 'MountainTime_US_Canada' +mountain_time_u_s_canada: 'MountainTimeUSCanada' nairobi: 'Nairobi' newfoundland: 'Newfoundland' nukualofa: 'Nukualofa' diff --git a/lib/bing/ads/api/v13/constants/wsdl.yml b/lib/bing/ads/api/v13/constants/wsdl.yml new file mode 100644 index 0000000..774499a --- /dev/null +++ b/lib/bing/ads/api/v13/constants/wsdl.yml @@ -0,0 +1,10 @@ +sandbox: + customer_management: "https://clientcenter.api.sandbox.bingads.microsoft.com/Api/CustomerManagement/v13/CustomerManagementService.svc?singleWsdl" + campaign_management: "https://campaign.api.sandbox.bingads.microsoft.com/Api/Advertiser/CampaignManagement/v13/CampaignManagementService.svc?singleWsdl" + reporting: "https://reporting.api.sandbox.bingads.microsoft.com/Api/Advertiser/Reporting/v13/ReportingService.svc?singleWsdl" + bulk: 'https://bulk.api.sandbox.bingads.microsoft.com/Api/Advertiser/CampaignManagement/v13/BulkService.svc?singleWsdl' +production: + customer_management: "https://clientcenter.api.bingads.microsoft.com/Api/CustomerManagement/v13/CustomerManagementService.svc?singleWsdl" + campaign_management: "https://campaign.api.bingads.microsoft.com/Api/Advertiser/CampaignManagement/v13/CampaignManagementService.svc?singleWsdl" + reporting: "https://reporting.api.bingads.microsoft.com/Api/Advertiser/Reporting/v13/ReportingService.svc?singleWsdl" + bulk: 'https://bulk.api.bingads.microsoft.com/Api/Advertiser/CampaignManagement/v13/BulkService.svc?singleWsdl' diff --git a/lib/bing/ads/api/v11/data.rb b/lib/bing/ads/api/v13/data.rb similarity index 82% rename from lib/bing/ads/api/v11/data.rb rename to lib/bing/ads/api/v13/data.rb index c44234d..9971057 100644 --- a/lib/bing/ads/api/v11/data.rb +++ b/lib/bing/ads/api/v13/data.rb @@ -3,3 +3,4 @@ require_relative './data/campaign' require_relative './data/keyword' require_relative './data/report_request' +require_relative './data/bulk_request' diff --git a/lib/bing/ads/api/v11/data/ad_group.rb b/lib/bing/ads/api/v13/data/ad_group.rb similarity index 64% rename from lib/bing/ads/api/v11/data/ad_group.rb rename to lib/bing/ads/api/v13/data/ad_group.rb index f3959ba..e6f9db2 100644 --- a/lib/bing/ads/api/v11/data/ad_group.rb +++ b/lib/bing/ads/api/v13/data/ad_group.rb @@ -1,35 +1,34 @@ module Bing module Ads module API - module V11 + module V13 module Data - # Bing::Ads::API::V11::Data::AdGroup + # Bing::Ads::API::V13::Data::AdGroup class AdGroup # @order # https://msdn.microsoft.com/en-us/library/bing-ads-campaign-management-adgroup.aspx - KEYS_ORDER = [ - :ad_distribution, - :ad_rotation, - :bidding_scheme, - :content_match_bid, - :end_date, - :forward_compatibility_map, - :id, - :language, - :name, - :native_bid_adjustment, - :network, - :pricing_model, - :remarketing_targeting_setting, - :search_bid, - :settings, - :start_date, - :status, - :tracking_url_template, - :url_custom_parameters - # Alphabetical - ] + # Alphabetical + KEYS_ORDER = %i[ + ad_distribution + ad_rotation + bidding_scheme + content_match_bid + end_date + forward_compatibility_map + id + language + name + native_bid_adjustment + network + remarketing_targeting_setting + search_bid + settings + start_date + status + tracking_url_template + url_custom_parameter + ].freeze def self.prepare(ad_group_raw) ad_group_raw[:ad_rotation] = { type: ad_group_raw[:ad_rotation] } if ad_group_raw[:ad_rotation] @@ -37,7 +36,7 @@ def self.prepare(ad_group_raw) # TODO support MaxClicksBiddingScheme, MaxConversionsBiddingScheme and TargetCpaBiddingScheme ad_group_raw[:bidding_scheme] = { type: ad_group_raw[:bidding_scheme], - '@xsi:type' => "#{Bing::Ads::API::V11::NAMESPACE_IDENTIFIER}:#{ad_group_raw[:bidding_scheme]}" + '@xsi:type' => "#{Bing::Ads::API::V13::NAMESPACE_IDENTIFIER}:#{ad_group_raw[:bidding_scheme]}" } end ad_group_raw[:content_match_bid] = { amount: ad_group_raw[:content_match_bid] } if ad_group_raw[:content_match_bid] diff --git a/lib/bing/ads/api/v13/data/bulk_request.rb b/lib/bing/ads/api/v13/data/bulk_request.rb new file mode 100644 index 0000000..ff7bfb4 --- /dev/null +++ b/lib/bing/ads/api/v13/data/bulk_request.rb @@ -0,0 +1,52 @@ +module Bing + module Ads + module API + module V13 + module Data + class BulkRequest + + KEYS_ORDER = %i[ + account_ids + compression_type + data_scope + download_entities + download_file_type + format_version + last_sync_time_in_utc + ].freeze + + class << self + def prepare(bulk_request_row) + bulk_request_row[:account_ids] = prepare_account_ids( + account_ids: bulk_request_row[:account_ids] + ) + bulk_request_row[:download_entities] = + prepare_download_entities( + levels: bulk_request_row.delete(:levels) + ) + bulk_request_row.except!(:levels) + bulk_request = Bing::Ads::Utils.sort_keys(bulk_request_row, KEYS_ORDER) + Bing::Ads::Utils.camelcase_keys(bulk_request) + end + + private + + def prepare_account_ids(account_ids:) + { + 'a1:long' => account_ids, + '@xmlns:a1' => 'http://schemas.microsoft.com/2003/10/Serialization/Arrays' + } + end + + def prepare_download_entities(levels:) + levels = %w[campaign] if levels.nil? || levels.empty? + entities = levels.map(&:pluralize).map(&:camelcase) + { download_entity: entities } + end + end + end + end + end + end + end +end diff --git a/lib/bing/ads/api/v11/data/campaign.rb b/lib/bing/ads/api/v13/data/campaign.rb similarity index 82% rename from lib/bing/ads/api/v11/data/campaign.rb rename to lib/bing/ads/api/v13/data/campaign.rb index ea80a3c..8f76764 100644 --- a/lib/bing/ads/api/v11/data/campaign.rb +++ b/lib/bing/ads/api/v13/data/campaign.rb @@ -1,23 +1,25 @@ module Bing module Ads module API - module V11 + module V13 module Data - # Bing::Ads::API::V11::Data::Campaign + # Bing::Ads::API::V13::Data::Campaign class Campaign # @order # https://msdn.microsoft.com/en-us/library/bing-ads-campaign-management-campaign.aspx KEYS_ORDER = [ + :audience_bid_adjustment, :bidding_scheme, :budget_type, :daily_budget, - :description, + :experiment_id, + :final_suffix_url, :forward_compatibility_map, :id, :name, - :native_bid_adjustment, :status, + :sub_type, :time_zone, :tracking_url_template, :url_custom_parameters, @@ -26,14 +28,14 @@ class Campaign :settings, :budget_id, :languages - ] + ].freeze def self.prepare(campaign_raw) if campaign_raw[:bidding_scheme] campaign_raw[:bidding_scheme] = { # TODO support MaxClicksBiddingScheme, MaxConversionsBiddingScheme and TargetCpaBiddingScheme type: campaign_raw[:bidding_scheme], - '@xsi:type' => "#{Bing::Ads::API::V11::NAMESPACE_IDENTIFIER}:#{campaign_raw[:bidding_scheme]}" + '@xsi:type' => "#{Bing::Ads::API::V13::NAMESPACE_IDENTIFIER}:#{campaign_raw[:bidding_scheme]}" } end # TODO UrlCustomParameters diff --git a/lib/bing/ads/api/v11/data/expanded_text_ad.rb b/lib/bing/ads/api/v13/data/expanded_text_ad.rb similarity index 52% rename from lib/bing/ads/api/v11/data/expanded_text_ad.rb rename to lib/bing/ads/api/v13/data/expanded_text_ad.rb index bcdf3e8..8e72bad 100644 --- a/lib/bing/ads/api/v11/data/expanded_text_ad.rb +++ b/lib/bing/ads/api/v13/data/expanded_text_ad.rb @@ -1,35 +1,30 @@ module Bing module Ads module API - module V11 + module V13 module Data - # Bing::Ads::API::V11::Data::ExpandedTextAd + # Bing::Ads::API::V13::Data::ExpandedTextAd class ExpandedTextAd # @order # https://msdn.microsoft.com/en-us/library/bing-ads-campaign-management-ad.aspx - KEYS_ORDER = [ - :ad_format_preference, - :device_preference, - :editorial_status, - :final_app_urls, - :final_mobile_urls, - :final_urls, - :forward_compatibility_map, - :id, - :status, - :tracking_url_template, - :url_custom_parameters, - :path_1, - :path_2, - :text, - :title_part_1, - :title_part_2, - :type - ] + KEYS_ORDER = %i[ + ad_format_preference + device_preference + editorial_status + final_app_urls + final_mobile_urls + final_urls + forward_compatibility_map + id + status + tracking_url_template + type + url_custom_parameters + ].freeze def self.prepare(ad_raw) - ad_raw['@xsi:type'] = "#{Bing::Ads::API::V11::NAMESPACE_IDENTIFIER}:#{ad_raw[:type]}" + ad_raw['@xsi:type'] = "#{Bing::Ads::API::V13::NAMESPACE_IDENTIFIER}:#{ad_raw[:type]}" # TODO FinalAppUrls ad_raw[:final_mobile_urls] = { 'ins1:string' => ad_raw[:final_mobile_urls] } if ad_raw[:final_mobile_urls] ad_raw[:final_urls] = { 'ins1:string' => ad_raw[:final_urls] } if ad_raw[:final_urls] diff --git a/lib/bing/ads/api/v11/data/keyword.rb b/lib/bing/ads/api/v13/data/keyword.rb similarity index 90% rename from lib/bing/ads/api/v11/data/keyword.rb rename to lib/bing/ads/api/v13/data/keyword.rb index e8f7438..07ec6d7 100644 --- a/lib/bing/ads/api/v11/data/keyword.rb +++ b/lib/bing/ads/api/v13/data/keyword.rb @@ -1,20 +1,21 @@ module Bing module Ads module API - module V11 + module V13 module Data - # Bing::Ads::API::V11::Data::Keyword + # Bing::Ads::API::V13::Data::Keyword class Keyword # @order # https://msdn.microsoft.com/en-us/library/bing-ads-campaign-management-keyword.aspx KEYS_ORDER = [ - :bidding_scheme, :bid, + :bidding_scheme, :destination_url, :editorial_status, :final_app_urls, :final_mobile_urls, + :final_url_suffix, :final_urls, :forward_compatibility_map, :id, @@ -26,8 +27,7 @@ class Keyword :text, :tracking_url_template, :url_custom_parameters - # Alphabetical - ] + ].freeze def self.prepare(keyword_raw) # To use the AdGroup default match type bid, @@ -37,7 +37,7 @@ def self.prepare(keyword_raw) # TODO support MaxClicksBiddingScheme, MaxConversionsBiddingScheme and TargetCpaBiddingScheme keyword_raw[:bidding_scheme] = { type: keyword_raw[:bidding_scheme], - '@xsi:type' => "#{Bing::Ads::API::V11::NAMESPACE_IDENTIFIER}:#{keyword_raw[:bidding_scheme]}" + '@xsi:type' => "#{Bing::Ads::API::V13::NAMESPACE_IDENTIFIER}:#{keyword_raw[:bidding_scheme]}" } end keyword_raw[:final_mobile_urls] = { 'ins1:string' => keyword_raw[:final_mobile_urls] } if keyword_raw[:final_mobile_urls] diff --git a/lib/bing/ads/api/v11/data/report_request.rb b/lib/bing/ads/api/v13/data/report_request.rb similarity index 82% rename from lib/bing/ads/api/v11/data/report_request.rb rename to lib/bing/ads/api/v13/data/report_request.rb index d6a4995..3a2ea53 100644 --- a/lib/bing/ads/api/v11/data/report_request.rb +++ b/lib/bing/ads/api/v13/data/report_request.rb @@ -1,33 +1,32 @@ module Bing module Ads module API - module V11 + module V13 module Data - # Bing::Ads::API::V11::Data::ReportRequest + # Bing::Ads::API::V13::Data::ReportRequest class ReportRequest # @order # https://msdn.microsoft.com/en-us/library/bing-ads-reporting-reportrequest.aspx - KEYS_ORDER = [ - :exclude_column_headers, - :exclude_report_footer, - :exclude_report_header, - :format, - :language, - :report_name, - :return_only_complete_data, - :aggregation, - :columns, - :filter, - :scope, - :time - ] + KEYS_ORDER = %i[ + exclude_column_headers + exclude_report_footer + exclude_report_header + format + report_name + return_only_complete_data + aggregation + columns + filter + scope + time + ].freeze class << self def prepare(type, report_request_raw) report_request_raw[:columns] = prepare_columns( columns: report_request_raw[:columns], - type: type.to_s.classify + type: type.to_s.camelcase ) report_request_raw[:scope] = prepare_scope( @@ -42,13 +41,13 @@ def prepare(type, report_request_raw) report_request_raw.except!(:from_date, :to_date, :account_ids) report_request = Bing::Ads::Utils.sort_keys(report_request_raw, KEYS_ORDER) - namespace_identifier = Bing::Ads::API::V11::NAMESPACE_IDENTIFIER + namespace_identifier = Bing::Ads::API::V13::NAMESPACE_IDENTIFIER { report_request: Bing::Ads::Utils.camelcase_keys(report_request), :attributes! => { report_request: { "xmlns:i" => "http://www.w3.org/2001/XMLSchema-instance", - "i:type" => "#{namespace_identifier}:#{type.to_s.classify}ReportRequest" + "i:type" => "#{namespace_identifier}:#{type.to_s.camelcase}ReportRequest" } } } diff --git a/lib/bing/ads/api/v11/services.rb b/lib/bing/ads/api/v13/services.rb similarity index 100% rename from lib/bing/ads/api/v11/services.rb rename to lib/bing/ads/api/v13/services.rb diff --git a/lib/bing/ads/api/v11/services/base.rb b/lib/bing/ads/api/v13/services/base.rb similarity index 97% rename from lib/bing/ads/api/v11/services/base.rb rename to lib/bing/ads/api/v13/services/base.rb index 89f7492..f9f3aab 100644 --- a/lib/bing/ads/api/v11/services/base.rb +++ b/lib/bing/ads/api/v13/services/base.rb @@ -1,9 +1,9 @@ module Bing module Ads module API - module V11 + module V13 module Services - # Bing::Ads::API::V11::Base + # Bing::Ads::API::V13::Base class Base attr_accessor :soap_client, :environment, :retry_attempts @@ -27,7 +27,7 @@ def initialize(options = {}) @customer_id = options[:customer_id] raise 'You must set the service environment' unless @environment options[:wsdl_url] = service_wsdl_url - options[:namespace_identifier] = Bing::Ads::API::V11::NAMESPACE_IDENTIFIER + options[:namespace_identifier] = Bing::Ads::API::V13::NAMESPACE_IDENTIFIER @soap_client = Bing::Ads::API::SOAPClient.new(options) end @@ -112,7 +112,7 @@ def service_name # # @return String with the Service url def service_wsdl_url - Bing::Ads::API::V11.constants.wsdl.send(environment).send(service_name) + Bing::Ads::API::V13.constants.wsdl.send(environment).send(service_name) end def handle_soap_fault(operation, fault_detail, key) diff --git a/lib/bing/ads/api/v13/services/bulk.rb b/lib/bing/ads/api/v13/services/bulk.rb new file mode 100644 index 0000000..e9bb80b --- /dev/null +++ b/lib/bing/ads/api/v13/services/bulk.rb @@ -0,0 +1,59 @@ +module Bing + module Ads + module API + module V13 + module Services + # Bing::Ads::API::V13::Services::Bulk + class Bulk < Base + def initialize(options = {}) + super(options) + end + + def get_bulk_upload_url(account_id = nil, response_mode = 'ErrorsOnly') + account_id ||= @account_id + + response = call(__method__, response_mode: response_mode, account_id: account_id) + response_body = response_body(response, __method__) + response_body.slice(:request_id, :upload_url) + end + + def get_bulk_upload_status(request_id) + response = call(__method__, request_id: request_id) + response_body = response_body(response, __method__) + response_body.slice(:errors, :percent_complete, :request_status, :result_file_url) + end + + def download_campaigns_by_account_ids(options) + payload = Bing::Ads::API::V13::Data::BulkRequest.prepare(options) + response = call(__method__, payload) + response_body = response_body(response, __method__) + response_body.slice(:download_request_id) + end + + # def download_campaigns_by_campaign_ids(options) + # options[:account_ids] ||= [@account_id] + # options[:download_entities] ||= [] + # response = call(__method__, response_mode: response_mode, account_id: account_id) + # response_body = response_body(response, __method__) + # response_body.slice(:request_id, :download_url) + # end + + def get_bulk_download_status(request_id) + response = call(__method__, request_id: request_id) + response_body = response_body(response, __method__) + response_body.slice(:errors, :percent_complete, :request_status, :result_file_url) + end + + # TODO operations: https://msdn.microsoft.com/en-us/library/bing-ads-bulk-service-operations.aspx + + private + + def service_name + 'bulk' + end + end + end + end + end + end +end diff --git a/lib/bing/ads/api/v11/services/campaign_management.rb b/lib/bing/ads/api/v13/services/campaign_management.rb similarity index 93% rename from lib/bing/ads/api/v11/services/campaign_management.rb rename to lib/bing/ads/api/v13/services/campaign_management.rb index 6587391..e785a4a 100644 --- a/lib/bing/ads/api/v11/services/campaign_management.rb +++ b/lib/bing/ads/api/v13/services/campaign_management.rb @@ -1,9 +1,9 @@ module Bing module Ads module API - module V11 + module V13 module Services - # Bing::Ads::API::V11::Services::CampaignManagement + # Bing::Ads::API::V13::Services::CampaignManagement class CampaignManagement < Base def initialize(options = {}) super(options) @@ -29,7 +29,7 @@ def get_campaigns_by_ids(account_id, campaign_ids) def add_campaigns(account_id, campaigns) validate_limits!(:campaign, :add, campaigns) - campaigns = campaigns.map { |campaign| Bing::Ads::API::V11::Data::Campaign.prepare(campaign) } + campaigns = campaigns.map { |campaign| Bing::Ads::API::V13::Data::Campaign.prepare(campaign) } payload = { account_id: account_id, campaigns: { campaign: campaigns } @@ -40,7 +40,7 @@ def add_campaigns(account_id, campaigns) def update_campaigns(account_id, campaigns) validate_limits!(:campaign, :update, campaigns) - campaigns = campaigns.map { |campaign| Bing::Ads::API::V11::Data::Campaign.prepare(campaign) } + campaigns = campaigns.map { |campaign| Bing::Ads::API::V13::Data::Campaign.prepare(campaign) } payload = { account_id: account_id, campaigns: { campaign: campaigns } @@ -78,7 +78,7 @@ def get_ad_groups_by_ids(campaign_id, ad_groups_ids) def add_ad_groups(campaign_id, ad_groups) validate_limits!(:ad_group, :add, ad_groups) - ad_groups = ad_groups.map { |ad_group| Bing::Ads::API::V11::Data::AdGroup.prepare(ad_group) } + ad_groups = ad_groups.map { |ad_group| Bing::Ads::API::V13::Data::AdGroup.prepare(ad_group) } payload = { campaign_id: campaign_id, ad_groups: { ad_group: ad_groups } @@ -89,7 +89,7 @@ def add_ad_groups(campaign_id, ad_groups) def update_ad_groups(campaign_id, ad_groups) validate_limits!(:ad_group, :update, ad_groups) - ad_groups = ad_groups.map { |ad_group| Bing::Ads::API::V11::Data::AdGroup.prepare(ad_group) } + ad_groups = ad_groups.map { |ad_group| Bing::Ads::API::V13::Data::AdGroup.prepare(ad_group) } payload = { campaign_id: campaign_id, ad_groups: { ad_group: ad_groups } @@ -142,7 +142,7 @@ def get_ads_by_ids(ad_group_id, ad_ids) def add_ads(ad_group_id, ads) validate_limits!(:ad, :add, ads) - ads = ads.map { |ad| Bing::Ads::API::V11::Data::ExpandedTextAd.prepare(ad) } + ads = ads.map { |ad| Bing::Ads::API::V13::Data::ExpandedTextAd.prepare(ad) } payload = { ad_group_id: ad_group_id, ads: { ad: ads } @@ -153,7 +153,7 @@ def add_ads(ad_group_id, ads) def update_ads(ad_group_id, ads) validate_limits!(:ad, :update, ads) - ads = ads.map { |ad| Bing::Ads::API::V11::Data::ExpandedTextAd.prepare(ad) } + ads = ads.map { |ad| Bing::Ads::API::V13::Data::ExpandedTextAd.prepare(ad) } payload = { ad_group_id: ad_group_id, ads: { ad: ads } @@ -190,7 +190,7 @@ def get_keywords_by_ids(ad_group_id, keyword_ids) def add_keywords(ad_group_id, keywords) validate_limits!(:keyword, :add, keywords) - keywords = keywords.map { |keyword| Bing::Ads::API::V11::Data::Keyword.prepare(keyword) } + keywords = keywords.map { |keyword| Bing::Ads::API::V13::Data::Keyword.prepare(keyword) } payload = { ad_group_id: ad_group_id, keywords: { keyword: keywords } @@ -201,7 +201,7 @@ def add_keywords(ad_group_id, keywords) def update_keywords(ad_group_id, keywords) validate_limits!(:keyword, :update, keywords) - keywords = keywords.map { |keyword| Bing::Ads::API::V11::Data::Keyword.prepare(keyword) } + keywords = keywords.map { |keyword| Bing::Ads::API::V13::Data::Keyword.prepare(keyword) } payload = { ad_group_id: ad_group_id, keywords: { keyword: keywords } @@ -310,7 +310,7 @@ def service_name end def validate_limits!(type, operation, array) - limit = Bing::Ads::API::V11.constants.limits.per_call.send(type) + limit = Bing::Ads::API::V13.constants.limits.per_call.send(type) if array.size > limit raise Bing::Ads::API::Errors::LimitError.new(operation, limit, type) end @@ -319,11 +319,11 @@ def validate_limits!(type, operation, array) def all_ad_types { ad_type: [ - Bing::Ads::API::V11.constants.campaign_management.ad_types_for_get.text, - Bing::Ads::API::V11.constants.campaign_management.ad_types_for_get.expanded_text, - Bing::Ads::API::V11.constants.campaign_management.ad_types_for_get.image, - Bing::Ads::API::V11.constants.campaign_management.ad_types_for_get.product, - Bing::Ads::API::V11.constants.campaign_management.ad_types_for_get.app_install + Bing::Ads::API::V13.constants.campaign_management.ad_types_for_get.text, + Bing::Ads::API::V13.constants.campaign_management.ad_types_for_get.expanded_text, + Bing::Ads::API::V13.constants.campaign_management.ad_types_for_get.image, + Bing::Ads::API::V13.constants.campaign_management.ad_types_for_get.product, + Bing::Ads::API::V13.constants.campaign_management.ad_types_for_get.app_install ] } end diff --git a/lib/bing/ads/api/v11/services/customer_management.rb b/lib/bing/ads/api/v13/services/customer_management.rb similarity index 92% rename from lib/bing/ads/api/v11/services/customer_management.rb rename to lib/bing/ads/api/v13/services/customer_management.rb index 0aab941..6ce6e87 100644 --- a/lib/bing/ads/api/v11/services/customer_management.rb +++ b/lib/bing/ads/api/v13/services/customer_management.rb @@ -1,9 +1,9 @@ module Bing module Ads module API - module V11 + module V13 module Services - # Bing::Ads::API::V11::Services::CustomerManagement + # Bing::Ads::API::V13::Services::CustomerManagement class CustomerManagement < Base def initialize(options = {}) super(options) diff --git a/lib/bing/ads/api/v11/services/reporting.rb b/lib/bing/ads/api/v13/services/reporting.rb similarity index 86% rename from lib/bing/ads/api/v11/services/reporting.rb rename to lib/bing/ads/api/v13/services/reporting.rb index 82922da..3ac52df 100644 --- a/lib/bing/ads/api/v11/services/reporting.rb +++ b/lib/bing/ads/api/v13/services/reporting.rb @@ -1,16 +1,16 @@ module Bing module Ads module API - module V11 + module V13 module Services - # Bing::Ads::API::V11::Services::CampaignManagement + # Bing::Ads::API::V13::Services::CampaignManagement class Reporting < Base def initialize(options = {}) super(options) end def submit_generate_report(type, report_options) - payload = Bing::Ads::API::V11::Data::ReportRequest.prepare(type, report_options) + payload = Bing::Ads::API::V13::Data::ReportRequest.prepare(type, report_options) response = call(:submit_generate_report, payload) response_body = response_body(response, __method__) response_body @@ -35,6 +35,9 @@ def report_url(report_request_id) end def report_body(report_request_id) + report_url = report_url(report_request_id) + return if report_url.nil? + HttpClient.download(report_url(report_request_id)) end diff --git a/lib/bing/ads/version.rb b/lib/bing/ads/version.rb index b2ead12..c5ab5f7 100644 --- a/lib/bing/ads/version.rb +++ b/lib/bing/ads/version.rb @@ -1,5 +1,5 @@ module Bing module Ads - VERSION = '12.0.0'.freeze + VERSION = '13.0.0'.freeze end end From 2cfb9fef47c44c691c7e2b923d92d71ba6ad5f52 Mon Sep 17 00:00:00 2001 From: Anant Prasad Date: Mon, 20 Jan 2020 17:57:59 +0530 Subject: [PATCH 04/18] Updated dependency close to rails 6 --- bing-ads.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bing-ads.gemspec b/bing-ads.gemspec index 56f1ba6..e836f94 100644 --- a/bing-ads.gemspec +++ b/bing-ads.gemspec @@ -22,7 +22,7 @@ Gem::Specification.new do |spec| spec.require_paths = ["lib"] spec.add_dependency 'savon', '~> 2.11' - spec.add_dependency 'activesupport', '~> 5.0' + spec.add_dependency 'activesupport', '<= 6.0.2' spec.add_dependency 'persey', '~> 1.0.0' spec.add_development_dependency "bundler", "~> 1.15" From 8ae266c74a3da7ce74676ffef13e45e47d3acbe6 Mon Sep 17 00:00:00 2001 From: Prashant Vithani Date: Sun, 31 May 2020 18:54:18 +0530 Subject: [PATCH 05/18] Update `activesupport` dependency version to allow minor version upgrade --- bing-ads.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bing-ads.gemspec b/bing-ads.gemspec index e836f94..8eef0c7 100644 --- a/bing-ads.gemspec +++ b/bing-ads.gemspec @@ -22,7 +22,7 @@ Gem::Specification.new do |spec| spec.require_paths = ["lib"] spec.add_dependency 'savon', '~> 2.11' - spec.add_dependency 'activesupport', '<= 6.0.2' + spec.add_dependency 'activesupport', '~> 6.0' spec.add_dependency 'persey', '~> 1.0.0' spec.add_development_dependency "bundler", "~> 1.15" From 605a7b9a89649f0daaed441b58e2474379ccf662 Mon Sep 17 00:00:00 2001 From: Rahil Momin Date: Thu, 16 Mar 2023 22:13:51 +0530 Subject: [PATCH 06/18] Add format_version as a valid parameter in report_request --- lib/bing/ads/api/v13/data/report_request.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/bing/ads/api/v13/data/report_request.rb b/lib/bing/ads/api/v13/data/report_request.rb index 3a2ea53..5a83527 100644 --- a/lib/bing/ads/api/v13/data/report_request.rb +++ b/lib/bing/ads/api/v13/data/report_request.rb @@ -13,6 +13,7 @@ class ReportRequest exclude_report_footer exclude_report_header format + format_version report_name return_only_complete_data aggregation From 2fd9109e40139b26cacbf708d6f5e423f700e474 Mon Sep 17 00:00:00 2001 From: Rahil Momin Date: Thu, 16 Mar 2023 22:39:07 +0530 Subject: [PATCH 07/18] Bump gem version --- lib/bing/ads/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bing/ads/version.rb b/lib/bing/ads/version.rb index c5ab5f7..fcebf42 100644 --- a/lib/bing/ads/version.rb +++ b/lib/bing/ads/version.rb @@ -1,5 +1,5 @@ module Bing module Ads - VERSION = '13.0.0'.freeze + VERSION = '13.0.1'.freeze end end From 0461acb37109e5e8795cdf3d25be7e664a3ad31a Mon Sep 17 00:00:00 2001 From: Ankur Gupta Date: Mon, 29 Jan 2024 14:28:30 +0530 Subject: [PATCH 08/18] Upgrade bundler/rspec/rake for SOC2 Make it same as that of adwyze --- bing-ads.gemspec | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bing-ads.gemspec b/bing-ads.gemspec index 8eef0c7..a5a07c3 100644 --- a/bing-ads.gemspec +++ b/bing-ads.gemspec @@ -25,7 +25,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'activesupport', '~> 6.0' spec.add_dependency 'persey', '~> 1.0.0' - spec.add_development_dependency "bundler", "~> 1.15" - spec.add_development_dependency "rake", "~> 10.0" - spec.add_development_dependency "rspec", "~> 3.0" + spec.add_development_dependency "bundler", "~> 2.3" + spec.add_development_dependency "rake", "~> 13.0" + spec.add_development_dependency "rspec", "~> 3.9" end From 7c6f6ca9b64604626cfa975a595c6ae7a57418b8 Mon Sep 17 00:00:00 2001 From: Muneeb V Date: Thu, 29 May 2025 03:29:22 +0000 Subject: [PATCH 09/18] fix(int, bing): categorize CallRateExceeded error as rate limit error --- lib/bing/ads/api/errors.rb | 7 +++++++ lib/bing/ads/api/v13/services/base.rb | 3 +++ 2 files changed, 10 insertions(+) diff --git a/lib/bing/ads/api/errors.rb b/lib/bing/ads/api/errors.rb index 4fbf964..81be1f9 100644 --- a/lib/bing/ads/api/errors.rb +++ b/lib/bing/ads/api/errors.rb @@ -20,6 +20,13 @@ def initialize(operation, limit, type) super("can not #{operation} more than #{limit} #{type.to_s.humanize.downcase.pluralize} in a single call") end end + + # Bing::Ads::API::Errors::RateLimitError + class RateLimitError < RuntimeError + def initialize(message = "Rate limit exceeded. Please try again later.") + super(message) + end + end end end end diff --git a/lib/bing/ads/api/v13/services/base.rb b/lib/bing/ads/api/v13/services/base.rb index f9f3aab..8f5fec2 100644 --- a/lib/bing/ads/api/v13/services/base.rb +++ b/lib/bing/ads/api/v13/services/base.rb @@ -121,6 +121,9 @@ def handle_soap_fault(operation, fault_detail, key) fault_detail[key][:errors][:ad_api_error][:error_code] == 'AuthenticationTokenExpired' raise Bing::Ads::API::Errors::AuthenticationTokenExpired, 'renew authentication token or obtain a new one.' + elsif fault_detail.dig(key, :errors, :ad_api_error, :error_code) == 'CallRateExceeded' + raise Bing::Ads::API::Errors::RateLimitError, + 'Rate limit exceeded. Please try again later.' else raise Bing::Ads::API::Errors::UnhandledSOAPFault, "SOAP error (#{fault_detail[key]}) while calling #{operation}." From 17f16cfd50615846a6afefacd34573127b0a54e2 Mon Sep 17 00:00:00 2001 From: Muneeb V Date: Thu, 29 May 2025 08:17:43 +0000 Subject: [PATCH 10/18] feat(bing): Add retry for ratelimit errors --- lib/bing/ads/api/v13/services/base.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/bing/ads/api/v13/services/base.rb b/lib/bing/ads/api/v13/services/base.rb index 8f5fec2..d3d0d05 100644 --- a/lib/bing/ads/api/v13/services/base.rb +++ b/lib/bing/ads/api/v13/services/base.rb @@ -73,6 +73,14 @@ def call(operation, payload) rescue Savon::InvalidResponseError => error # TODO better handling raise + rescue Bing::Ads::API::Errors::RateLimitError => error + if retries_made < retry_attempts + # https://learn.microsoft.com/en-us/advertising/guides/handle-service-errors-exceptions?view=bingads-13#code-117 + sleep(60 + rand(180)) + retries_made += 1 + retry + else + raise rescue if retries_made < retry_attempts sleep(2**retries_made) From 9edf55fc0d08de69a8ab6442608df76babf5123a Mon Sep 17 00:00:00 2001 From: Muneeb V Date: Thu, 29 May 2025 09:30:04 +0000 Subject: [PATCH 11/18] fix(bing): fix syntax error --- lib/bing/ads/api/v13/services/base.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/bing/ads/api/v13/services/base.rb b/lib/bing/ads/api/v13/services/base.rb index d3d0d05..0d9aa85 100644 --- a/lib/bing/ads/api/v13/services/base.rb +++ b/lib/bing/ads/api/v13/services/base.rb @@ -81,6 +81,7 @@ def call(operation, payload) retry else raise + end rescue if retries_made < retry_attempts sleep(2**retries_made) From 71356c02290edba6cd80c77be24464484f768029 Mon Sep 17 00:00:00 2001 From: Muneeb V Date: Thu, 29 May 2025 09:43:15 +0000 Subject: [PATCH 12/18] Bump gem version to 13.0.2 --- lib/bing/ads/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bing/ads/version.rb b/lib/bing/ads/version.rb index fcebf42..e5d977a 100644 --- a/lib/bing/ads/version.rb +++ b/lib/bing/ads/version.rb @@ -1,5 +1,5 @@ module Bing module Ads - VERSION = '13.0.1'.freeze + VERSION = '13.0.2'.freeze end end From 6defb5905d630de094bfeac63b4202b23a35c965 Mon Sep 17 00:00:00 2001 From: Muneeb V Date: Tue, 3 Jun 2025 05:42:49 +0000 Subject: [PATCH 13/18] fix(int, bing): Improve SOAP client error handling and retry logic Centralizes retry mechanisms and introduces dedicated retry handlers --- lib/bing/ads/api/errors.rb | 7 ++++++ lib/bing/ads/api/v13/services/base.rb | 33 +++++++++++++++++++-------- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/lib/bing/ads/api/errors.rb b/lib/bing/ads/api/errors.rb index 81be1f9..d577b38 100644 --- a/lib/bing/ads/api/errors.rb +++ b/lib/bing/ads/api/errors.rb @@ -27,6 +27,13 @@ def initialize(message = "Rate limit exceeded. Please try again later.") super(message) end end + + # Bing::Ads::API::Errors::RateLimitError + class BulkApiRateLimitError < RuntimeError + def initialize(message = "Bulk API Rate limit exceeded. Please try again later.") + super(message) + end + end end end end diff --git a/lib/bing/ads/api/v13/services/base.rb b/lib/bing/ads/api/v13/services/base.rb index 0d9aa85..67d11bd 100644 --- a/lib/bing/ads/api/v13/services/base.rb +++ b/lib/bing/ads/api/v13/services/base.rb @@ -47,6 +47,7 @@ def initialize(options = {}) def call(operation, payload) retries_made = 0 raise 'You must provide an operation' if operation.nil? + begin response = soap_client.call(operation: operation.to_sym, payload: payload) return response.hash @@ -73,24 +74,33 @@ def call(operation, payload) rescue Savon::InvalidResponseError => error # TODO better handling raise - rescue Bing::Ads::API::Errors::RateLimitError => error + rescue StandardError => error if retries_made < retry_attempts - # https://learn.microsoft.com/en-us/advertising/guides/handle-service-errors-exceptions?view=bingads-13#code-117 - sleep(60 + rand(180)) + handle_rate_limit_retry(error, retries_made) retries_made += 1 retry else raise end - rescue - if retries_made < retry_attempts - sleep(2**retries_made) - retries_made += 1 - retry + end + end + + # Handles the rate limit retry for the API calls. + # + # @param error - The error object + # @param retries_made - The number of retries made so far + def handle_rate_limit_retry(error, retries_made) + sleep_duration = case error + when Bing::Ads::API::Errors::BulkApiRateLimitError + # https://learn.microsoft.com/en-us/advertising/guides/operation-error-codes?view=bingads-13 + 900 + rand(180) # 15-18 minutes + when Bing::Ads::API::Errors::RateLimitError + # https://learn.microsoft.com/en-us/advertising/guides/handle-service-errors-exceptions?view=bingads-13#code-117 + 60 + rand(180) else - raise + 2**retries_made end - end + sleep(sleep_duration) end # Extracts the actual response from the entire response hash. @@ -133,6 +143,9 @@ def handle_soap_fault(operation, fault_detail, key) elsif fault_detail.dig(key, :errors, :ad_api_error, :error_code) == 'CallRateExceeded' raise Bing::Ads::API::Errors::RateLimitError, 'Rate limit exceeded. Please try again later.' + elsif fault_detail.dig(key, :operation_errors, :operation_error, :error_code) == 'BulkServiceNoMoreCallsPermittedForTheTimePeriod' + raise Bing::Ads::API::Errors::BulkApiRateLimitError, + 'Rate limit exceeded. Please try again later.' else raise Bing::Ads::API::Errors::UnhandledSOAPFault, "SOAP error (#{fault_detail[key]}) while calling #{operation}." From 9164f12edf27d2681c7bd5f5f6de6b60cf04b9a7 Mon Sep 17 00:00:00 2001 From: Muneeb V Date: Mon, 15 Sep 2025 12:08:42 +0000 Subject: [PATCH 14/18] Bump gem version to 13.0.3 I missed to update the version last time we made some changes to this repo. [history](https://github.com/clarisights/bing-ads/commits/master/) Updating the version so that adwyze code can pick it up once we update the version there. Intended changes were mostly about error handling on ratelimit errors. --- lib/bing/ads/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bing/ads/version.rb b/lib/bing/ads/version.rb index e5d977a..2acdb23 100644 --- a/lib/bing/ads/version.rb +++ b/lib/bing/ads/version.rb @@ -1,5 +1,5 @@ module Bing module Ads - VERSION = '13.0.2'.freeze + VERSION = '13.0.3'.freeze end end From fa5a642f0b3aa294766b0d22e38a860b2e898486 Mon Sep 17 00:00:00 2001 From: killbotXD Date: Wed, 5 Nov 2025 07:02:21 +0000 Subject: [PATCH 15/18] chore(deps): relax active support dependency --- bing-ads.gemspec | 2 +- lib/bing/ads/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bing-ads.gemspec b/bing-ads.gemspec index a5a07c3..4b17076 100644 --- a/bing-ads.gemspec +++ b/bing-ads.gemspec @@ -22,7 +22,7 @@ Gem::Specification.new do |spec| spec.require_paths = ["lib"] spec.add_dependency 'savon', '~> 2.11' - spec.add_dependency 'activesupport', '~> 6.0' + spec.add_dependency 'activesupport', '>= 6.0', '< 8.0' spec.add_dependency 'persey', '~> 1.0.0' spec.add_development_dependency "bundler", "~> 2.3" diff --git a/lib/bing/ads/version.rb b/lib/bing/ads/version.rb index 2acdb23..59399fd 100644 --- a/lib/bing/ads/version.rb +++ b/lib/bing/ads/version.rb @@ -1,5 +1,5 @@ module Bing module Ads - VERSION = '13.0.3'.freeze + VERSION = '13.0.4'.freeze end end From a571b03315ad22dd48092ceb6a088cba6b1ac07f Mon Sep 17 00:00:00 2001 From: Anmol Arora Date: Tue, 16 Jun 2026 07:33:28 +0000 Subject: [PATCH 16/18] feat: Treat AccountNotAuthorized as a typed exception Add AccountNotAuthorized error class and map operation_error code 'AccountNotAuthorized' to it in handle_soap_fault. This allows callers to distinguish account privilege errors from generic SOAP faults, enabling proper user-failure classification. Bump version: 13.0.4 -> 13.0.5 --- lib/bing/ads/api/errors.rb | 3 +++ lib/bing/ads/api/v13/services/base.rb | 4 ++++ lib/bing/ads/version.rb | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/bing/ads/api/errors.rb b/lib/bing/ads/api/errors.rb index d577b38..1d6da62 100644 --- a/lib/bing/ads/api/errors.rb +++ b/lib/bing/ads/api/errors.rb @@ -34,6 +34,9 @@ def initialize(message = "Bulk API Rate limit exceeded. Please try again later." super(message) end end + + # Bing::Ads::API::Errors::AccountNotAuthorized + class AccountNotAuthorized < RuntimeError; end; end end end diff --git a/lib/bing/ads/api/v13/services/base.rb b/lib/bing/ads/api/v13/services/base.rb index 67d11bd..fa93b61 100644 --- a/lib/bing/ads/api/v13/services/base.rb +++ b/lib/bing/ads/api/v13/services/base.rb @@ -146,6 +146,10 @@ def handle_soap_fault(operation, fault_detail, key) elsif fault_detail.dig(key, :operation_errors, :operation_error, :error_code) == 'BulkServiceNoMoreCallsPermittedForTheTimePeriod' raise Bing::Ads::API::Errors::BulkApiRateLimitError, 'Rate limit exceeded. Please try again later.' + elsif fault_detail.dig(key, :operation_errors, :operation_error, :error_code) == 'AccountNotAuthorized' + raise Bing::Ads::API::Errors::AccountNotAuthorized, + "Insufficient privileges to access one or more accounts in the report "\ + "request while calling #{operation}." else raise Bing::Ads::API::Errors::UnhandledSOAPFault, "SOAP error (#{fault_detail[key]}) while calling #{operation}." diff --git a/lib/bing/ads/version.rb b/lib/bing/ads/version.rb index 59399fd..ae10303 100644 --- a/lib/bing/ads/version.rb +++ b/lib/bing/ads/version.rb @@ -1,5 +1,5 @@ module Bing module Ads - VERSION = '13.0.4'.freeze + VERSION = '13.0.5'.freeze end end From 703ec997422c33bcd70d3228da90ae1267175f5d Mon Sep 17 00:00:00 2001 From: Vijay Kumar Date: Mon, 22 Jun 2026 18:56:57 +0530 Subject: [PATCH 17/18] fix: Treat UserIsNotAuthorized as a typed exception --- lib/bing/ads/api/errors.rb | 3 +++ lib/bing/ads/api/v13/services/base.rb | 3 +++ 2 files changed, 6 insertions(+) diff --git a/lib/bing/ads/api/errors.rb b/lib/bing/ads/api/errors.rb index 1d6da62..b3aab2a 100644 --- a/lib/bing/ads/api/errors.rb +++ b/lib/bing/ads/api/errors.rb @@ -37,6 +37,9 @@ def initialize(message = "Bulk API Rate limit exceeded. Please try again later." # Bing::Ads::API::Errors::AccountNotAuthorized class AccountNotAuthorized < RuntimeError; end; + + # Bing::Ads::API::Errors::UserIsNotAuthorized + class UserIsNotAuthorized < RuntimeError; end; end end end diff --git a/lib/bing/ads/api/v13/services/base.rb b/lib/bing/ads/api/v13/services/base.rb index fa93b61..f435a33 100644 --- a/lib/bing/ads/api/v13/services/base.rb +++ b/lib/bing/ads/api/v13/services/base.rb @@ -143,6 +143,9 @@ def handle_soap_fault(operation, fault_detail, key) elsif fault_detail.dig(key, :errors, :ad_api_error, :error_code) == 'CallRateExceeded' raise Bing::Ads::API::Errors::RateLimitError, 'Rate limit exceeded. Please try again later.' + elsif fault_detail.dig(key, :errors, :ad_api_error, :error_code) == 'UserIsNotAuthorized' + raise Bing::Ads::API::Errors::UserIsNotAuthorized, + "User is not authorized to perform this operation while calling #{operation}." elsif fault_detail.dig(key, :operation_errors, :operation_error, :error_code) == 'BulkServiceNoMoreCallsPermittedForTheTimePeriod' raise Bing::Ads::API::Errors::BulkApiRateLimitError, 'Rate limit exceeded. Please try again later.' From 368e2c0ed288b97bc7c763a24ee937ac91ea9931 Mon Sep 17 00:00:00 2001 From: Vijay Kumar Date: Tue, 23 Jun 2026 15:18:36 +0530 Subject: [PATCH 18/18] fix: Bump gem version post user perms error classification change Ref: https://github.com/clarisights/bing-ads/pull/14 --- lib/bing/ads/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bing/ads/version.rb b/lib/bing/ads/version.rb index ae10303..7d599df 100644 --- a/lib/bing/ads/version.rb +++ b/lib/bing/ads/version.rb @@ -1,5 +1,5 @@ module Bing module Ads - VERSION = '13.0.5'.freeze + VERSION = '13.0.6'.freeze end end