fuelsdk_json_bump 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 (57) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +26 -0
  3. data/Gemfile +3 -0
  4. data/Gemfile.lock +91 -0
  5. data/Guardfile +8 -0
  6. data/LICENSE.md +13 -0
  7. data/README.md +127 -0
  8. data/Rakefile +1 -0
  9. data/fuelsdk.gemspec +30 -0
  10. data/lib/fuelsdk.rb +74 -0
  11. data/lib/fuelsdk/client.rb +282 -0
  12. data/lib/fuelsdk/http_request.rb +116 -0
  13. data/lib/fuelsdk/objects.rb +757 -0
  14. data/lib/fuelsdk/rest.rb +122 -0
  15. data/lib/fuelsdk/soap.rb +288 -0
  16. data/lib/fuelsdk/targeting.rb +58 -0
  17. data/lib/fuelsdk/utils.rb +47 -0
  18. data/lib/fuelsdk/version.rb +39 -0
  19. data/lib/new.rb +1240 -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 +200 -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 +250 -0
@@ -0,0 +1,47 @@
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 FuelSDK
38
+ module_function
39
+ def format_name_value_pairs attributes
40
+ attrs = []
41
+ attributes.each do |name, value|
42
+ attrs.push 'Name' => name, 'Value' => value
43
+ end
44
+
45
+ attrs
46
+ end
47
+ end
@@ -0,0 +1,39 @@
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 FuelSDK
38
+ VERSION = "0.0.5"
39
+ end
data/lib/new.rb ADDED
@@ -0,0 +1,1240 @@
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 "fuelsdk/version"
38
+
39
+ require 'rubygems'
40
+ require 'open-uri'
41
+ require 'savon'
42
+ require 'date'
43
+ require 'json'
44
+ require 'yaml'
45
+ require 'jwt'
46
+
47
+
48
+ def indifferent_access key, hash
49
+ hash[key.to_sym] || hash[key.to_s]
50
+ end
51
+
52
+ module FuelSDK
53
+
54
+ class Soap
55
+ def client
56
+ 'soap'
57
+ end
58
+ end
59
+
60
+ class Rest
61
+ def client
62
+ 'rest'
63
+ end
64
+ end
65
+
66
+ class Client
67
+ attr_reader :id, :secret, :signature
68
+
69
+ def initialize params={}, debug=false
70
+ @debug = debug
71
+ @id = indifferent_access :clientid, params
72
+ @secret = indifferent_access :clientsecret, params
73
+ @signature = indifferent_access :appsignature, params
74
+ end
75
+ end
76
+
77
+ class SoapClient < Client
78
+ def initialize getWSDL=true, params={}, debug=false
79
+ super params, debug
80
+ @wsdl = params["defaultwsdl"]
81
+ end
82
+ end
83
+
84
+ class RestClient < Client
85
+ end
86
+
87
+
88
+ # parse response
89
+ class ET_Constructor
90
+ attr_accessor :status, :code, :message, :results, :request_id, :moreResults
91
+
92
+ def initialize(response = nil, rest = false)
93
+ @results = []
94
+ if !response.nil? && !rest then
95
+ envelope = response.hash[:envelope]
96
+ @@body = envelope[:body]
97
+
98
+ if ((!response.soap_fault?) or (!response.http_error?)) then
99
+ @code = response.http.code
100
+ @status = true
101
+ elsif (response.soap_fault?) then
102
+ @code = response.http.code
103
+ @message = @@body[:fault][:faultstring]
104
+ @status = false
105
+ elsif (response.http_error?) then
106
+ @code = response.http.code
107
+ @status = false
108
+ end
109
+ elsif
110
+ @code = response.code
111
+ @status = true
112
+ if @code != "200" then
113
+ @status = false
114
+ end
115
+
116
+ begin
117
+ @results = JSON.parse(response.body)
118
+ rescue
119
+ @message = response.body
120
+ end
121
+
122
+ end
123
+ end
124
+ end
125
+
126
+ class ET_CreateWSDL
127
+
128
+ def initialize(path)
129
+ # Get the header info for the correct wsdl
130
+ response = HTTPI.head(@wsdl)
131
+ if response and (response.code >= 200 and response.code <= 400) then
132
+ header = response.headers
133
+ # Check when the WSDL was last modified
134
+ modifiedTime = Date.parse(header['last-modified'])
135
+ p = path + '/ExactTargetWSDL.xml'
136
+ # Check if a local file already exists
137
+ if (File.file?(p) and File.readable?(p) and !File.zero?(p)) then
138
+ createdTime = File.new(p).mtime.to_date
139
+
140
+ # Check if the locally created WSDL older than the production WSDL
141
+ if createdTime < modifiedTime then
142
+ createIt = true
143
+ else
144
+ createIt = false
145
+ end
146
+ else
147
+ createIt = true
148
+ end
149
+
150
+ if createIt then
151
+ res = open(@wsdl).read
152
+ File.open(p, 'w+') { |f|
153
+ f.write(res)
154
+ }
155
+ end
156
+ @status = response.code
157
+ else
158
+ @status = response.code
159
+ end
160
+ end
161
+ end
162
+
163
+ class ET_Client < ET_CreateWSDL
164
+ attr_accessor :auth, :ready, :status, :debug, :authToken
165
+ attr_reader :authTokenExpiration,:internalAuthToken, :wsdlLoc, :clientId,
166
+ :clientSecret, :soapHeader, :authObj, :path, :appsignature, :stackID, :refreshKey
167
+
168
+ def initialize(getWSDL = true, debug = false, params = nil)
169
+ config = YAML.load_file("config.yaml")
170
+ @clientId = config["clientid"]
171
+ @clientSecret = config["clientsecret"]
172
+ @appsignature = config["appsignature"]
173
+ @wsdl = config["defaultwsdl"]
174
+ @debug = debug
175
+
176
+ begin
177
+ @path = File.dirname(__FILE__)
178
+
179
+ #make a new WSDL
180
+ if getWSDL then
181
+ super(@path)
182
+ end
183
+
184
+ if params && params.has_key?("jwt") then
185
+ jwt = JWT.decode(params["jwt"], @appsignature, true);
186
+ @authToken = jwt['request']['user']['oauthToken']
187
+ @authTokenExpiration = Time.new + jwt['request']['user']['expiresIn']
188
+ @internalAuthToken = jwt['request']['user']['internalOauthToken']
189
+ @refreshKey = jwt['request']['user']['refreshToken']
190
+
191
+ self.determineStack
192
+
193
+ @authObj = {'oAuth' => {'oAuthToken' => @internalAuthToken}}
194
+ @authObj[:attributes!] = { 'oAuth' => { 'xmlns' => 'http://exacttarget.com' }}
195
+
196
+ myWSDL = File.read(@path + '/ExactTargetWSDL.xml')
197
+ @auth = Savon.client(
198
+ soap_header: @authObj,
199
+ wsdl: myWSDL,
200
+ endpoint: @endpoint,
201
+ wsse_auth: ["*", "*"],
202
+ raise_errors: false,
203
+ log: @debug,
204
+ open_timeout:180,
205
+ read_timeout: 180
206
+ )
207
+ else
208
+ self.refreshToken
209
+ end
210
+
211
+ rescue
212
+ raise
213
+ end
214
+
215
+ if ((@auth.operations.length > 0) and (@status >= 200 and @status <= 400)) then
216
+ @ready = true
217
+ else
218
+ @ready = false
219
+ end
220
+ end
221
+
222
+ def debug=(value)
223
+ @debug = value
224
+ end
225
+
226
+ def refreshToken(force = nil)
227
+ #If we don't already have a token or the token expires within 5 min(300 seconds), get one
228
+ if ((@authToken.nil? || Time.new + 300 > @authTokenExpiration) || force) then
229
+ begin
230
+ uri = URI.parse("https://auth.exacttargetapis.com/v1/requestToken?legacy=1")
231
+ http = Net::HTTP.new(uri.host, uri.port)
232
+ http.use_ssl = true
233
+ request = Net::HTTP::Post.new(uri.request_uri)
234
+ jsonPayload = {'clientId' => @clientId, 'clientSecret' => @clientSecret}
235
+
236
+ #Pass in the refreshKey if we have it
237
+ if @refreshKey then
238
+ jsonPayload['refreshToken'] = @refreshKey
239
+ end
240
+
241
+ request.body = jsonPayload.to_json
242
+ request.add_field "Content-Type", "application/json"
243
+ tokenResponse = JSON.parse(http.request(request).body)
244
+
245
+ if !tokenResponse.has_key?('accessToken') then
246
+ raise 'Unable to validate App Keys(ClientID/ClientSecret) provided: ' + http.request(request).body
247
+ end
248
+
249
+ @authToken = tokenResponse['accessToken']
250
+ @authTokenExpiration = Time.new + tokenResponse['expiresIn']
251
+ @internalAuthToken = tokenResponse['legacyToken']
252
+ if tokenResponse.has_key?("refreshToken") then
253
+ @refreshKey = tokenResponse['refreshToken']
254
+ end
255
+
256
+ if @endpoint.nil? then
257
+ self.determineStack
258
+ end
259
+
260
+ @authObj = {'oAuth' => {'oAuthToken' => @internalAuthToken}}
261
+ @authObj[:attributes!] = { 'oAuth' => { 'xmlns' => 'http://exacttarget.com' }}
262
+
263
+ myWSDL = File.read(@path + '/ExactTargetWSDL.xml')
264
+ @auth = Savon.client(
265
+ soap_header: @authObj,
266
+ wsdl: myWSDL,
267
+ endpoint: @endpoint,
268
+ wsse_auth: ["*", "*"],
269
+ raise_errors: false,
270
+ log: @debug
271
+ )
272
+
273
+ rescue Exception => e
274
+ raise 'Unable to validate App Keys(ClientID/ClientSecret) provided: ' + e.message
275
+ end
276
+ end
277
+ end
278
+
279
+ def AddSubscriberToList(emailAddress, listIDs, subscriberKey = nil)
280
+ newSub = ET_Subscriber.new
281
+ newSub.authStub = self
282
+ lists = []
283
+
284
+ listIDs.each{ |p|
285
+ lists.push({"ID"=> p})
286
+ }
287
+
288
+ newSub.props = {"EmailAddress" => emailAddress, "Lists" => lists}
289
+ if !subscriberKey.nil? then
290
+ newSub.props['SubscriberKey'] = subscriberKey;
291
+ end
292
+
293
+ # Try to add the subscriber
294
+ postResponse = newSub.post
295
+
296
+ if postResponse.status == false then
297
+ # If the subscriber already exists in the account then we need to do an update.
298
+ # Update Subscriber On List
299
+ if postResponse.results[0][:error_code] == "12014" then
300
+ patchResponse = newSub.patch
301
+ return patchResponse
302
+ end
303
+ end
304
+ return postResponse
305
+ end
306
+
307
+ def CreateDataExtensions(dataExtensionDefinitions)
308
+ newDEs = ET_DataExtension.new
309
+ newDEs.authStub = self
310
+
311
+ newDEs.props = dataExtensionDefinitions
312
+ postResponse = newDEs.post
313
+
314
+ return postResponse
315
+ end
316
+
317
+
318
+ protected
319
+ def determineStack()
320
+ begin
321
+ uri = URI.parse("https://www.exacttargetapis.com/platform/v1/endpoints/soap?access_token=" + @authToken)
322
+ http = Net::HTTP.new(uri.host, uri.port)
323
+
324
+ http.use_ssl = true
325
+
326
+ request = Net::HTTP::Get.new(uri.request_uri)
327
+
328
+ contextResponse = JSON.parse(http.request(request).body)
329
+ @endpoint = contextResponse['url']
330
+
331
+ rescue StandardError => e
332
+ raise 'Unable to determine stack using /platform/v1/tokenContext: ' + e.message
333
+ end
334
+ end
335
+ end
336
+
337
+ class ET_Describe < ET_Constructor
338
+ def initialize(authStub = nil, objType = nil)
339
+ begin
340
+ authStub.refreshToken
341
+ response = authStub.auth.call(:describe, :message => {
342
+ 'DescribeRequests' =>
343
+ {'ObjectDefinitionRequest' =>
344
+ {'ObjectType' => objType}
345
+ }
346
+ })
347
+ ensure
348
+ super(response)
349
+
350
+ if @status then
351
+ objDef = @@body[:definition_response_msg][:object_definition]
352
+
353
+ if objDef then
354
+ @overallStatus = true
355
+ else
356
+ @overallStatus = false
357
+ end
358
+ @results = @@body[:definition_response_msg][:object_definition][:properties]
359
+ end
360
+ end
361
+ end
362
+ end
363
+
364
+ class ET_Post < ET_Constructor
365
+ def initialize(authStub, objType, props = nil)
366
+ @results = []
367
+
368
+ begin
369
+ authStub.refreshToken
370
+ if props.is_a? Array then
371
+ obj = {
372
+ 'Objects' => [],
373
+ :attributes! => { 'Objects' => { 'xsi:type' => ('tns:' + objType) } }
374
+ }
375
+ props.each{ |p|
376
+ obj['Objects'] << p
377
+ }
378
+ else
379
+ obj = {
380
+ 'Objects' => props,
381
+ :attributes! => { 'Objects' => { 'xsi:type' => ('tns:' + objType) } }
382
+ }
383
+ end
384
+
385
+ response = authStub.auth.call(:create, :message => obj)
386
+
387
+ ensure
388
+ super(response)
389
+ if @status then
390
+ if @@body[:create_response][:overall_status] != "OK"
391
+ @status = false
392
+ end
393
+ #@results = @@body[:create_response][:results]
394
+ if !@@body[:create_response][:results].nil? then
395
+ if !@@body[:create_response][:results].is_a? Hash then
396
+ @results = @results + @@body[:create_response][:results]
397
+ else
398
+ @results.push(@@body[:create_response][:results])
399
+ end
400
+ end
401
+ end
402
+ end
403
+ end
404
+ end
405
+
406
+ class ET_Delete < ET_Constructor
407
+
408
+ def initialize(authStub, objType, props = nil)
409
+ @results = []
410
+ begin
411
+ authStub.refreshToken
412
+ if props.is_a? Array then
413
+ obj = {
414
+ 'Objects' => [],
415
+ :attributes! => { 'Objects' => { 'xsi:type' => ('tns:' + objType) } }
416
+ }
417
+ props.each{ |p|
418
+ obj['Objects'] << p
419
+ }
420
+ else
421
+ obj = {
422
+ 'Objects' => props,
423
+ :attributes! => { 'Objects' => { 'xsi:type' => ('tns:' + objType) } }
424
+ }
425
+ end
426
+
427
+ response = authStub.auth.call(:delete, :message => obj)
428
+ ensure
429
+ super(response)
430
+ if @status then
431
+ if @@body[:delete_response][:overall_status] != "OK"
432
+ @status = false
433
+ end
434
+ if !@@body[:delete_response][:results].is_a? Hash then
435
+ @results = @results + @@body[:delete_response][:results]
436
+ else
437
+ @results.push(@@body[:delete_response][:results])
438
+ end
439
+ end
440
+ end
441
+ end
442
+ end
443
+
444
+ class ET_Patch < ET_Constructor
445
+ def initialize(authStub, objType, props = nil)
446
+ @results = []
447
+ begin
448
+ authStub.refreshToken
449
+ if props.is_a? Array then
450
+ obj = {
451
+ 'Objects' => [],
452
+ :attributes! => { 'Objects' => { 'xsi:type' => ('tns:' + objType) } }
453
+ }
454
+ props.each{ |p|
455
+ obj['Objects'] << p
456
+ }
457
+ else
458
+ obj = {
459
+ 'Objects' => props,
460
+ :attributes! => { 'Objects' => { 'xsi:type' => ('tns:' + objType) } }
461
+ }
462
+ end
463
+
464
+ response = authStub.auth.call(:update, :message => obj)
465
+
466
+ ensure
467
+ super(response)
468
+ if @status then
469
+ if @@body[:update_response][:overall_status] != "OK"
470
+ @status = false
471
+ end
472
+ if !@@body[:update_response][:results].is_a? Hash then
473
+ @results = @results + @@body[:update_response][:results]
474
+ else
475
+ @results.push(@@body[:update_response][:results])
476
+ end
477
+ end
478
+ end
479
+ end
480
+ end
481
+
482
+ class ET_Continue < ET_Constructor
483
+ def initialize(authStub, request_id)
484
+ @results = []
485
+ authStub.refreshToken
486
+ obj = {'ContinueRequest' => request_id}
487
+ response = authStub.auth.call(:retrieve, :message => {'RetrieveRequest' => obj})
488
+
489
+ super(response)
490
+
491
+ if @status then
492
+ if @@body[:retrieve_response_msg][:overall_status] != "OK" && @@body[:retrieve_response_msg][:overall_status] != "MoreDataAvailable" then
493
+ @status = false
494
+ @message = @@body[:retrieve_response_msg][:overall_status]
495
+ end
496
+
497
+ @moreResults = false
498
+ if @@body[:retrieve_response_msg][:overall_status] == "MoreDataAvailable" then
499
+ @moreResults = true
500
+ end
501
+
502
+ if (!@@body[:retrieve_response_msg][:results].is_a? Hash) && (!@@body[:retrieve_response_msg][:results].nil?) then
503
+ @results = @results + @@body[:retrieve_response_msg][:results]
504
+ elsif (!@@body[:retrieve_response_msg][:results].nil?)
505
+ @results.push(@@body[:retrieve_response_msg][:results])
506
+ end
507
+
508
+ # Store the Last Request ID for use with continue
509
+ @request_id = @@body[:retrieve_response_msg][:request_id]
510
+ end
511
+ end
512
+ end
513
+
514
+ class ET_Get < ET_Constructor
515
+ def initialize(authStub, objType, props = nil, filter = nil)
516
+ @results = []
517
+ authStub.refreshToken
518
+ if !props then
519
+ resp = ET_Describe.new(authStub, objType)
520
+ if resp then
521
+ props = []
522
+ resp.results.map { |p|
523
+ if p[:is_retrievable] then
524
+ props << p[:name]
525
+ end
526
+ }
527
+ end
528
+ end
529
+
530
+ # If the properties is a hash, then we just want to use the keys
531
+ if props.is_a? Hash then
532
+ obj = {'ObjectType' => objType,'Properties' => props.keys}
533
+ else
534
+ obj = {'ObjectType' => objType,'Properties' => props}
535
+ end
536
+
537
+ if filter then
538
+ if filter.has_key?('LogicalOperator') then
539
+ obj['Filter'] = filter
540
+ obj[:attributes!] = { 'Filter' => { 'xsi:type' => 'tns:ComplexFilterPart' }}
541
+ obj['Filter'][:attributes!] = { 'LeftOperand' => { 'xsi:type' => 'tns:SimpleFilterPart' }, 'RightOperand' => { 'xsi:type' => 'tns:SimpleFilterPart' }}
542
+ else
543
+ obj['Filter'] = filter
544
+ obj[:attributes!] = { 'Filter' => { 'xsi:type' => 'tns:SimpleFilterPart' } }
545
+ end
546
+ end
547
+
548
+ response = authStub.auth.call(:retrieve, :message => {
549
+ 'RetrieveRequest' => obj
550
+ })
551
+
552
+ super(response)
553
+
554
+ if @status then
555
+ if @@body[:retrieve_response_msg][:overall_status] != "OK" && @@body[:retrieve_response_msg][:overall_status] != "MoreDataAvailable" then
556
+ @status = false
557
+ @message = @@body[:retrieve_response_msg][:overall_status]
558
+ end
559
+
560
+ @moreResults = false
561
+ if @@body[:retrieve_response_msg][:overall_status] == "MoreDataAvailable" then
562
+ @moreResults = true
563
+ end
564
+
565
+ if (!@@body[:retrieve_response_msg][:results].is_a? Hash) && (!@@body[:retrieve_response_msg][:results].nil?) then
566
+ @results = @results + @@body[:retrieve_response_msg][:results]
567
+ elsif (!@@body[:retrieve_response_msg][:results].nil?)
568
+ @results.push(@@body[:retrieve_response_msg][:results])
569
+ end
570
+
571
+ # Store the Last Request ID for use with continue
572
+ @request_id = @@body[:retrieve_response_msg][:request_id]
573
+ end
574
+ end
575
+ end
576
+
577
+ class ET_BaseObject
578
+ attr_accessor :authStub, :props
579
+ attr_reader :obj, :lastRequestID, :endpoint
580
+
581
+ def initialize
582
+ @authStub = nil
583
+ @props = nil
584
+ @filter = nil
585
+ @lastRequestID = nil
586
+ @endpoint = nil
587
+ end
588
+ end
589
+
590
+ class ET_GetSupport < ET_BaseObject
591
+ attr_accessor :filter
592
+
593
+ def get(props = nil, filter = nil)
594
+ if props and props.is_a? Array then
595
+ @props = props
596
+ end
597
+
598
+ if @props and @props.is_a? Hash then
599
+ @props = @props.keys
600
+ end
601
+
602
+ if filter and filter.is_a? Hash then
603
+ @filter = filter
604
+ end
605
+ obj = ET_Get.new(@authStub, @obj, @props, @filter)
606
+
607
+ @lastRequestID = obj.request_id
608
+
609
+ return obj
610
+ end
611
+
612
+ def info()
613
+ ET_Describe.new(@authStub, @obj)
614
+ end
615
+
616
+ def getMoreResults()
617
+ ET_Continue.new(@authStub, @lastRequestID)
618
+ end
619
+ end
620
+
621
+ class ET_CUDSupport < ET_GetSupport
622
+
623
+ def post()
624
+ if props and props.is_a? Hash then
625
+ @props = props
626
+ end
627
+
628
+ if @extProps then
629
+ @extProps.each { |key, value|
630
+ @props[key.capitalize] = value
631
+ }
632
+ end
633
+
634
+ ET_Post.new(@authStub, @obj, @props)
635
+ end
636
+
637
+ def patch()
638
+ if props and props.is_a? Hash then
639
+ @props = props
640
+ end
641
+
642
+ ET_Patch.new(@authStub, @obj, @props)
643
+ end
644
+
645
+ def delete()
646
+ if props and props.is_a? Hash then
647
+ @props = props
648
+ end
649
+
650
+ ET_Delete.new(@authStub, @obj, @props)
651
+ end
652
+ end
653
+
654
+ class ET_GetSupportRest < ET_BaseObject
655
+ attr_reader :urlProps, :urlPropsRequired, :lastPageNumber
656
+
657
+ def get(props = nil)
658
+ if props and props.is_a? Hash then
659
+ @props = props
660
+ end
661
+
662
+ completeURL = @endpoint
663
+ additionalQS = {}
664
+
665
+ if @props and @props.is_a? Hash then
666
+ @props.each do |k,v|
667
+ if @urlProps.include?(k) then
668
+ completeURL.sub!("{#{k}}", v)
669
+ else
670
+ additionalQS[k] = v
671
+ end
672
+ end
673
+ end
674
+
675
+ @urlPropsRequired.each do |value|
676
+ if !@props || !@props.has_key?(value) then
677
+ raise "Unable to process request due to missing required prop: #{value}"
678
+ end
679
+ end
680
+
681
+ @urlProps.each do |value|
682
+ completeURL.sub!("/{#{value}}", "")
683
+ end
684
+
685
+ obj = ET_GetRest.new(@authStub, completeURL,additionalQS)
686
+
687
+ if obj.results.has_key?('page') then
688
+ @lastPageNumber = obj.results['page']
689
+ pageSize = obj.results['pageSize']
690
+ if obj.results.has_key?('count') then
691
+ count = obj.results['count']
692
+ elsif obj.results.has_key?('totalCount') then
693
+ count = obj.results['totalCount']
694
+ end
695
+
696
+ if !count.nil? && count > (@lastPageNumber * pageSize) then
697
+ obj.moreResults = true
698
+ end
699
+ end
700
+ return obj
701
+ end
702
+
703
+ def getMoreResults()
704
+ if props and props.is_a? Hash then
705
+ @props = props
706
+ end
707
+
708
+ originalPageValue = "1"
709
+ removePageFromProps = false
710
+
711
+ if !@props.nil? && @props.has_key?('$page') then
712
+ originalPageValue = @props['page']
713
+ else
714
+ removePageFromProps = true
715
+ end
716
+
717
+ if @props.nil?
718
+ @props = {}
719
+ end
720
+
721
+ @props['$page'] = @lastPageNumber + 1
722
+
723
+ obj = self.get
724
+
725
+ if removePageFromProps then
726
+ @props.delete('$page')
727
+ else
728
+ @props['$page'] = originalPageValue
729
+ end
730
+
731
+ return obj
732
+ end
733
+ end
734
+
735
+ class ET_CUDSupportRest < ET_GetSupportRest
736
+
737
+ def post()
738
+ completeURL = @endpoint
739
+
740
+ if @props and @props.is_a? Hash then
741
+ @props.each do |k,v|
742
+ if @urlProps.include?(k) then
743
+ completeURL.sub!("{#{k}}", v)
744
+ end
745
+ end
746
+ end
747
+
748
+ @urlPropsRequired.each do |value|
749
+ if !@props || !@props.has_key?(value) then
750
+ raise "Unable to process request due to missing required prop: #{value}"
751
+ end
752
+ end
753
+
754
+ # Clean Optional Parameters from Endpoint URL first
755
+ @urlProps.each do |value|
756
+ completeURL.sub!("/{#{value}}", "")
757
+ end
758
+
759
+ ET_PostRest.new(@authStub, completeURL, @props)
760
+ end
761
+
762
+ def patch()
763
+ completeURL = @endpoint
764
+ # All URL Props are required when doing Patch
765
+ @urlProps.each do |value|
766
+ if !@props || !@props.has_key?(value) then
767
+ raise "Unable to process request due to missing required prop: #{value}"
768
+ end
769
+ end
770
+
771
+ if @props and @props.is_a? Hash then
772
+ @props.each do |k,v|
773
+ if @urlProps.include?(k) then
774
+ completeURL.sub!("{#{k}}", v)
775
+ end
776
+ end
777
+ end
778
+
779
+ obj = ET_PatchRest.new(@authStub, completeURL, @props)
780
+ end
781
+
782
+ def delete()
783
+ completeURL = @endpoint
784
+ # All URL Props are required when doing Patch
785
+ @urlProps.each do |value|
786
+ if !@props || !@props.has_key?(value) then
787
+ raise "Unable to process request due to missing required prop: #{value}"
788
+ end
789
+ end
790
+
791
+ if @props and @props.is_a? Hash then
792
+ @props.each do |k,v|
793
+ if @urlProps.include?(k) then
794
+ completeURL.sub!("{#{k}}", v)
795
+ end
796
+ end
797
+ end
798
+
799
+ ET_DeleteRest.new(@authStub, completeURL)
800
+ end
801
+
802
+ end
803
+
804
+
805
+ class ET_GetRest < ET_Constructor
806
+ def initialize(authStub, endpoint, qs = nil)
807
+ authStub.refreshToken
808
+
809
+ if qs then
810
+ qs['access_token'] = authStub.authToken
811
+ else
812
+ qs = {"access_token" => authStub.authToken}
813
+ end
814
+
815
+ uri = URI.parse(endpoint)
816
+ uri.query = URI.encode_www_form(qs)
817
+ http = Net::HTTP.new(uri.host, uri.port)
818
+ http.use_ssl = true
819
+ request = Net::HTTP::Get.new(uri.request_uri)
820
+ requestResponse = http.request(request)
821
+
822
+ @moreResults = false
823
+
824
+ obj = super(requestResponse, true)
825
+ return obj
826
+ end
827
+ end
828
+
829
+
830
+ class ET_ContinueRest < ET_Constructor
831
+ def initialize(authStub, endpoint, qs = nil)
832
+ authStub.refreshToken
833
+
834
+ if qs then
835
+ qs['access_token'] = authStub.authToken
836
+ else
837
+ qs = {"access_token" => authStub.authToken}
838
+ end
839
+
840
+ uri = URI.parse(endpoint)
841
+ uri.query = URI.encode_www_form(qs)
842
+ http = Net::HTTP.new(uri.host, uri.port)
843
+ http.use_ssl = true
844
+ request = Net::HTTP::Get.new(uri.request_uri)
845
+ requestResponse = http.request(request)
846
+
847
+ @moreResults = false
848
+
849
+ super(requestResponse, true)
850
+ end
851
+ end
852
+
853
+
854
+ class ET_PostRest < ET_Constructor
855
+ def initialize(authStub, endpoint, payload)
856
+ authStub.refreshToken
857
+
858
+ qs = {"access_token" => authStub.authToken}
859
+ uri = URI.parse(endpoint)
860
+ uri.query = URI.encode_www_form(qs)
861
+ http = Net::HTTP.new(uri.host, uri.port)
862
+ http.use_ssl = true
863
+ request = Net::HTTP::Post.new(uri.request_uri)
864
+ request.body = payload.to_json
865
+ request.add_field "Content-Type", "application/json"
866
+ requestResponse = http.request(request)
867
+
868
+ super(requestResponse, true)
869
+
870
+ end
871
+ end
872
+
873
+ class ET_PatchRest < ET_Constructor
874
+ def initialize(authStub, endpoint, payload)
875
+ authStub.refreshToken
876
+
877
+ qs = {"access_token" => authStub.authToken}
878
+ uri = URI.parse(endpoint)
879
+ uri.query = URI.encode_www_form(qs)
880
+ http = Net::HTTP.new(uri.host, uri.port)
881
+ http.use_ssl = true
882
+ request = Net::HTTP::Patch.new(uri.request_uri)
883
+ request.body = payload.to_json
884
+ request.add_field "Content-Type", "application/json"
885
+ requestResponse = http.request(request)
886
+ super(requestResponse, true)
887
+
888
+ end
889
+ end
890
+
891
+ class ET_DeleteRest < ET_Constructor
892
+ def initialize(authStub, endpoint)
893
+ authStub.refreshToken
894
+
895
+ qs = {"access_token" => authStub.authToken}
896
+
897
+ uri = URI.parse(endpoint)
898
+ uri.query = URI.encode_www_form(qs)
899
+ http = Net::HTTP.new(uri.host, uri.port)
900
+ http.use_ssl = true
901
+ request = Net::HTTP::Delete.new(uri.request_uri)
902
+ requestResponse = http.request(request)
903
+ super(requestResponse, true)
904
+
905
+ end
906
+ end
907
+
908
+ class ET_Campaign < ET_CUDSupportRest
909
+ def initialize
910
+ super
911
+ @endpoint = 'https://www.exacttargetapis.com/hub/v1/campaigns/{id}'
912
+ @urlProps = ["id"]
913
+ @urlPropsRequired = []
914
+ end
915
+
916
+ class Asset < ET_CUDSupportRest
917
+ def initialize
918
+ super
919
+ @endpoint = 'https://www.exacttargetapis.com/hub/v1/campaigns/{id}/assets/{assetId}'
920
+ @urlProps = ["id", "assetId"]
921
+ @urlPropsRequired = ["id"]
922
+ end
923
+ end
924
+ end
925
+
926
+ class ET_Subscriber < ET_CUDSupport
927
+ def initialize
928
+ super
929
+ @obj = 'Subscriber'
930
+ end
931
+ end
932
+
933
+ class ET_DataExtension < ET_CUDSupport
934
+ attr_accessor :columns
935
+
936
+ def initialize
937
+ super
938
+ @obj = 'DataExtension'
939
+ end
940
+
941
+ def post
942
+ originalProps = @props
943
+
944
+ if @props.is_a? Array then
945
+ multiDE = []
946
+ @props.each { |currentDE|
947
+ currentDE['Fields'] = {}
948
+ currentDE['Fields']['Field'] = []
949
+ currentDE['columns'].each { |key|
950
+ currentDE['Fields']['Field'].push(key)
951
+ }
952
+ currentDE.delete('columns')
953
+ multiDE.push(currentDE.dup)
954
+ }
955
+
956
+ @props = multiDE
957
+ else
958
+ @props['Fields'] = {}
959
+ @props['Fields']['Field'] = []
960
+
961
+ @columns.each { |key|
962
+ @props['Fields']['Field'].push(key)
963
+ }
964
+ end
965
+
966
+ obj = super
967
+ @props = originalProps
968
+ return obj
969
+ end
970
+
971
+ def patch
972
+ @props['Fields'] = {}
973
+ @props['Fields']['Field'] = []
974
+ @columns.each { |key|
975
+ @props['Fields']['Field'].push(key)
976
+ }
977
+ obj = super
978
+ @props.delete("Fields")
979
+ return obj
980
+ end
981
+
982
+ class Column < ET_GetSupport
983
+ def initialize
984
+ super
985
+ @obj = 'DataExtensionField'
986
+ end
987
+
988
+ def get
989
+
990
+ if props and props.is_a? Array then
991
+ @props = props
992
+ end
993
+
994
+ if @props and @props.is_a? Hash then
995
+ @props = @props.keys
996
+ end
997
+
998
+ if filter and filter.is_a? Hash then
999
+ @filter = filter
1000
+ end
1001
+
1002
+ fixCustomerKey = false
1003
+ if filter and filter.is_a? Hash then
1004
+ @filter = filter
1005
+ if @filter.has_key?("Property") && @filter["Property"] == "CustomerKey" then
1006
+ @filter["Property"] = "DataExtension.CustomerKey"
1007
+ fixCustomerKey = true
1008
+ end
1009
+ end
1010
+
1011
+ obj = ET_Get.new(@authStub, @obj, @props, @filter)
1012
+ @lastRequestID = obj.request_id
1013
+
1014
+ if fixCustomerKey then
1015
+ @filter["Property"] = "CustomerKey"
1016
+ end
1017
+
1018
+ return obj
1019
+ end
1020
+ end
1021
+
1022
+ class Row < ET_CUDSupport
1023
+ attr_accessor :Name, :CustomerKey
1024
+
1025
+ def initialize()
1026
+ super
1027
+ @obj = "DataExtensionObject"
1028
+ end
1029
+
1030
+ def get
1031
+ getName
1032
+ if props and props.is_a? Array then
1033
+ @props = props
1034
+ end
1035
+
1036
+ if @props and @props.is_a? Hash then
1037
+ @props = @props.keys
1038
+ end
1039
+
1040
+ if filter and filter.is_a? Hash then
1041
+ @filter = filter
1042
+ end
1043
+
1044
+ obj = ET_Get.new(@authStub, "DataExtensionObject[#{@Name}]", @props, @filter)
1045
+ @lastRequestID = obj.request_id
1046
+
1047
+ return obj
1048
+ end
1049
+
1050
+ def post
1051
+ getCustomerKey
1052
+ originalProps = @props
1053
+ ## FIX THIS
1054
+ if @props.is_a? Array then
1055
+ =begin
1056
+ multiRow = []
1057
+ @props.each { |currentDE|
1058
+
1059
+ currentDE['columns'].each { |key|
1060
+ currentDE['Fields'] = {}
1061
+ currentDE['Fields']['Field'] = []
1062
+ currentDE['Fields']['Field'].push(key)
1063
+ }
1064
+ currentDE.delete('columns')
1065
+ multiRow.push(currentDE.dup)
1066
+ }
1067
+
1068
+ @props = multiRow
1069
+ =end
1070
+ else
1071
+ currentFields = []
1072
+ currentProp = {}
1073
+
1074
+ @props.each { |key,value|
1075
+ currentFields.push({"Name" => key, "Value" => value})
1076
+ }
1077
+ currentProp['CustomerKey'] = @CustomerKey
1078
+ currentProp['Properties'] = {}
1079
+ currentProp['Properties']['Property'] = currentFields
1080
+ end
1081
+
1082
+ obj = ET_Post.new(@authStub, @obj, currentProp)
1083
+ @props = originalProps
1084
+ obj
1085
+ end
1086
+
1087
+ def patch
1088
+ getCustomerKey
1089
+ currentFields = []
1090
+ currentProp = {}
1091
+
1092
+ @props.each { |key,value|
1093
+ currentFields.push({"Name" => key, "Value" => value})
1094
+ }
1095
+ currentProp['CustomerKey'] = @CustomerKey
1096
+ currentProp['Properties'] = {}
1097
+ currentProp['Properties']['Property'] = currentFields
1098
+
1099
+ ET_Patch.new(@authStub, @obj, currentProp)
1100
+ end
1101
+ def delete
1102
+ getCustomerKey
1103
+ currentFields = []
1104
+ currentProp = {}
1105
+
1106
+ @props.each { |key,value|
1107
+ currentFields.push({"Name" => key, "Value" => value})
1108
+ }
1109
+ currentProp['CustomerKey'] = @CustomerKey
1110
+ currentProp['Keys'] = {}
1111
+ currentProp['Keys']['Key'] = currentFields
1112
+
1113
+ ET_Delete.new(@authStub, @obj, currentProp)
1114
+ end
1115
+
1116
+ private
1117
+ def getCustomerKey
1118
+ if @CustomerKey.nil? then
1119
+ if @CustomerKey.nil? && @Name.nil? then
1120
+ raise 'Unable to process DataExtension::Row request due to CustomerKey and Name not being defined on ET_DatExtension::row'
1121
+ else
1122
+ de = ET_DataExtension.new
1123
+ de.authStub = @authStub
1124
+ de.props = ["Name","CustomerKey"]
1125
+ de.filter = {'Property' => 'CustomerKey','SimpleOperator' => 'equals','Value' => @Name}
1126
+ getResponse = de.get
1127
+ if getResponse.status && (getResponse.results.length == 1) then
1128
+ @CustomerKey = getResponse.results[0][:customer_key]
1129
+ else
1130
+ raise 'Unable to process DataExtension::Row request due to unable to find DataExtension based on Name'
1131
+ end
1132
+ end
1133
+ end
1134
+ end
1135
+
1136
+ def getName
1137
+ if @Name.nil? then
1138
+ if @CustomerKey.nil? && @Name.nil? then
1139
+ raise 'Unable to process DataExtension::Row request due to CustomerKey and Name not being defined on ET_DatExtension::row'
1140
+ else
1141
+ de = ET_DataExtension.new
1142
+ de.authStub = @authStub
1143
+ de.props = ["Name","CustomerKey"]
1144
+ de.filter = {'Property' => 'CustomerKey','SimpleOperator' => 'equals','Value' => @CustomerKey}
1145
+ getResponse = de.get
1146
+ if getResponse.status && (getResponse.results.length == 1) then
1147
+ @Name = getResponse.results[0][:name]
1148
+ else
1149
+ raise 'Unable to process DataExtension::Row request due to unable to find DataExtension based on CustomerKey'
1150
+ end
1151
+ end
1152
+ end
1153
+ end
1154
+ end
1155
+ end
1156
+
1157
+ class ET_List < ET_CUDSupport
1158
+ def initialize
1159
+ super
1160
+ @obj = 'List'
1161
+ end
1162
+
1163
+ class Subscriber < ET_GetSupport
1164
+ def initialize
1165
+ super
1166
+ @obj = 'ListSubscriber'
1167
+ end
1168
+ end
1169
+ end
1170
+
1171
+ class ET_Email < ET_CUDSupport
1172
+ def initialize
1173
+ super
1174
+ @obj = 'Email'
1175
+ end
1176
+ end
1177
+
1178
+ class ET_TriggeredSend < ET_CUDSupport
1179
+ attr_accessor :subscribers
1180
+ def initialize
1181
+ super
1182
+ @obj = 'TriggeredSendDefinition'
1183
+ end
1184
+
1185
+ def send
1186
+ @tscall = {"TriggeredSendDefinition" => @props, "Subscribers" => @subscribers}
1187
+ ET_Post.new(@authStub, "TriggeredSend", @tscall)
1188
+ end
1189
+ end
1190
+
1191
+ class ET_ContentArea < ET_CUDSupport
1192
+ def initialize
1193
+ super
1194
+ @obj = 'ContentArea'
1195
+ end
1196
+ end
1197
+
1198
+ class ET_Folder < ET_CUDSupport
1199
+ def initialize
1200
+ super
1201
+ @obj = 'DataFolder'
1202
+ end
1203
+ end
1204
+
1205
+ class ET_SentEvent < ET_GetSupport
1206
+ def initialize
1207
+ super
1208
+ @obj = 'SentEvent'
1209
+ end
1210
+ end
1211
+
1212
+ class ET_OpenEvent < ET_GetSupport
1213
+ def initialize
1214
+ super
1215
+ @obj = 'OpenEvent'
1216
+ end
1217
+ end
1218
+
1219
+ class ET_BounceEvent < ET_GetSupport
1220
+ def initialize
1221
+ super
1222
+ @obj = 'BounceEvent'
1223
+ end
1224
+ end
1225
+
1226
+ class ET_UnsubEvent < ET_GetSupport
1227
+ def initialize
1228
+ super
1229
+ @obj = 'UnsubEvent'
1230
+ end
1231
+ end
1232
+
1233
+ class ET_ClickEvent < ET_GetSupport
1234
+ def initialize
1235
+ super
1236
+ @obj = 'ClickEvent'
1237
+ end
1238
+ end
1239
+
1240
+ end