marketingcloudsdk 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +26 -0
  3. data/Gemfile +3 -0
  4. data/Gemfile.lock +92 -0
  5. data/Guardfile +8 -0
  6. data/LICENSE.md +13 -0
  7. data/README.md +130 -0
  8. data/Rakefile +1 -0
  9. data/lib/marketingcloudsdk.rb +74 -0
  10. data/lib/marketingcloudsdk/client.rb +283 -0
  11. data/lib/marketingcloudsdk/http_request.rb +113 -0
  12. data/lib/marketingcloudsdk/objects.rb +757 -0
  13. data/lib/marketingcloudsdk/rest.rb +122 -0
  14. data/lib/marketingcloudsdk/soap.rb +288 -0
  15. data/lib/marketingcloudsdk/targeting.rb +58 -0
  16. data/lib/marketingcloudsdk/utils.rb +47 -0
  17. data/lib/marketingcloudsdk/version.rb +39 -0
  18. data/lib/new.rb +1240 -0
  19. data/marketingcloudsdk.gemspec +30 -0
  20. data/samples/sample-AddSubscriberToList.rb +56 -0
  21. data/samples/sample-CreateAndStartDataExtensionImport.rb +29 -0
  22. data/samples/sample-CreateAndStartListImport.rb +27 -0
  23. data/samples/sample-CreateContentAreas.rb +48 -0
  24. data/samples/sample-CreateDataExtensions.rb +54 -0
  25. data/samples/sample-CreateProfileAttributes.rb +48 -0
  26. data/samples/sample-SendEmailToDataExtension.rb +23 -0
  27. data/samples/sample-SendEmailToList.rb +23 -0
  28. data/samples/sample-SendTriggeredSends.rb +30 -0
  29. data/samples/sample-bounceevent.rb +70 -0
  30. data/samples/sample-campaign.rb +211 -0
  31. data/samples/sample-clickevent.rb +71 -0
  32. data/samples/sample-contentarea.rb +122 -0
  33. data/samples/sample-dataextension.rb +209 -0
  34. data/samples/sample-directverb.rb +55 -0
  35. data/samples/sample-email.rb +122 -0
  36. data/samples/sample-email.senddefinition.rb +134 -0
  37. data/samples/sample-folder.rb +143 -0
  38. data/samples/sample-import.rb +104 -0
  39. data/samples/sample-list.rb +105 -0
  40. data/samples/sample-list.subscriber.rb +97 -0
  41. data/samples/sample-openevent.rb +70 -0
  42. data/samples/sample-profileattribute.rb +57 -0
  43. data/samples/sample-sentevent.rb +70 -0
  44. data/samples/sample-subscriber.rb +136 -0
  45. data/samples/sample-triggeredsend.rb +130 -0
  46. data/samples/sample-unsubevent.rb +72 -0
  47. data/samples/sample_helper.rb.template +8 -0
  48. data/spec/client_spec.rb +210 -0
  49. data/spec/helper_funcs_spec.rb +11 -0
  50. data/spec/http_request_spec.rb +36 -0
  51. data/spec/objects_helper_spec.rb +32 -0
  52. data/spec/objects_spec.rb +484 -0
  53. data/spec/rest_spec.rb +48 -0
  54. data/spec/soap_spec.rb +140 -0
  55. data/spec/spec_helper.rb +14 -0
  56. data/spec/targeting_spec.rb +39 -0
  57. metadata +260 -0
