bing_ads_api 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. data/README.md +57 -0
  2. data/lib/ads_common_for_bing_ads.rb +49 -0
  3. data/lib/ads_common_for_bing_ads/api_config.rb +41 -0
  4. data/lib/ads_common_for_bing_ads/auth/client_login_handler.rb +168 -0
  5. data/lib/ads_common_for_bing_ads/build/savon_registry.rb +15 -0
  6. data/lib/ads_common_for_bing_ads/parameters_validator.rb +36 -0
  7. data/lib/ads_common_for_bing_ads/savon_headers_base_header_handler.rb +9 -0
  8. data/lib/ads_common_for_bing_ads/savon_service.rb +141 -0
  9. data/lib/bing_ads_api.rb +175 -0
  10. data/lib/bing_ads_api/api_config.rb +162 -0
  11. data/lib/bing_ads_api/client_login_header_handler.rb +21 -0
  12. data/lib/bing_ads_api/credential_handler.rb +91 -0
  13. data/lib/bing_ads_api/errors.rb +564 -0
  14. data/lib/bing_ads_api/report_header_handler.rb +46 -0
  15. data/lib/bing_ads_api/report_utils.rb +202 -0
  16. data/lib/bing_ads_api/v7/administration_service.rb +37 -0
  17. data/lib/bing_ads_api/v7/administration_service_registry.rb +30 -0
  18. data/lib/bing_ads_api/v7/campaign_management_service.rb +398 -0
  19. data/lib/bing_ads_api/v7/campaign_management_service_registry.rb +30 -0
  20. data/lib/bing_ads_api/v7/customer_billing_service.rb +58 -0
  21. data/lib/bing_ads_api/v7/customer_billing_service_registry.rb +30 -0
  22. data/lib/bing_ads_api/v7/customer_management_service.rb +98 -0
  23. data/lib/bing_ads_api/v7/customer_management_service_registry.rb +30 -0
  24. data/lib/bing_ads_api/v7/notification_service.rb +38 -0
  25. data/lib/bing_ads_api/v7/notification_service_registry.rb +30 -0
  26. data/lib/bing_ads_api/v7/reporting_service.rb +38 -0
  27. data/lib/bing_ads_api/v7/reporting_service_registry.rb +30 -0
  28. data/lib/bing_ads_api/v8/ad_intelligence_service.rb +86 -0
  29. data/lib/bing_ads_api/v8/ad_intelligence_service_registry.rb +30 -0
  30. data/lib/bing_ads_api/v8/administration_service.rb +38 -0
  31. data/lib/bing_ads_api/v8/administration_service_registry.rb +30 -0
  32. data/lib/bing_ads_api/v8/bulk_service.rb +42 -0
  33. data/lib/bing_ads_api/v8/bulk_service_registry.rb +30 -0
  34. data/lib/bing_ads_api/v8/campaign_management_service.rb +390 -0
  35. data/lib/bing_ads_api/v8/campaign_management_service_registry.rb +30 -0
  36. data/lib/bing_ads_api/v8/customer_billing_service.rb +62 -0
  37. data/lib/bing_ads_api/v8/customer_billing_service_registry.rb +30 -0
  38. data/lib/bing_ads_api/v8/customer_management_service.rb +162 -0
  39. data/lib/bing_ads_api/v8/customer_management_service_registry.rb +30 -0
  40. data/lib/bing_ads_api/v8/notification_service.rb +38 -0
  41. data/lib/bing_ads_api/v8/notification_service_registry.rb +30 -0
  42. data/lib/bing_ads_api/v8/optimizer_service.rb +46 -0
  43. data/lib/bing_ads_api/v8/optimizer_service_registry.rb +30 -0
  44. data/lib/bing_ads_api/v8/reporting_service.rb +38 -0
  45. data/lib/bing_ads_api/v8/reporting_service_registry.rb +30 -0
  46. data/lib/bing_ads_api/version.rb +5 -0
  47. data/rakefile.rb +54 -0
  48. metadata +156 -0
