fuelsdk 0.0.1

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