@@ -0,0 +1,113 @@
1
+ =begin
2
+ Copyright (c) 2013 ExactTarget, Inc.
3
+
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
7
+
8
+ following conditions are met:
9
+
10
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
11
+
12
+ following disclaimer.
13
+
14
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
15
+
16
+ following disclaimer in the documentation and/or other materials provided with the distribution.
17
+
18
+ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
19
+
20
+ products derived from this software without specific prior written permission.
21
+
22
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
23
+
24
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25
+
26
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27
+
28
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29
+
30
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31
+
32
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
33
+
34
+ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35
+ =end
36
+
37
+ require 'open-uri'
38
+ require 'net/https'
39
+ require 'json'
40
+
41
+ module MarketingCloudSDK
42
+
43
+ class HTTPResponse < MarketingCloudSDK::Response
44
+ def initialize raw, client, request
45
+ super raw, client
46
+ @request = request
47
+ end
48
+
49
+ def continue
50
+ rsp = nil
51
+ if more?
52
+ @request['options']['page'] = @results['page'].to_i + 1
53
+ rsp = unpack @client.rest_get(@request['url'], @request['options'])
54
+ else
55
+ puts 'No more data'
56
+ end
57
+
58
+ rsp
59
+ end
60
+
61
+ def [] key
62
+ @results[key]
63
+ end
64
+
65
+ private
66
+ def unpack raw
67
+ @code = raw.code.to_i
68
+ @message = raw.message
69
+ @body = JSON.parse(raw.body) rescue {}
70
+ @results = @body
71
+ @more = ((@results['count'] || @results['totalCount']) > @results['page'] * @results['pageSize']) rescue false
72
+ @success = @message == 'OK'
73
+ end
74
+
75
+ # by default try everything against results
76
+ def method_missing method, *args, &block
77
+ @results.send(method, *args, &block)
78
+ end
79
+ end
80
+
81
+ module HTTPRequest
82
+ request_methods = ['get', 'post', 'patch', 'delete']
83
+ request_methods.each do |method|
84
+ class_eval <<-EOT, __FILE__, __LINE__ + 1
85
+ def #{method}(url, options={}) # def post(url, options)
86
+ request Net::HTTP::#{method.capitalize}, url, options # request Net::HTTP::Post, url, options
87
+ end # end
88
+ EOT
89
+ end
90
+
91
+ private
92
+
93
+ def generate_uri(url, params=nil)
94
+ uri = URI.parse(url)
95
+ uri.query = URI.encode_www_form(params) if params
96
+ uri
97
+ end
98
+
99
+ def request(method, url, options={})
100
+ uri = generate_uri url, options['params']
101
+ http = Net::HTTP.new(uri.host, uri.port)
102
+ http.use_ssl = true
103
+
104
+ data = options['data']
105
+ _request = method.new uri.request_uri
106
+ _request.body = data.to_json if data
107
+ _request.content_type = options['content_type'] if options['content_type']
108
+ _request.add_field('User_Agent', 'FuelSDK-Ruby')
109
+ response = http.request(_request)
110
+ HTTPResponse.new(response, self, :url => url, :options => options)
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,757 @@
1
+ =begin
2
+ Copyright (c) 2013 ExactTarget, Inc.
3
+
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
7
+
8
+ following conditions are met:
9
+
10
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
11
+
12
+ following disclaimer.
13
+
14
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
15
+
16
+ following disclaimer in the documentation and/or other materials provided with the distribution.
17
+
18
+ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
19
+
20
+ products derived from this software without specific prior written permission.
21
+
22
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
23
+
24
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25
+
26
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27
+
28
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29
+
30
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31
+
32
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
33
+
34
+ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35
+ =end
36
+
37
+ module MarketingCloudSDK
38
+ module Objects
39
+ module Soap
40
+ module Read
41
+ attr_accessor :filter
42
+ def get _id=nil
43
+ client.soap_get _id||id, properties, filter
44
+ end
45
+
46
+ def info
47
+ client.soap_describe id
48
+ end
49
+ end
50
+
51
+ module CUD #create, update, delete
52
+ def post
53
+ if self.respond_to?('folder_property') && !self.folder_id.nil?
54
+ properties[self.folder_property] = self.folder_id
55
+ elsif self.respond_to?('folder_property') && !self.folder_property.nil? && !client.package_name.nil? then
56
+ if client.package_folders.nil? then
57
+ getPackageFolder = ET_Folder.new
58
+ getPackageFolder.authStub = client
59
+ getPackageFolder.properties = ["ID", "ContentType"]
60
+ getPackageFolder.filter = {"Property" => "Name", "SimpleOperator" => "equals", "Value" => client.package_name}
61
+ resultPackageFolder = getPackageFolder.get
62
+ if resultPackageFolder.status then
63
+ client.package_folders = {}
64
+ resultPackageFolder.results.each do |value|
65
+ client.package_folders[value[:content_type]] = value[:id]
66
+ end
67
+ else
68
+ raise "Unable to retrieve folders from account due to: #{resultPackageFolder.message}"
69
+ end
70
+ end
71
+
72
+ if !client.package_folders.has_key?(self.folder_media_type) then
73
+ if client.parentFolders.nil? then
74
+ parentFolders = ET_Folder.new
75
+ parentFolders.authStub = client
76
+ parentFolders.properties = ["ID", "ContentType"]
77
+ parentFolders.filter = {"Property" => "ParentFolder.ID", "SimpleOperator" => "equals", "Value" => "0"}
78
+ resultParentFolders = parentFolders.get
79
+ if resultParentFolders.status then
80
+ client.parent_folders = {}
81
+ resultParentFolders.results.each do |value|
82
+ client.parent_folders[value[:content_type]] = value[:id]
83
+ end
84
+ else
85
+ raise "Unable to retrieve folders from account due to: #{resultParentFolders.message}"
86
+ end
87
+ end
88
+
89
+ newFolder = ET_Folder.new
90
+ newFolder.authStub = client
91
+ newFolder.properties = {"Name" => client.package_name, "Description" => client.package_name, "ContentType"=> self.folder_media_type, "IsEditable"=>"true", "ParentFolder" => {"ID" => client.parentFolders[self.folder_media_type]}}
92
+ folderResult = newFolder.post
93
+ if folderResult.status then
94
+ client.package_folders[self.folder_media_type] = folderResult.results[0][:new_id]
95
+ else
96
+ raise "Unable to create folder for Post due to: #{folderResult.message}"
97
+ end
98
+
99
+ end
100
+ properties[self.folder_property] = client.package_folders[self.folder_media_type]
101
+ end
102
+ client.soap_post id, properties
103
+ end
104
+
105
+ def patch
106
+ client.soap_patch id, properties
107
+ end
108
+
109
+ def delete
110
+ client.soap_delete id, properties
111
+ end
112
+ end
113
+
114
+ module Upsert
115
+ def put
116
+ client.soap_put id, properties
117
+ end
118
+ end
119
+
120
+ end
121
+
122
+ module Rest
123
+ module Read
124
+ def get
125
+ client.rest_get id, properties
126
+ end
127
+ end
128
+
129
+ module CUD
130
+ def post
131
+ client.rest_post id, properties
132
+ end
133
+
134
+ def patch
135
+ client.rest_patch id, properties
136
+ end
137
+
138
+ def delete
139
+ client.rest_delete id, properties
140
+ end
141
+ end
142
+
143
+ end
144
+
145
+ class Base
146
+ attr_accessor :properties, :client
147
+ attr_reader :id
148
+
149
+ alias props= properties= # backward compatibility
150
+ alias authStub= client= # backward compatibility
151
+
152
+ def properties
153
+ #@properties = [@properties].compact unless @properties.kind_of? Array
154
+ @properties
155
+ end
156
+
157
+ #Backwards compatibility
158
+ def props
159
+ @properties
160
+ end
161
+
162
+ def id
163
+ self.class.id
164
+ end
165
+
166
+ class << self
167
+ def id
168
+ self.name.split('::').pop
169
+ end
170
+ end
171
+ end
172
+ end
173
+
174
+ class BounceEvent < Objects::Base
175
+ attr_accessor :get_since_last_batch
176
+ include Objects::Soap::Read
177
+ end
178
+
179
+ class ClickEvent < Objects::Base
180
+ attr_accessor :get_since_last_batch
181
+ include Objects::Soap::Read
182
+ end
183
+
184
+ class ContentArea < Objects::Base
185
+ include Objects::Soap::Read
186
+ include Objects::Soap::CUD
187
+ attr_accessor :folder_id
188
+
189
+ def folder_property
190
+ 'CategoryID'
191
+ end
192
+
193
+ def folder_media_type
194
+ 'content'
195
+ end
196
+ end
197
+
198
+ class DataFolder < Objects::Base
199
+ include Objects::Soap::Read
200
+ include Objects::Soap::CUD
201
+ end
202
+
203
+ class Folder < DataFolder
204
+ class << self
205
+ def id
206
+ DataFolder.id
207
+ end
208
+ end
209
+ end
210
+
211
+ class Email < Objects::Base
212
+ include Objects::Soap::Read
213
+ include Objects::Soap::CUD
214
+ attr_accessor :folder_id
215
+
216
+ def folder_property
217
+ 'CategoryID'
218
+ end
219
+
220
+ def folder_media_type
221
+ 'email'
222
+ end
223
+
224
+ class SendDefinition < Objects::Base
225
+ include Objects::Soap::Read
226
+ include Objects::Soap::CUD
227
+ attr_accessor :folder_id
228
+
229
+ def id
230
+ 'EmailSendDefinition'
231
+ end
232
+
233
+ def folder_property
234
+ 'CategoryID'
235
+ end
236
+
237
+ def folder_media_type
238
+ 'userinitiatedsends'
239
+ end
240
+
241
+
242
+ def send
243
+ perform_response = client.soap_perform id, 'start' , properties
244
+ if perform_response.status then
245
+ @last_task_id = perform_response.results[0][:result][:task][:id]
246
+ end
247
+ perform_response
248
+ end
249
+
250
+ def status
251
+ client.soap_get "Send", ['ID','CreatedDate', 'ModifiedDate', 'Client.ID', 'Email.ID', 'SendDate','FromAddress','FromName','Duplicates','InvalidAddresses','ExistingUndeliverables','ExistingUnsubscribes','HardBounces','SoftBounces','OtherBounces','ForwardedEmails','UniqueClicks','UniqueOpens','NumberSent','NumberDelivered','NumberTargeted','NumberErrored','NumberExcluded','Unsubscribes','MissingAddresses','Subject','PreviewURL','SentDate','EmailName','Status','IsMultipart','SendLimit','SendWindowOpen','SendWindowClose','BCCEmail','EmailSendDefinition.ObjectID','EmailSendDefinition.CustomerKey'], {'Property' => 'ID','SimpleOperator' => 'equals','Value' => @last_task_id}
252
+ end
253
+
254
+ private
255
+ attr_accessor :last_task_id
256
+
257
+ end
258
+ end
259
+
260
+
261
+
262
+ class Import < Objects::Base
263
+ include Objects::Soap::Read
264
+ include Objects::Soap::CUD
265
+
266
+
267
+ def id
268
+ 'ImportDefinition'
269
+ end
270
+
271
+ def post
272
+ originalProp = properties
273
+ cleanProps
274
+ obj = super
275
+ properties = originalProp
276
+ return obj
277
+ end
278
+
279
+ def patch
280
+ originalProp = properties
281
+ cleanProps
282
+ obj = super
283
+ properties = originalProp
284
+ return obj
285
+ end
286
+
287
+ def start
288
+ perform_response = client.soap_perform id, 'start' , properties
289
+ if perform_response.status then
290
+ @last_task_id = perform_response.results[0][:result][:task][:id]
291
+ end
292
+ perform_response
293
+ end
294
+
295
+ def status
296
+ client.soap_get "ImportResultsSummary", ['ImportDefinitionCustomerKey','TaskResultID','ImportStatus','StartDate','EndDate','DestinationID','NumberSuccessful','NumberDuplicated','NumberErrors','TotalRows','ImportType'], {'Property' => 'TaskResultID','SimpleOperator' => 'equals','Value' => @last_task_id}
297
+ end
298
+
299
+ private
300
+ attr_accessor :last_task_id
301
+
302
+ def cleanProps
303
+ # If the ID property is specified for the destination then it must be a list import
304
+ if properties.has_key?('DestinationObject') then
305
+ if properties['DestinationObject'].has_key?('ID') then
306
+ properties[:attributes!] = { 'DestinationObject' => { 'xsi:type' => 'tns:List'}}
307
+ end
308
+ end
309
+ end
310
+ end
311
+
312
+
313
+ class List < Objects::Base
314
+ include Objects::Soap::Read
315
+ include Objects::Soap::CUD
316
+ attr_accessor :folder_id
317
+
318
+ def folder_property
319
+ 'Category'
320
+ end
321
+
322
+ def folder_media_type
323
+ 'list'
324
+ end
325
+
326
+ class Subscriber < Objects::Base
327
+ include Objects::Soap::Read
328
+ def id
329
+ 'ListSubscriber'
330
+ end
331
+ end
332
+ end
333
+
334
+ class OpenEvent < Objects::Base
335
+ attr_accessor :get_since_last_batch
336
+ include Objects::Soap::Read
337
+ end
338
+
339
+ class SentEvent < Objects::Base
340
+ attr_accessor :get_since_last_batch
341
+ include Objects::Soap::Read
342
+ end
343
+
344
+ class Subscriber < Objects::Base
345
+ include Objects::Soap::Read
346
+ include Objects::Soap::CUD
347
+ include Objects::Soap::Upsert
348
+ end
349
+
350
+ class UnsubEvent < Objects::Base
351
+ attr_accessor :get_since_last_batch
352
+ include Objects::Soap::Read
353
+ end
354
+
355
+ class ProfileAttribute < Objects::Base
356
+ def get
357
+ client.soap_describe "Subscriber"
358
+ end
359
+
360
+ def post
361
+ client.soap_configure "PropertyDefinition","create", properties
362
+ end
363
+
364
+ def delete
365
+ client.soap_configure "PropertyDefinition","delete", properties
366
+ end
367
+
368
+ def patch
369
+ client.soap_configure "PropertyDefinition","update", properties
370
+ end
371
+ end
372
+
373
+ class TriggeredSend < Objects::Base
374
+ include Objects::Soap::Read
375
+ include Objects::Soap::CUD
376
+ attr_accessor :folder_id, :subscribers, :attributes
377
+ def id
378
+ 'TriggeredSendDefinition'
379
+ end
380
+
381
+ def folder_property
382
+ 'CategoryID'
383
+ end
384
+
385
+ def folder_media_type
386
+ 'triggered_send'
387
+ end
388
+
389
+ def send
390
+ if self.properties.is_a? Array then
391
+ tscall = []
392
+ self.properties.each{ |p|
393
+ tscall.push({"TriggeredSendDefinition" => {"CustomerKey" => p["CustomerKey"]}, "Subscribers" => p["Subscribers"], "Attributes" => p["Attributes"]})
394
+ }
395
+ else
396
+ tscall = {"TriggeredSendDefinition" => self.properties, "Subscribers" => @subscribers, "Attributes" => @attributes }
397
+ end
398
+ client.soap_post 'TriggeredSend', tscall
399
+ end
400
+ end
401
+
402
+ class DataExtension < Objects::Base
403
+ include Objects::Soap::Read
404
+ include Objects::Soap::CUD
405
+ attr_accessor :fields, :folder_id
406
+
407
+ def folder_property
408
+ 'CategoryID'
409
+ end
410
+
411
+ def folder_media_type
412
+ 'dataextension'
413
+ end
414
+
415
+ alias columns= fields= # backward compatibility
416
+
417
+ def post
418
+ munge_fields self.properties
419
+ super
420
+ end
421
+
422
+ def patch
423
+ munge_fields self.properties
424
+ super
425
+ end
426
+
427
+ class Column < Objects::Base
428
+ include Objects::Soap::Read
429
+ def id
430
+ 'DataExtensionField'
431
+ end
432
+ def get
433
+ if filter and filter.kind_of? Hash and \
434
+ filter.include? 'Property' and filter['Property'] == 'CustomerKey'
435
+ filter['Property'] = 'DataExtension.CustomerKey'
436
+ end
437
+ super
438
+ end
439
+ end
440
+
441
+ class Row < Objects::Base
442
+ include Objects::Soap::Read
443
+ include Objects::Soap::CUD
444
+ include Objects::Soap::Upsert
445
+
446
+ attr_accessor :name, :customer_key
447
+
448
+ # backward compatibility
449
+ alias Name= name=
450
+ alias CustomerKey= customer_key=
451
+
452
+ def id
453
+ 'DataExtensionObject'
454
+ end
455
+
456
+ def get
457
+ super "#{id}[#{name}]"
458
+ end
459
+
460
+ def name
461
+ unless @name
462
+ retrieve_required
463
+ end
464
+ @name
465
+ end
466
+
467
+ def customer_key
468
+ unless @customer_key
469
+ retrieve_required
470
+ end
471
+ @customer_key
472
+ end
473
+
474
+ def post
475
+ munge_properties self.properties
476
+ super
477
+ end
478
+
479
+ def patch
480
+ munge_properties self.properties
481
+ super
482
+ end
483
+
484
+ def put
485
+ munge_properties self.properties
486
+ super
487
+ end
488
+
489
+ def delete
490
+ munge_keys self.properties
491
+ super
492
+ end
493
+
494
+ private
495
+ #::TODO::
496
+ # opportunity for meta programming here... but need to get this out the door
497
+ def munge_keys d
498
+ if d.kind_of? Array
499
+ d.each do |o|
500
+
501
+ next if explicit_keys(o) && explicit_customer_key(o)
502
+
503
+ formatted = []
504
+ o['CustomerKey'] = customer_key unless explicit_customer_key o
505
+ unless explicit_properties(o)
506
+ o.each do |k, v|
507
+ next if k == 'CustomerKey'
508
+ formatted.concat MarketingCloudSDK.format_name_value_pairs k => v
509
+ o.delete k
510
+ end
511
+ o['Keys'] = {'Key' => formatted }
512
+ end
513
+ end
514
+ else
515
+ formatted = []
516
+ d.each do |k, v|
517
+ next if k == 'CustomerKey'
518
+ formatted.concat MarketingCloudSDK.format_name_value_pairs k => v
519
+ d.delete k
520
+ end
521
+ d['CustomerKey'] = customer_key
522
+ d['Keys'] = {'Key' => formatted }
523
+ end
524
+ end
525
+
526
+ def explicit_keys h
527
+ h['Keys'] and h['Keys']['Key']
528
+ end
529
+
530
+ def munge_properties d
531
+ if d.kind_of? Array
532
+ d.each do |o|
533
+ next if explicit_properties(o) && explicit_customer_key(o)
534
+
535
+ formatted = []
536
+ o['CustomerKey'] = customer_key unless explicit_customer_key o
537
+ unless explicit_properties(o)
538
+ o.each do |k, v|
539
+ next if k == 'CustomerKey'
540
+ formatted.concat MarketingCloudSDK.format_name_value_pairs k => v
541
+ o.delete k
542
+ end
543
+ o['Properties'] = {'Property' => formatted }
544
+ end
545
+ end
546
+ else
547
+ formatted = []
548
+ d.each do |k, v|
549
+ formatted.concat MarketingCloudSDK.format_name_value_pairs k => v
550
+ d.delete k
551
+ end
552
+ d['CustomerKey'] = customer_key
553
+ d['Properties'] = {'Property' => formatted }
554
+ end
555
+ end
556
+
557
+ def explicit_properties h
558
+ h['Properties'] and h['Properties']['Property']
559
+ end
560
+
561
+ def explicit_customer_key h
562
+ h['CustomerKey']
563
+ end
564
+
565
+ def retrieve_required
566
+ # have to use instance variables so we don't recursivelly retrieve_required
567
+ if !@name && !@customer_key
568
+ raise 'Unable to process DataExtension::Row ' \
569
+ 'request due to missing CustomerKey and Name'
570
+ end
571
+ if !@name || !@customer_key
572
+ filter = {
573
+ 'Property' => @name.nil? ? 'CustomerKey' : 'Name',
574
+ 'SimpleOperator' => 'equals',
575
+ 'Value' => @customer_key || @name
576
+ }
577
+ rsp = client.soap_get 'DataExtension', ['Name', 'CustomerKey'], filter
578
+ if rsp.success? && rsp.results.count == 1
579
+ self.name = rsp.results.first[:name]
580
+ self.customer_key = rsp.results.first[:customer_key]
581
+ else
582
+ raise 'Unable to process DataExtension::Row'
583
+ end
584
+ end
585
+ end
586
+ end
587
+
588
+ private
589
+
590
+ def munge_fields d
591
+ # maybe one day will make it smart enough to zip properties and fields if count is same?
592
+ if d.kind_of? Array and d.count > 1 and (fields and !fields.empty?)
593
+ # we could map the field to all DataExtensions, but lets make user be explicit.
594
+ # if they are going to use fields attribute properties should
595
+ # be a single DataExtension Defined in a Hash
596
+ raise 'Unable to handle muliple DataExtension definitions and a field definition'
597
+ end
598
+
599
+ if d.kind_of? Array
600
+ d.each do |de|
601
+ if (explicit_fields(de) and (de['columns'] || de['fields'] || has_fields)) or
602
+ (de['columns'] and (de['fields'] || has_fields)) or
603
+ (de['fields'] and has_fields)
604
+ raise 'Fields are defined in too many ways. Please only define once.' # ahhh what, to do...
605
+ end
606
+
607
+ # let users who chose, to define fields explicitly within the hash definition
608
+ next if explicit_fields de
609
+
610
+ de['Fields'] = {'Field' => de['columns'] || de['fields'] || fields}
611
+ # sanitize
612
+
613
+ raise 'DataExtension needs atleast one field.' unless de['Fields']['Field']
614
+ end
615
+ else
616
+ self.properties['Fields'] = {'Field' => self.properties['columns'] || self.properties['fields'] || fields}
617
+ raise 'DataExtension needs atleast one field.' unless self.properties['Fields']['Field']
618
+ self.properties.delete 'columns'
619
+ self.properties.delete 'fields'
620
+ end
621
+ end
622
+
623
+ def explicit_fields h
624
+ h['Fields'] and h['Fields']['Field']
625
+ end
626
+
627
+ def has_fields
628
+ fields and !fields.empty?
629
+ end
630
+ end
631
+
632
+ class Campaign < Objects::Base
633
+ include Objects::Rest::Read
634
+ include Objects::Rest::CUD
635
+
636
+ def properties
637
+ @properties ||= {}
638
+ @properties.merge! 'id' => '' unless @properties.include? 'id'
639
+ @properties
640
+ end
641
+
642
+ def id
643
+ "https://www.exacttargetapis.com/hub/v1/campaigns/%{id}"
644
+ end
645
+
646
+ class Asset < Objects::Base
647
+ include Objects::Rest::Read
648
+ include Objects::Rest::CUD
649
+
650
+ def properties
651
+ @properties ||= {}
652
+ @properties.merge! 'assetId' => '' unless @properties.include? 'assetId'
653
+ @properties
654
+ end
655
+
656
+ def id
657
+ 'https://www.exacttargetapis.com/hub/v1/campaigns/%{id}/assets/%{assetId}'
658
+ end
659
+ end
660
+ end
661
+
662
+ # Direct Verb Access Section
663
+
664
+ class Get < Objects::Base
665
+ include Objects::Soap::Read
666
+ attr_accessor :id
667
+
668
+ def initialize client, id, properties, filter
669
+ self.properties = properties
670
+ self.filter = filter
671
+ self.client = client
672
+ self.id = id
673
+ end
674
+
675
+ def get
676
+ super id
677
+ end
678
+
679
+ class << self
680
+ def new client, id, properties=nil, filter=nil
681
+ o = self.allocate
682
+ o.send :initialize, client, id, properties, filter
683
+ return o.get
684
+ end
685
+ end
686
+ end
687
+
688
+ class Post < Objects::Base
689
+ include Objects::Soap::CUD
690
+ attr_accessor :id
691
+
692
+ def initialize client, id, properties
693
+ self.properties = properties
694
+ self.client = client
695
+ self.id = id
696
+ end
697
+
698
+ def post
699
+ super
700
+ end
701
+
702
+ class << self
703
+ def new client, id, properties=nil
704
+ o = self.allocate
705
+ o.send :initialize, client, id, properties
706
+ return o.post
707
+ end
708
+ end
709
+ end
710
+
711
+ class Delete < Objects::Base
712
+ include Objects::Soap::CUD
713
+ attr_accessor :id
714
+
715
+ def initialize client, id, properties
716
+ self.properties = properties
717
+ self.client = client
718
+ self.id = id
719
+ end
720
+
721
+ def delete
722
+ super
723
+ end
724
+
725
+ class << self
726
+ def new client, id, properties=nil
727
+ o = self.allocate
728
+ o.send :initialize, client, id, properties
729
+ return o.delete
730
+ end
731
+ end
732
+ end
733
+
734
+ class Patch < Objects::Base
735
+ include Objects::Soap::CUD
736
+ attr_accessor :id
737
+
738
+ def initialize client, id, properties
739
+ self.properties = properties
740
+ self.client = client
741
+ self.id = id
742
+ end
743
+
744
+ def patch
745
+ super
746
+ end
747
+
748
+ class << self
749
+ def new client, id, properties=nil
750
+ o = self.allocate
751
+ o.send :initialize, client, id, properties
752
+ return o.patch
753
+ end
754
+ end
755
+ end
756
+
757
+ end