bing-ads-api 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +92 -0
  3. data/Rakefile +38 -0
  4. data/lib/bing-ads-api.rb +38 -0
  5. data/lib/bing-ads-api.yml +345 -0
  6. data/lib/bing-ads-api/api_exception.rb +42 -0
  7. data/lib/bing-ads-api/client_proxy.rb +131 -0
  8. data/lib/bing-ads-api/config.rb +75 -0
  9. data/lib/bing-ads-api/constants.rb +133 -0
  10. data/lib/bing-ads-api/data/ad.rb +119 -0
  11. data/lib/bing-ads-api/data/ad_group.rb +121 -0
  12. data/lib/bing-ads-api/data/campaign.rb +40 -0
  13. data/lib/bing-ads-api/data/report_request.rb +78 -0
  14. data/lib/bing-ads-api/data/report_request_status.rb +48 -0
  15. data/lib/bing-ads-api/data/reporting/account_performance_report_request.rb +176 -0
  16. data/lib/bing-ads-api/data/reporting/campaign_performance_report_request.rb +186 -0
  17. data/lib/bing-ads-api/data/reporting/helpers/column_helper.rb +65 -0
  18. data/lib/bing-ads-api/data/reporting/helpers/filter_helper.rb +124 -0
  19. data/lib/bing-ads-api/data/reporting/helpers/scope_helper.rb +51 -0
  20. data/lib/bing-ads-api/data/reporting/helpers/time_helper.rb +69 -0
  21. data/lib/bing-ads-api/data/reporting/performance_report_request.rb +78 -0
  22. data/lib/bing-ads-api/data_object.rb +35 -0
  23. data/lib/bing-ads-api/fault/ad_api_error.rb +15 -0
  24. data/lib/bing-ads-api/fault/ad_api_fault_detail.rb +67 -0
  25. data/lib/bing-ads-api/fault/api_fault_detail.rb +97 -0
  26. data/lib/bing-ads-api/fault/application_fault.rb +18 -0
  27. data/lib/bing-ads-api/fault/batch_error.rb +47 -0
  28. data/lib/bing-ads-api/fault/operation_error.rb +22 -0
  29. data/lib/bing-ads-api/fault/partial_errors.rb +75 -0
  30. data/lib/bing-ads-api/service.rb +174 -0
  31. data/lib/bing-ads-api/service/campaign_management.rb +483 -0
  32. data/lib/bing-ads-api/service/reporting.rb +101 -0
  33. data/lib/bing-ads-api/soap_hasheable.rb +143 -0
  34. data/lib/bing-ads-api/version.rb +6 -0
  35. data/lib/locales/es.yml +174 -0
  36. data/lib/tasks/bing-ads-api_tasks.rake +4 -0
  37. data/test/bing-ads-api_test.rb +134 -0
  38. data/test/campaign_management_test.rb +463 -0
  39. data/test/data_object_test.rb +46 -0
  40. data/test/dummy/README.rdoc +261 -0
  41. data/test/dummy/Rakefile +7 -0
  42. data/test/dummy/app/assets/javascripts/application.js +15 -0
  43. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  44. data/test/dummy/app/controllers/application_controller.rb +3 -0
  45. data/test/dummy/app/helpers/application_helper.rb +2 -0
  46. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  47. data/test/dummy/config.ru +4 -0
  48. data/test/dummy/config/application.rb +56 -0
  49. data/test/dummy/config/boot.rb +10 -0
  50. data/test/dummy/config/database.yml +25 -0
  51. data/test/dummy/config/environment.rb +5 -0
  52. data/test/dummy/config/environments/development.rb +37 -0
  53. data/test/dummy/config/environments/production.rb +67 -0
  54. data/test/dummy/config/environments/test.rb +37 -0
  55. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  56. data/test/dummy/config/initializers/inflections.rb +15 -0
  57. data/test/dummy/config/initializers/mime_types.rb +5 -0
  58. data/test/dummy/config/initializers/secret_token.rb +7 -0
  59. data/test/dummy/config/initializers/session_store.rb +8 -0
  60. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  61. data/test/dummy/config/locales/en.yml +5 -0
  62. data/test/dummy/config/routes.rb +58 -0
  63. data/test/dummy/db/development.sqlite3 +0 -0
  64. data/test/dummy/db/test.sqlite3 +0 -0
  65. data/test/dummy/log/development.log +29 -0
  66. data/test/dummy/log/test.log +3264 -0
  67. data/test/dummy/public/404.html +26 -0
  68. data/test/dummy/public/422.html +26 -0
  69. data/test/dummy/public/500.html +25 -0
  70. data/test/dummy/public/favicon.ico +0 -0
  71. data/test/dummy/script/rails +6 -0
  72. data/test/report_request_test.rb +312 -0
  73. data/test/reporting_test.rb +145 -0
  74. data/test/test_helper.rb +11 -0
  75. metadata +205 -0