@@ -0,0 +1,57 @@
1
+ Bing Ads Api Client
2
+ =============
3
+
4
+ Description
5
+ -------------
6
+ A simple ruby wrapper for the Bing Ads API based on the gem "google-ads-common".
7
+
8
+ Usage
9
+ -------------
10
+
11
+ # install in your gem file
12
+ gem 'bing_ads_api', :git => 'https://github.com/weboglobin/bing_ads_api.git'
13
+
14
+ # initialize the client
15
+ client = BingAdsApi::Api.new(
16
+ {
17
+ :authentication => {
18
+ :method => 'ClientLogin',
19
+ :developer_token => 'DEVELOPER_TOKEN',
20
+ :user_name => 'USERNAME',
21
+ :password => 'PASSWORD',
22
+ :customer_id => 'customer_id', # may be optional for some requests
23
+ :customer_account_id => 'customer_account_id' # may be optional for some requests
24
+ },
25
+ :service => {:environment => 'PRODUCTION'},
26
+ :library => {:log_level => 'DEBUG'}
27
+ })
28
+
29
+ # select the service and the API version (:v7 or :v8)
30
+ administration_service = client.service(:AdministrationService, :v7)
31
+
32
+ # send your request
33
+ result = administration_service.get_assigned_quota()
34
+
35
+ # select another service
36
+ campaign_service = client.service(:CampaignManagementService, :v7)
37
+
38
+ # send another request
39
+ result = campaign_service.get_campaigns_by_account_id({:account_id => 00000})
40
+
41
+ # create a campaign
42
+ result2 = campaign_service.add_campaigns({:account_id => 00000,
43
+ :campaigns => {:campaign => [{:budget_type => "DailyBudgetWithMaximumMonthlySpend",
44
+ :conversion_tracking_enabled => false,
45
+ :daily_budget => 5,
46
+ :daylight_saving => false,
47
+ :description => "A perfect new campaign",
48
+ :monthly_budget => 50,
49
+ :name => "perfectcampaign",
50
+ :time_zone => "BrusselsCopenhagenMadridParis"}]
51
+ }
52
+ })
53
+
54
+ About the gem
55
+ -------------
56
+ The tests still have to be written.
57
+ Any help is welcome !
@@ -0,0 +1,49 @@
1
+ require 'ads_common/api'
2
+ require 'ads_common/api_config'
3
+ require 'ads_common/credential_handler'
4
+ require 'ads_common/errors'
5
+ require 'ads_common/http'
6
+ require 'ads_common/savon_service'
7
+ require 'ads_common/parameters_validator'
8
+ require 'ads_common/build/savon_generator'
9
+ require 'ads_common/build/savon_registry'
10
+ require 'ads_common/savon_headers/base_header_handler'
11
+ require 'ads_common/savon_headers/oauth_header_handler'
12
+ require 'ads_common/savon_headers/httpi_request_proxy'
13
+
14
+ ####### AdsCommon Overriden for Bing Ads #########
15
+ module AdsCommonForBingAds
16
+ class Api < AdsCommon::Api; end
17
+ module ApiConfig; include AdsCommon::ApiConfig; end
18
+ class Config < AdsCommon::Config; end
19
+ class CredentialHandler < AdsCommon::CredentialHandler; end
20
+ module Errors; include AdsCommon::Errors; end
21
+ class Http < AdsCommon::Http; end
22
+ class ParametersValidator < AdsCommon::ParametersValidator; end
23
+ class ResultsExtractor < AdsCommon::ResultsExtractor; end
24
+ class SavonService < AdsCommon::SavonService; end
25
+ module Utils; include AdsCommon::Utils; end
26
+ module ApiConfig; include AdsCommon::ApiConfig; end
27
+ # Auth
28
+ module Auth
29
+ class BaseHandler < AdsCommon::Auth::BaseHandler; end
30
+ class ClientLoginHandler < AdsCommon::Auth::ClientLoginHandler; end
31
+ class OAuth2Handler < AdsCommon::Auth::OAuth2Handler; end
32
+ class OAuthHandler < AdsCommon::Auth::OAuthHandler; end
33
+ end
34
+ # Build
35
+ module Build
36
+ class SavonAbstractGenerator < AdsCommon::Build::SavonAbstractGenerator; end
37
+ class SavonGenerator < AdsCommon::Build::SavonGenerator; end
38
+ class SavonRegistry < AdsCommon::Build::SavonRegistry; end
39
+ class SavonRegistryGenerator < AdsCommon::Build::SavonRegistryGenerator; end
40
+ class SavonServiceGenerator < AdsCommon::Build::SavonServiceGenerator; end
41
+ end
42
+ # SavonHeaders
43
+ module SavonHeaders
44
+ class BaseHeaderHandler < AdsCommon::SavonHeaders::BaseHeaderHandler; end
45
+ class OAuthHeaderHandler < AdsCommon::SavonHeaders::OAuthHeaderHandler; end
46
+ # httpi_request_proxy is ::OAuth::RequestProxy::HTTPIRequest < OAuth::RequestProxy::Base
47
+ end
48
+
49
+ end
@@ -0,0 +1,41 @@
1
+ ####### Overriden for Bing Ads #########
2
+ AdsCommonForBingAds::ApiConfig.module_eval do
3
+ # Get the endpoint for a service on a given environment and API version.
4
+ #
5
+ # Args:
6
+ # - environment: the service environment to be used
7
+ # - version: the API version
8
+ # - service: the name of the API service
9
+ #
10
+ # Returns:
11
+ # The endpoint URL
12
+ #
13
+ def endpoint(environment, version, service)
14
+ if !address_config().nil?
15
+ address_config()[version][service][environment].to_s
16
+ else
17
+ ""
18
+ end
19
+ end
20
+ # Generates an array of WSDL URLs based on defined Services and version
21
+ # supplied. This method is used by generators to determine what service
22
+ # wrappers to generate.
23
+ #
24
+ # Args:
25
+ # - version: the API version.
26
+ #
27
+ # Returns
28
+ # hash of pairs Service => WSDL URL
29
+ #
30
+ def get_wsdls(version)
31
+ res = {}
32
+ services(version).each do |service|
33
+ if (!address_config().nil?)
34
+ path = address_config()[version][service][default_environment()].to_s
35
+ end
36
+ res[service.to_s] = path || ""
37
+ end
38
+ return res
39
+ end
40
+
41
+ end
@@ -0,0 +1,168 @@
1
+ ####### Overriden for Bing Ads #########
2
+
3
+ AdsCommonForBingAds::Auth::ClientLoginHandler.class_eval do
4
+
5
+ #ACCOUNT_TYPE = 'GOOGLE'
6
+ #AUTH_PATH = '/accounts/ClientLogin'
7
+ #AUTH_PREFIX = 'GoogleLogin auth='
8
+ #CAPTCHA_PATH = '/accounts/'
9
+
10
+ # Initializes the ClientLoginHandler with all the necessary details.
11
+ def initialize(config, auth_server, service_name)
12
+ super(config)
13
+ @server = auth_server
14
+ @service_name = service_name
15
+ end
16
+
17
+ # Invalidates the stored token if the user_name, password or provided auth
18
+ # token have changed.
19
+ def property_changed(prop, value)
20
+ if [:user_name, :password].include?(prop)
21
+ @token = nil
22
+ end
23
+ if :auth_token.eql?(prop)
24
+ @token = create_token_from_string(value)
25
+ end
26
+ end
27
+
28
+ # Handle specific ClientLogin errors.
29
+ def handle_error(error)
30
+ # TODO: Add support for automatically regenerating auth tokens when they
31
+ # expire.
32
+ get_logger().error(error)
33
+ raise error
34
+ end
35
+
36
+ # Returns authorization string.
37
+ def auth_string(credentials, request)
38
+ return [AUTH_PREFIX, get_token(credentials)].join
39
+ end
40
+
41
+ private
42
+
43
+ # Auxiliary method to validate the credentials for token generation.
44
+ #
45
+ # Args:
46
+ # - credentials: a hash with the credentials for the account being
47
+ # accessed
48
+ #
49
+ # Raises:
50
+ # - AdsCommonForBingAds::Errors::AuthError if validation fails
51
+ #
52
+ def validate_credentials(credentials)
53
+ if credentials.nil?
54
+ raise AdsCommonForBingAds::Errors::AuthError, 'No credentials supplied.'
55
+ end
56
+
57
+ if credentials[:auth_token].nil?
58
+ if credentials[:user_name].nil?
59
+ raise AdsCommonForBingAds::Errors::AuthError,
60
+ 'UserName address not included in credentials.'
61
+ end
62
+ if credentials[:password].nil?
63
+ raise AdsCommonForBingAds::Errors::AuthError,
64
+ 'Password not included in credentials.'
65
+ end
66
+ else
67
+ if credentials[:user_name] and credentials[:password]
68
+ get_logger().warn('Both auth_token and login credentials present' +
69
+ ', preferring auth_token.')
70
+ end
71
+ end
72
+ end
73
+
74
+ # Auxiliary method to generate an authentication token for login in via
75
+ # the ClientLogin API.
76
+ #
77
+ # Args:
78
+ # - credentials: a hash with the credentials for the account being
79
+ # accessed
80
+ #
81
+ # Returns:
82
+ # - The auth token for the account
83
+ #
84
+ # Raises:
85
+ # - AdsCommonForBingAds::Errors::AuthError if authentication fails
86
+ #
87
+ def create_token(credentials)
88
+ token = credentials.include?(:auth_token) ?
89
+ create_token_from_string(credentials[:auth_token]) :
90
+ generate_token(credentials)
91
+ return token
92
+ end
93
+
94
+ # Creates token for provided auth string. Trivial for this handler.
95
+ def create_token_from_string(token_string)
96
+ return token_string
97
+ end
98
+
99
+ # Prepares POST data for ClientLogin request.
100
+ def get_login_data(credentials)
101
+ user_name = CGI.escape(credentials[:user_name])
102
+ password = CGI.escape(credentials[:password])
103
+ service_name = @service_name
104
+ data = "accountType=%s&Email=%s&Passwd=%s&service=%s" %
105
+ [ACCOUNT_TYPE, user_name, password, service_name]
106
+ if credentials[:logintoken] and credentials[:logincaptcha]
107
+ data += "&logintoken=%s&logincaptcha=%s" %
108
+ [CGI.escape(credentials[:logintoken]),
109
+ CGI.escape(credentials[:logincaptcha])]
110
+ end
111
+ return data
112
+ end
113
+
114
+ # Generates new client login token based on credentials.
115
+ def generate_token(credentials)
116
+ validate_credentials(credentials)
117
+
118
+ url = @server + AUTH_PATH
119
+ data = get_login_data(credentials)
120
+ headers = {'Content-Type' => 'application/x-www-form-urlencoded'}
121
+
122
+ response = AdsCommonForBingAds::Http.post_response(url, data, @config, headers)
123
+ results = parse_token_text(response.body)
124
+
125
+ if response.code == 200 and results.include?('Auth')
126
+ return results['Auth']
127
+ else
128
+ handle_login_error(credentials, response, results)
129
+ end
130
+ end
131
+
132
+ # Raises relevant error based on response and parsed results.
133
+ def handle_login_error(credentials, response, results)
134
+ # Handling for known errors.
135
+ if 'CaptchaRequired'.eql?(results['Error'])
136
+ captcha_url = @server + CAPTCHA_PATH + results['CaptchaUrl']
137
+ raise AdsCommonForBingAds::Errors::CaptchaRequiredError.new(results['Error'],
138
+ results['CaptchaToken'], captcha_url, results['Url'])
139
+ end
140
+ # For other errors throwing a generic error.
141
+ error_message = "ClientLogin failed for user_name '%s': HTTP code %d." %
142
+ [credentials[:user_name], response.code]
143
+ error_str = results['Error'] || response.body
144
+ error_message += " Error: %s." % error_str if error_str
145
+ if results.include?('Info')
146
+ error_message += " Info: %s." % results['Info']
147
+ end
148
+ raise AdsCommonForBingAds::Errors::AuthError.new(error_message, error_str,
149
+ results['Info'])
150
+ end
151
+
152
+ # Extracts key-value pairs from ClientLogin server response.
153
+ #
154
+ # Args:
155
+ # - text: server response string
156
+ #
157
+ # Returns:
158
+ # Hash of key-value pairs
159
+ #
160
+ def parse_token_text(text)
161
+ return text.split("\n").inject({}) do |result, line|
162
+ key, *values = line.split('=')
163
+ result[key] = values.join('=')
164
+ result
165
+ end
166
+ end
167
+
168
+ end
@@ -0,0 +1,15 @@
1
+
2
+ class AdsCommonForBingAds::Build::SavonRegistry
3
+
4
+ # Extracts input parameters of given method as an array.
5
+ def extract_input_parameters(op_node, doc)
6
+ input_element = REXML::XPath.first(op_node, 'descendant::wsdl:input')
7
+ input_name = get_element_name(input_element)
8
+ input_fields = find_sequence_fields(input_name, doc)
9
+ return {:name => input_name.snakecase, :fields => input_fields}
10
+ end
11
+
12
+
13
+
14
+
15
+ end
@@ -0,0 +1,36 @@
1
+
2
+ AdsCommonForBingAds::ParametersValidator.class_eval do
3
+
4
+ # Validates input parameters to:
5
+ # - add parameter names;
6
+ # - resolve xsi:type where required;
7
+ # - convert some native types to XML.
8
+ def validate_args(action_name, args)
9
+ in_params = @registry.get_method_signature(action_name)[:input] # Hash like {:name=>"get_accounts_info_request", :fields=>[]}
10
+ # TODO: compare number of parameters.
11
+ args_hash = args#{in_params[:name] => deep_copy(args)}
12
+ #validate_arguments(args_hash, in_params)
13
+ return args_hash
14
+ end
15
+
16
+ private
17
+
18
+ # Validates given arguments based on provided fields list.
19
+ def validate_arguments(args_hash, fields_list, type_ns = nil)
20
+ check_extra_fields(args_hash, array_from_named_list(fields_list))
21
+ add_order_key(args_hash, fields_list)
22
+ fields_list.each do |field|
23
+ key = field[:name]
24
+ item = args_hash[key]
25
+ check_required_argument_present(item, field)
26
+ if item
27
+ item_type = get_full_type_signature(field[:type])
28
+ item_ns = field[:ns] || type_ns
29
+ key = handle_namespace_override(args_hash, key, item_ns) if item_ns
30
+ validate_arg(item, args_hash, key, item_type)
31
+ end
32
+ end
33
+ return args_hash
34
+ end
35
+
36
+ end
@@ -0,0 +1,9 @@
1
+
2
+ class AdsCommonForBingAds::SavonHeaders::BaseHeaderHandler
3
+
4
+ # Generates SOAP headers with the default request header element.
5
+ def generate_headers(request, soap)
6
+ soap.header.merge!(generate_request_header())
7
+ end
8
+
9
+ end
@@ -0,0 +1,141 @@
1
+ ####### Overriden for Bing Ads #########
2
+
3
+ class AdsCommonForBingAds::SavonService < AdsCommon::SavonService
4
+
5
+ private
6
+
7
+ # [CHANGES:
8
+ # => ParametersValidator to AdsCommonForBingAds::ParametersValidator
9
+ # => ResultsExtractor to AdsCommon::ResultsExtractor]
10
+ # Executes SOAP action specified as a string with given arguments.
11
+ def execute_action(action_name, args, &block)
12
+ registry = get_service_registry()
13
+ validator = AdsCommonForBingAds::ParametersValidator.new(registry)
14
+ args = validator.validate_args(action_name, args)
15
+ response = execute_soap_request(
16
+ action_name.to_sym, args, validator.extra_namespaces)
17
+ log_headers(response.http.headers)
18
+ handle_errors(response)
19
+ extractor = AdsCommon::ResultsExtractor.new(registry)
20
+ result = extractor.extract_result(response, action_name, &block)
21
+ run_user_block(extractor, response, result, &block) if block_given?
22
+ return result
23
+ end
24
+
25
+ # Adds namespace to the given string.
26
+ #
27
+ # Args:
28
+ # - str: String to prepend with a namespace
29
+ #
30
+ # Returns:
31
+ # - String with a namespace
32
+ #
33
+ def prepend_namespace(str, namespace = AdsCommonForBingAds::SavonHeaders::BaseHeaderHandler::DEFAULT_NAMESPACE)
34
+ return "%s:%s" % [namespace, str]
35
+ end
36
+
37
+ # Executes the SOAP request with original SOAP name.
38
+ def execute_soap_request(action, args, extra_namespaces)
39
+ ns = AdsCommonForBingAds::SavonHeaders::BaseHeaderHandler::DEFAULT_NAMESPACE
40
+ original_input_name = get_service_registry.get_method_signature(action)[:input][:name].to_s.camelize
41
+ original_action_name = get_service_registry.get_method_signature(action)[:original_name].to_s.camelize
42
+ original_action_name = action if original_action_name.nil?
43
+ args = args.first if args.is_a?(Array)
44
+ additional_headers = args.delete(:headers) unless args.nil?
45
+ prepend_namespace_to_hash(args, ns)
46
+ prepend_namespace_to_hash(additional_headers, ns)
47
+ puts "**************************************args = #{args}"
48
+ response = @client.request(ns, original_input_name) do |soap|
49
+ @client.http.headers["SOAPAction"] = original_action_name
50
+ soap.body = args
51
+ set_headers(soap, extra_namespaces)
52
+ soap.header.merge!(additional_headers) if additional_headers.is_a?(Hash)
53
+ end
54
+ return response
55
+ end
56
+ def prepend_namespace_to_hash h, namespace
57
+ if h.is_a?(Hash)
58
+ h.dup.each do |k,v|
59
+ h.delete(k)
60
+ if k.to_s=~/!$/ || k.to_s=~/:/ #on ne transforme pas les noms finissant par ! ou contenant : (exemple: attributes! ou xsi:type)
61
+ h[k] = prepend_namespace_to_hash(v, namespace)
62
+ else
63
+ h[k.to_s=~ /^#{namespace}:/ ? k : prepend_namespace(k.to_s.camelize, namespace)] = prepend_namespace_to_hash(v, namespace)
64
+ end
65
+ end
66
+ elsif h.is_a?(Array) # e.g: h = {:campaigns => {:campaign => [{:name => 'foo'}, {:name => 'foofoo'}]}}
67
+ h.map!{|e| prepend_namespace_to_hash(e, namespace)}
68
+ end
69
+ return h
70
+ end
71
+
72
+ # Creates and sets up Savon client.
73
+ def create_savon_client(endpoint, namespace)
74
+ Nori.advanced_typecasting = false
75
+ client = Savon::Client.new do |wsdl, httpi|
76
+ wsdl.endpoint = endpoint
77
+ wsdl.namespace = namespace
78
+ wsdl.element_form_default = :qualified
79
+ AdsCommonForBingAds::Http.configure_httpi(@config, httpi)
80
+ end
81
+ client.config.raise_errors = false
82
+ client.config.logger.subject = get_logger()
83
+ return client
84
+ end
85
+
86
+ # Finds an exception object for a given response.
87
+ def exception_for_soap_fault(response)
88
+ begin
89
+ fault = response[:fault]
90
+ if fault[:detail] and fault[:detail][:api_exception_fault]
91
+ exception_fault = fault[:detail][:api_exception_fault]
92
+ exception_name = exception_fault[:application_exception_type]
93
+ exception_class = get_module().const_get(exception_name)
94
+ return exception_class.new(exception_fault)
95
+ # Specific to Bing Ads
96
+ elsif fault[:detail] and fault[:detail][:api_fault]
97
+ operation_error = fault[:detail][:api_fault][:operation_errors][:operation_error]
98
+ operation_error = operation_error.first if operation_error.is_a?(Array) # if we get several errors, we only raise the first one
99
+ if exception_name = BingAdsApi::Errors::CODES[operation_error[:code]]
100
+ exception_class = BingAdsApi::Errors.const_get(exception_name)
101
+ else
102
+ raise Exception.new("code #{operation_error[:code]}")
103
+ end
104
+ return exception_class.new("#{operation_error[:message]} (#{operation_error[:details]})")
105
+ # Specific to Bing Ads (batches)
106
+ elsif fault[:detail] and fault[:detail][:api_fault_detail]
107
+ operation_error = fault[:detail][:api_fault_detail][:batch_errors][:batch_error]
108
+ operation_error = operation_error.first if operation_error.is_a?(Array) # if we get several errors, we only raise the first one
109
+ if exception_name = BingAdsApi::Errors::CODES[operation_error[:code]]
110
+ exception_class = BingAdsApi::Errors.const_get(exception_name)
111
+ else
112
+ raise Exception.new("code #{operation_error[:code]}")
113
+ end
114
+ return exception_class.new("#{operation_error[:message]} (#{operation_error[:details]})")
115
+ # Specific to Bing Ads (ad api)
116
+ elsif fault[:detail] and fault[:detail][:ad_api_fault_detail]
117
+ operation_error = fault[:detail][:ad_api_fault_detail][:errors][:ad_api_error]
118
+ operation_error = operation_error.first if operation_error.is_a?(Array) # if we get several errors, we only raise the first one
119
+ if exception_name = BingAdsApi::Errors::CODES[operation_error[:code]]
120
+ exception_class = BingAdsApi::Errors.const_get(exception_name)
121
+ else
122
+ raise Exception.new("code #{operation_error[:code]}")
123
+ end
124
+ return exception_class.new("#{operation_error[:message]} (#{operation_error[:details]})")
125
+ elsif fault[:faultstring]
126
+ fault_message = fault[:faultstring]
127
+ return AdsCommonForBingAds::Errors::ApiException.new(
128
+ "Unknown exception with error: %s" % fault_message)
129
+ else
130
+ raise ArgumentError.new(fault.to_s)
131
+ end
132
+ rescue Exception => e
133
+ operation_error ||= response[:fault][:detail][:api_fault][:operation_errors][:operation_error] rescue {}
134
+ return AdsCommonForBingAds::Errors::ApiException.new(
135
+ "Failed to resolve exception (%s), details: %s, SOAP fault: %s" %
136
+ [e.message, "#{operation_error[:message]} (#{operation_error[:details]})", response.soap_fault])
137
+ end
138
+ end
139
+
140
+
141
+ end