@@ -0,0 +1,174 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ module BingAdsApi
4
+
5
+ # Public : Base class for service object
6
+ #
7
+ # Author:: jlopezn@neonline.cl
8
+ #
9
+ class Service
10
+
11
+ attr_accessor :client_proxy, :environment
12
+
13
+ # Default logger for services
14
+ LOGGER = Logger.new(STDOUT)
15
+
16
+ # Public : Constructor
17
+ #
18
+ # Author:: jlopezn@neonline.cl
19
+ #
20
+ # === Parameters
21
+ # * +options+ - Hash with autentication and environment settings
22
+ #
23
+ # === Options
24
+ # * environment - +:production+ or +:sandbox+
25
+ # * username - Bing Ads username
26
+ # * passwrod - Bing Ads user's sign-in password
27
+ # * developer_token - client application's developer access token
28
+ # * customer_id - identifier for the customer that owns the account
29
+ # * account_id - identifier of the account that own the entities in the request
30
+ # * proxy - Hash with any Client Proxy additional options (such as header, logger or enconding)
31
+ #
32
+ # === Examples
33
+ # service = BingAdsApi::Service.new(
34
+ # :environment => :sandbox,
35
+ # :username => 'username',
36
+ # :password => 'pass',
37
+ # :developer_token => 'SOME_TOKEN',
38
+ # :account_id => 123456,
39
+ # :customer_id => 654321,
40
+ # :proxy => {:logger => Rails.logger}
41
+ # )
42
+ # # => <Service>
43
+ def initialize(options={})
44
+
45
+ # Service Environment
46
+ self.environment = options[:environment]
47
+
48
+ # ClientProxy settings
49
+ clientProxySettings = {
50
+ :username => options[:username],
51
+ :password => options[:password],
52
+ :developer_token => options[:developer_token],
53
+ :account_id => options[:account_id],
54
+ :customer_id => options[:customer_id],
55
+ :wsdl_url => options[:wdsl] || solve_wsdl_url
56
+ }
57
+
58
+ # Additionsl ClientProxy settings
59
+ clientProxySettings[:proxy] = options[:proxy] if options[:proxy]
60
+
61
+ # ClientProxy creation
62
+ self.client_proxy = BingAdsApi::ClientProxy.new(clientProxySettings)
63
+
64
+ end
65
+
66
+ # Public : This is a utility wrapper for calling services into the
67
+ # +ClientProxy+. This methods handle all the +Savon::Client+ Exceptions
68
+ # and returns a Hash with the call response
69
+ #
70
+ # Author:: jlopezn@neonline.cl
71
+ #
72
+ # === Parameters
73
+ # +operation+ - name of the operation to be called
74
+ # +message+ - hash with the parameters to the operation
75
+ #
76
+ # === Examples
77
+ # service.call(:some_operation, {key: value})
78
+ # # => <Hash>
79
+ #
80
+ # Returns:: Hash with the result of the service call
81
+ # Raises:: ServiceError if the SOAP call, the ClientProxy fails or the response is invalid
82
+ def call(operation, message, &block)
83
+ raise "You must provide an operation" if operation.nil?
84
+ begin
85
+ LOGGER.debug "BingAdsApi Service"
86
+ LOGGER.debug " Calling #{operation.to_s}"
87
+ LOGGER.debug " Message: #{message}"
88
+ response = self.client_proxy.call(operation.to_sym,
89
+ message: message)
90
+
91
+ LOGGER.debug "response header:"
92
+ LOGGER.debug "\t#{response.header}"
93
+
94
+ LOGGER.info "Operation #{operation.to_s} call success"
95
+ return response.hash
96
+ rescue Savon::SOAPFault => error
97
+ LOGGER.error "SOAP Error calling #{operation.to_s}: #{error.http.code}"
98
+ fault_detail = error.to_hash[:fault][:detail]
99
+ if fault_detail.key?(:api_fault_detail)
100
+ api_fault_detail = BingAdsApi::ApiFaultDetail.new(fault_detail[:api_fault_detail])
101
+ raise BingAdsApi::ApiException.new(
102
+ api_fault_detail, "SOAP Error calling #{operation.to_s}")
103
+ elsif fault_detail.key?(:ad_api_fault_detail)
104
+ ad_api_fault_detail = BingAdsApi::AdApiFaultDetail.new(fault_detail[:ad_api_fault_detail])
105
+ raise BingAdsApi::ApiException.new(
106
+ ad_api_fault_detail, "SOAP Error calling #{operation.to_s}")
107
+ else
108
+ raise
109
+ end
110
+ rescue Savon::HTTPError => error
111
+ LOGGER.error "Http Error calling #{operation.to_s}: #{error.http.code}"
112
+ raise
113
+ rescue Savon::InvalidResponseError => error
114
+ LOGGER.error "Invalid server reponse calling #{operation.to_s}"
115
+ raise
116
+ end
117
+ end
118
+
119
+
120
+ # Public : Extracts the actual response from the entire response hash.
121
+ # For example, if you specify 'AddCampaigns', this method will return
122
+ # the content of 'AddCampaignsResponse' tag as a Hash
123
+ #
124
+ # Author:: jlopezn@neonline.cl
125
+ #
126
+ # === Parameters
127
+ # response - The complete response hash received from a Operation call
128
+ # method - Name of the method of with the 'reponse' tag is require
129
+ #
130
+ # === Examples
131
+ # service.get_response_hash(Hash, 'add_campaigns')
132
+ # # => Hash
133
+ #
134
+ # Returns:: Hash with the inner structure of the method response hash
135
+ # Raises:: exception
136
+ def get_response_hash(response, method)
137
+ return response[:envelope][:body]["#{method}_response".to_sym]
138
+ end
139
+
140
+ private
141
+
142
+ # Private : This method must be overriden by specific services.
143
+ # Returns:: the service name
144
+ #
145
+ # Author:: jlopezn@neonline.cl
146
+ #
147
+ # Examples
148
+ # get_service_name
149
+ # # => "service_name"
150
+ #
151
+ # Returns:: String with the service name
152
+ # Raises:: exception if the specific Service class hasn't overriden this method
153
+ def get_service_name
154
+ raise "Should return the a service name from config.wsdl keys"
155
+ end
156
+
157
+
158
+ # Private : Solves the service WSDL URL based on his service name
159
+ # and environment values
160
+ #
161
+ # Author:: jlopezn@neonline.cl
162
+ #
163
+ # Examples
164
+ # solve_wsdl_url
165
+ # # => "https://bing.wsdl.url.com"
166
+ #
167
+ # Returns:: String with the Service url
168
+ def solve_wsdl_url
169
+ config = BingAdsApi::Config.instance
170
+ return config.service_wsdl(environment, get_service_name)
171
+ end
172
+ end
173
+
174
+ end
@@ -0,0 +1,483 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module BingAdsApi
3
+
4
+
5
+ # Public : This class represents the Campaign Management Services
6
+ # defined in the Bing Ads API, to manage advertising campaigns
7
+ #
8
+ # Author:: jlopezn@neonline.cl
9
+ #
10
+ # Examples
11
+ # options = {
12
+ # :environment => :sandbox,
13
+ # :username => "username",
14
+ # :password => "pass",
15
+ # :developer_token => "SOME_TOKEN",
16
+ # :customer_id => "1234567",
17
+ # :account_id => "9876543" }
18
+ # service = BingAdsApi::CampaignManagement.new(options)
19
+ class CampaignManagement < BingAdsApi::Service
20
+
21
+
22
+ # Public : Constructor
23
+ #
24
+ # Author:: jlopezn@neonline.cl
25
+ #
26
+ # options - Hash with the parameters for the client proxy and the environment
27
+ #
28
+ # Examples
29
+ # options = {
30
+ # :environment => :sandbox,
31
+ # :username => "username",
32
+ # :password => "password",
33
+ # :developer_token => "DEV_TOKEN",
34
+ # :customer_id => "123456",
35
+ # :account_id => "654321"
36
+ # }
37
+ # service = BingAdsApi::CampaignManagement.new(options)
38
+ def initialize(options={})
39
+ super(options)
40
+ end
41
+
42
+
43
+ #########################
44
+ ## Operations Wrappers ##
45
+ #########################
46
+
47
+ # Public : Returns all the campaigns found in the specified account
48
+ #
49
+ # Author:: jlopezn@neonline.cl
50
+ #
51
+ # === Parameters
52
+ # account_id - account who owns the campaigns
53
+ #
54
+ # === Examples
55
+ # campaign_management_service.get_campaigns_by_account_id(1)
56
+ # # => Array[BingAdsApi::Campaign]
57
+ #
58
+ # Returns:: Array of BingAdsApi::Campaign
59
+ #
60
+ # Raises:: exception
61
+ def get_campaigns_by_account_id(account_id)
62
+ response = call(:get_campaigns_by_account_id,
63
+ {account_id: account_id})
64
+ response_hash = get_response_hash(response, __method__)
65
+ campaigns = response_hash[:campaigns][:campaign].map do |camp_hash|
66
+ BingAdsApi::Campaign.new(camp_hash)
67
+ end
68
+ return campaigns
69
+ end
70
+
71
+
72
+ # Public : Adds a campaign to the specified account
73
+ #
74
+ # Author:: jlopezn@neonline.cl
75
+ #
76
+ # === Parameters
77
+ # account_id - account who will own the newly campaigns
78
+ # campaigns - An array of BingAdsApi::Campaign
79
+ #
80
+ # === Examples
81
+ # service.add_campaigns(1, [<BingAdsApi::Campaign>])
82
+ # # => <Hash>
83
+ #
84
+ # Returns:: hash with the 'add_campaigns_response' structure
85
+ #
86
+ # Raises:: exception
87
+ def add_campaigns(account_id, campaigns)
88
+
89
+ camps = []
90
+ if campaigns.is_a? Array
91
+ camps = campaigns.map{ |camp| camp.to_hash(:camelcase) }
92
+ elsif campaigns.is_a? BingAdsApi::Campaign
93
+ camps = campaigns.to_hash
94
+ else
95
+ raise "campaigns must be an array of BingAdsApi::Campaigns"
96
+ end
97
+ message = {
98
+ :account_id => account_id,
99
+ :campaigns => {:campaign => camps} }
100
+ puts message
101
+ response = call(:add_campaigns, message)
102
+ return get_response_hash(response, __method__)
103
+ end
104
+
105
+
106
+ # Public : Updates on or more campaigns for the specified account
107
+ #
108
+ # Author:: jlopezn@neonline.cl
109
+ #
110
+ # === Parameters
111
+ # account_id - account who own the updated campaigns
112
+ # campaigns - Array with the campaigns to be updated
113
+ #
114
+ # === Examples
115
+ # service_update_campaigns(1, [<BingAdsApi::Campaign])
116
+ # # => true
117
+ #
118
+ # Returns:: boolean. true if the update was successful. false otherwise
119
+ #
120
+ # Raises:: exception
121
+ def update_campaigns(account_id, campaigns)
122
+ camps = []
123
+ if campaigns.is_a? Array
124
+ camps = campaigns.map do |camp|
125
+ camp.to_hash(:camelcase)
126
+ end
127
+ elsif campaigns.is_a? BingAdsApi::Campaign
128
+ camps = campaigns.to_hash
129
+ else
130
+ raise "campaigns must be an array of BingAdsApi::Campaigns"
131
+ end
132
+ message = {
133
+ :account_id => account_id,
134
+ :campaigns => {:campaign => camps} }
135
+ puts message
136
+ response = call(:update_campaigns, message)
137
+ return get_response_hash(response, __method__)
138
+
139
+ end
140
+
141
+
142
+ # Public : Returns all the ad groups that belongs to the
143
+ # specified campaign
144
+ #
145
+ # Author:: jlopezn@neonline.cl
146
+ #
147
+ # === Parameters
148
+ # campaign_id - campaign id
149
+ #
150
+ # === Examples
151
+ # service.get_ad_groups_by_campaign_id(1)
152
+ # # => Array[AdGroups]
153
+ #
154
+ # Returns:: Array with all the ad groups present in campaign_id
155
+ #
156
+ # Raises:: exception
157
+ def get_ad_groups_by_campaign_id(campaign_id)
158
+ response = call(:get_ad_groups_by_campaign_id,
159
+ {campaign_id: campaign_id})
160
+ response_hash = get_response_hash(response, __method__)
161
+ ad_groups = response_hash[:ad_groups][:ad_group].map do |ad_group_hash|
162
+ BingAdsApi::AdGroup.new(ad_group_hash)
163
+ end
164
+ return ad_groups
165
+ end
166
+
167
+
168
+ # Public : Returns the specified ad groups that belongs to the
169
+ # specified campaign
170
+ #
171
+ # Author:: jlopezn@neonline.cl
172
+ #
173
+ # === Parameters
174
+ # campaign_id - campaign id
175
+ # ad_groups_ids - array with ids from ad groups
176
+ #
177
+ # === Examples
178
+ # service.get_ad_groups_by_ids(1, [1,2,3])
179
+ # # => Array[AdGroups]
180
+ #
181
+ # Returns:: Array with the ad groups specified in the ad_groups_ids array
182
+ #
183
+ # Raises:: exception
184
+ def get_ad_groups_by_ids(campaign_id, ad_groups_ids)
185
+
186
+ message = {
187
+ :campaign_id => campaign_id,
188
+ :ad_group_ids => {"ins1:long" => ad_groups_ids} }
189
+ response = call(:get_ad_groups_by_ids, message)
190
+ response_hash = get_response_hash(response, __method__)
191
+ ad_groups = response_hash[:ad_groups][:ad_group].map do |ad_group_hash|
192
+ BingAdsApi::AdGroup.new(ad_group_hash)
193
+ end
194
+ return ad_groups
195
+
196
+ end
197
+
198
+
199
+ # Public : Adds 1 or more AdGroups to a Campaign
200
+ #
201
+ # Author:: jlopezn@neonline.cl
202
+ #
203
+ # === Parameters
204
+ # campaing_id - the campaign id where the ad groups will be added
205
+ # ad_groups - Array[BingAdsApi::AdGroup] ad groups to be added
206
+ #
207
+ # === Examples
208
+ # service.add_ad_groups(1, [<BingAdsApi::AdGroup>])
209
+ # # => <Hash>
210
+ #
211
+ # Returns:: Hash with the 'add_ad_groups_response' structure
212
+ #
213
+ # Raises:: exception
214
+ def add_ad_groups(campaign_id, ad_groups)
215
+
216
+ groups = []
217
+ if ad_groups.is_a? Array
218
+ groups = ad_groups.map{ |gr| gr.to_hash(:camelcase) }
219
+ elsif ad_groups.is_a? BingAdsApi::AdGroup
220
+ groups = ad_groups.to_hash
221
+ else
222
+ raise "ad_groups must be an array of BingAdsApi::AdGroup"
223
+ end
224
+ message = {
225
+ :campaign_id => campaign_id,
226
+ :ad_groups => {:ad_group => groups} }
227
+ puts message
228
+ response = call(:add_ad_groups, message)
229
+ return get_response_hash(response, __method__)
230
+ end
231
+
232
+
233
+ # Public : Updates on or more ad groups in a specified campaign
234
+ #
235
+ # Author:: jlopezn@neonline.cl
236
+ #
237
+ # === Parameters
238
+ # campaign_id - campaign who owns the updated ad groups
239
+ #
240
+ # === Examples
241
+ # service.update_ad_groups(1, [<BingAdsApi::AdGroup])
242
+ # # => true
243
+ #
244
+ # Returns:: boolean. true if the updates is successfull. false otherwise
245
+ #
246
+ # Raises:: exception
247
+ def update_ad_groups(campaign_id, ad_groups)
248
+ groups = []
249
+ if ad_groups.is_a? Array
250
+ groups = ad_groups.map{ |gr| gr.to_hash(:camelcase) }
251
+ elsif ad_groups.is_a? BingAdsApi::AdGroup
252
+ groups = ad_groups.to_hash(:camelcase)
253
+ else
254
+ raise "ad_groups must be an array or instance of BingAdsApi::AdGroup"
255
+ end
256
+ message = {
257
+ :campaign_id => campaign_id,
258
+ :ad_groups => {:ad_group => groups} }
259
+ puts message
260
+ response = call(:update_ad_groups, message)
261
+ return get_response_hash(response, __method__)
262
+ end
263
+
264
+
265
+ # Public : Obtains all the ads associated to the specified ad group
266
+ #
267
+ # Author:: jlopezn@neonline.cl
268
+ #
269
+ # === Parameters
270
+ # ad_group_id - long with the ad group id
271
+ #
272
+ # === Examples
273
+ # service.get_ads_by_ad_group_id(1)
274
+ # # => [<BingAdsApi::Ad]
275
+ #
276
+ # Returns:: An array of BingAdsApi::Ad
277
+ #
278
+ # Raises:: exception
279
+ def get_ads_by_ad_group_id(ad_group_id)
280
+ response = call(:get_ads_by_ad_group_id,
281
+ {ad_group_id: ad_group_id})
282
+ response_hash = get_response_hash(response, __method__)
283
+
284
+ if response_hash[:ads][:ad].is_a?(Array)
285
+ ads = response_hash[:ads][:ad].map do |ad_hash|
286
+ initialize_ad(ad_hash)
287
+ end
288
+ else
289
+ ads = [ initialize_ad(response_hash[:ads][:ad]) ]
290
+ end
291
+ return ads
292
+ end
293
+
294
+
295
+ # Public : Obtains the ads indicated in ad_ids associated to the specified ad group
296
+ #
297
+ # Author:: jlopezn@neonline.cl
298
+ #
299
+ # === Parameters
300
+ # ad_group_id - long with the ad group id
301
+ # ads_id - an Array io ads ids, that are associated to the ad_group_id provided
302
+ #
303
+ # === Examples
304
+ # service.get_ads_by_ids(1, [1,2,3])
305
+ # # => [<BingAdsApi::Ad>]
306
+ #
307
+ # Returns:: An array of BingAdsApi::Ad
308
+ #
309
+ # Raises:: exception
310
+ def get_ads_by_ids(ad_group_id, ad_ids)
311
+
312
+
313
+ message = {
314
+ :ad_group_id => ad_group_id,
315
+ :ad_ids => {"ins1:long" => ad_ids} }
316
+ response = call(:get_ads_by_ids, message)
317
+ response_hash = get_response_hash(response, __method__)
318
+
319
+ if response_hash[:ads][:ad].is_a?(Array)
320
+ ads = response_hash[:ads][:ad].map do |ad_hash|
321
+ initialize_ad(ad_hash)
322
+ end
323
+ else
324
+ ads = [ initialize_ad(response_hash[:ads][:ad]) ]
325
+ end
326
+ return ads
327
+ end
328
+
329
+
330
+ # Public : Add ads into a specified ad group
331
+ #
332
+ # Author:: jlopezn@neonline.cl
333
+ #
334
+ # === Parameters
335
+ # ad_group_id - a number with the id where the ads should be added
336
+ # ads - an array of BingAdsApi::Ad instances
337
+ #
338
+ # === Examples
339
+ # # if the operation returns partial errors
340
+ # service.add_ads(1, [BingAdsApi::Ad])
341
+ # # => {:ad_ids => [], :partial_errors => BingAdsApi::PartialErrors }
342
+ #
343
+ # # if the operation doesn't return partial errors
344
+ # service.add_ads(1, [BingAdsApi::Ad])
345
+ # # => {:ad_ids => [] }
346
+ #
347
+ # Returns:: Hash with the AddAdsResponse structure.
348
+ # If the operation returns 'PartialErrors' key,
349
+ # this methods returns those errors as an BingAdsApi::PartialErrors
350
+ # instance
351
+ #
352
+ # Raises:: exception
353
+ def add_ads(ad_group_id, ads)
354
+
355
+ ads_for_soap = []
356
+ if ads.is_a? Array
357
+ ads_for_soap = ads.map{ |ad| ad_to_hash(ad, :camelcase) }
358
+ elsif ads.is_a? BingAdsApi::Ad
359
+ ads_for_soap = ad_to_hash(ads, :camelcase)
360
+ else
361
+ raise "ads must be an array or instance of BingAdsApi::Ad"
362
+ end
363
+ message = {
364
+ :ad_group_id => ad_group_id,
365
+ :ads => {:ad => ads_for_soap} }
366
+ puts message
367
+ response = call(:add_ads, message)
368
+
369
+ response_hash = get_response_hash(response, __method__)
370
+
371
+ # Checks if there are partial errors in the request
372
+ if response_hash[:partial_errors].key?(:batch_error)
373
+ partial_errors = BingAdsApi::PartialErrors.new(
374
+ response_hash[:partial_errors])
375
+ response_hash[:partial_errors] = partial_errors
376
+ else
377
+ response_hash.delete(:partial_errors)
378
+ end
379
+
380
+ return response_hash
381
+ end
382
+
383
+
384
+ # Public : Updates ads for the specified ad group
385
+ #
386
+ # Author:: jlopezn@neonline.cl
387
+ #
388
+ # === Parameters
389
+ # ad_group_id - long with the ad group id
390
+ # ads - array of BingAdsApi::Ad subclasses instances to update
391
+ #
392
+ # === Examples
393
+ # service.update_ads(1, [<BingAdsApi::Ad>])
394
+ # # => Hash
395
+ #
396
+ # Returns:: Hash with the UpdateAddsResponse structure
397
+ #
398
+ # Raises:: exception
399
+ def update_ads(ad_group_id, ads)
400
+
401
+ ads_for_soap = []
402
+ if ads.is_a? Array
403
+ ads_for_soap = ads.map{ |ad| ad_to_hash(ad, :camelcase) }
404
+ elsif ads.is_a? BingAdsApi::Ad
405
+ ads_for_soap = ad_to_hash(ads, :camelcase)
406
+ else
407
+ raise "ads must be an array or instance of BingAdsApi::Ad"
408
+ end
409
+ message = {
410
+ :ad_group_id => ad_group_id,
411
+ :ads => {:ad => ads_for_soap} }
412
+ puts message
413
+ response = call(:update_ads, message)
414
+
415
+ response_hash = get_response_hash(response, __method__)
416
+
417
+ # Checks if there are partial errors in the request
418
+ if response_hash[:partial_errors].key?(:batch_error)
419
+ partial_errors = BingAdsApi::PartialErrors.new(
420
+ response_hash[:partial_errors])
421
+ response_hash[:partial_errors] = partial_errors
422
+ else
423
+ response_hash.delete(:partial_errors)
424
+ end
425
+
426
+ return response_hash
427
+ end
428
+
429
+
430
+ private
431
+ def get_service_name
432
+ "campaign_management"
433
+ end
434
+
435
+ # Private : Returns an instance of any of the subclases of BingAdsApi::Ad based on the '@i:type' value in the hash
436
+ #
437
+ # Author:: jlopezn@neonline.cl
438
+ #
439
+ # ad_hash - Hash returned by the SOAP request with the Ad attributes
440
+ #
441
+ # Examples
442
+ # initialize_ad({:device_preference=>"0", :editorial_status=>"Active",
443
+ # :forward_compatibility_map=>{:"@xmlns:a"=>"http://schemas.datacontract.org/2004/07/System.Collections.Generic"},
444
+ # :id=>"1", :status=>"Active", :type=>"Text",
445
+ # :destination_url=>"www.some-url.com", :display_url=>"http://www.some-url.com",
446
+ # :text=>"My Page", :title=>"My Page",
447
+ # :"@i:type"=>"TextAd"})
448
+ # # => BingAdsApi::TextAd
449
+ #
450
+ # Returns:: BingAdsApi::Ad subclass instance
451
+ def initialize_ad(ad_hash)
452
+ ad = BingAdsApi::Ad.new(ad_hash)
453
+ case ad_hash["@i:type".to_sym]
454
+ when "TextAd"
455
+ ad = BingAdsApi::TextAd.new(ad_hash)
456
+ when "MobileAd"
457
+ ad = BingAdsApi::MobileAd.new(ad_hash)
458
+ when "ProductAd"
459
+ ad = BingAdsApi::ProductAd.new(ad_hash)
460
+ end
461
+ return ad
462
+ end
463
+
464
+
465
+ # Private : Helper method to correctly assemble the Ad XML for SOAP requests
466
+ #
467
+ # Author:: jlopezn@neonline.cl
468
+ #
469
+ # ad - BingAdsApi::Ad subclass instance
470
+ #
471
+ # Examples
472
+ # ad_to_hash(BingAdsApi::Ad, :camelcase)
473
+ # # => Hash
474
+ #
475
+ # Returns:: The same hash that ad.to_hash returns plus the needed key for the Ad Type
476
+ def ad_to_hash(ad, keys)
477
+ hash = ad.to_hash(keys)
478
+ hash["@xsi:type"] = self.client_proxy.class::NAMESPACE.to_s + ":" + ad.class.to_s.demodulize
479
+ return hash
480
+ end
481
+ end
482
+
483
+ end