sfmc-fuelsdk-ruby 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +28 -28
  3. data/Gemfile +3 -3
  4. data/Gemfile.lock +92 -90
  5. data/Guardfile +8 -8
  6. data/LICENSE.md +13 -13
  7. data/README.md +166 -143
  8. data/Rakefile +1 -1
  9. data/lib/marketingcloudsdk.rb +74 -74
  10. data/lib/marketingcloudsdk/client.rb +348 -296
  11. data/lib/marketingcloudsdk/http_request.rb +118 -118
  12. data/lib/marketingcloudsdk/objects.rb +757 -757
  13. data/lib/marketingcloudsdk/rest.rb +118 -118
  14. data/lib/marketingcloudsdk/soap.rb +296 -282
  15. data/lib/marketingcloudsdk/targeting.rb +99 -99
  16. data/lib/marketingcloudsdk/utils.rb +47 -47
  17. data/lib/marketingcloudsdk/version.rb +39 -39
  18. data/lib/new.rb +1240 -1240
  19. data/marketingcloudsdk.gemspec +30 -30
  20. data/samples/sample-AddSubscriberToList.rb +56 -56
  21. data/samples/sample-CreateAndStartDataExtensionImport.rb +29 -29
  22. data/samples/sample-CreateAndStartListImport.rb +27 -27
  23. data/samples/sample-CreateContentAreas.rb +48 -48
  24. data/samples/sample-CreateDataExtensions.rb +54 -54
  25. data/samples/sample-CreateProfileAttributes.rb +48 -48
  26. data/samples/sample-SendEmailToDataExtension.rb +23 -23
  27. data/samples/sample-SendEmailToList.rb +23 -23
  28. data/samples/sample-SendTriggeredSends.rb +30 -30
  29. data/samples/sample-bounceevent.rb +70 -70
  30. data/samples/sample-campaign.rb +211 -211
  31. data/samples/sample-clickevent.rb +71 -71
  32. data/samples/sample-contentarea.rb +122 -122
  33. data/samples/sample-dataextension.rb +209 -209
  34. data/samples/sample-directverb.rb +54 -54
  35. data/samples/sample-email.rb +122 -122
  36. data/samples/sample-email.senddefinition.rb +134 -134
  37. data/samples/sample-folder.rb +143 -143
  38. data/samples/sample-import.rb +103 -103
  39. data/samples/sample-list.rb +105 -105
  40. data/samples/sample-list.subscriber.rb +97 -97
  41. data/samples/sample-openevent.rb +70 -70
  42. data/samples/sample-profileattribute.rb +56 -56
  43. data/samples/sample-sentevent.rb +70 -70
  44. data/samples/sample-subscriber.rb +135 -135
  45. data/samples/sample-triggeredsend.rb +129 -129
  46. data/samples/sample-unsubevent.rb +72 -72
  47. data/samples/sample_helper.rb.template +10 -10
  48. data/spec/client_spec.rb +218 -218
  49. data/spec/default_values_fallback_spec.rb +30 -30
  50. data/spec/helper_funcs_spec.rb +11 -11
  51. data/spec/http_request_spec.rb +61 -61
  52. data/spec/objects_helper_spec.rb +32 -32
  53. data/spec/objects_spec.rb +484 -484
  54. data/spec/rest_spec.rb +48 -48
  55. data/spec/soap_spec.rb +140 -140
  56. data/spec/spec_helper.rb +14 -14
  57. data/spec/targeting_spec.rb +44 -44
  58. metadata +9 -9
@@ -1,99 +1,99 @@
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 'json'
38
-
39
- module MarketingCloudSDK::Targeting
40
- attr_accessor :access_token
41
- attr_reader :endpoint, :soap_endpoint
42
-
43
- include MarketingCloudSDK::HTTPRequest
44
-
45
- def cache_file
46
- 'soap_cache_file.json'
47
- end
48
-
49
- def endpoint
50
- unless @endpoint
51
- get_soap_endpoint
52
- end
53
- @endpoint
54
- end
55
-
56
- def get_soap_endpoint_from_file
57
- data_hash = nil
58
-
59
- if File.exist? cache_file
60
- file = File.read(cache_file)
61
- data_hash = JSON.parse(file)
62
- end
63
-
64
- data_hash
65
- end
66
-
67
- def set_soap_endpoint_to_file url
68
- data_hash = {
69
- 'url' => url,
70
- 'timestamp' => Time.new.to_f + (10 * 60)
71
- }
72
-
73
- File.open(cache_file, 'w') do |f|
74
- f.write(JSON.generate(data_hash))
75
- end
76
- end
77
-
78
- protected
79
- def get_soap_endpoint
80
- if self.soap_endpoint
81
- @endpoint = self.soap_endpoint
82
- return
83
- end
84
-
85
- cache_data = get_soap_endpoint_from_file
86
-
87
- if cache_data.nil? === false and not cache_data['url'].nil? and cache_data['timestamp'].to_f > Time.new.to_f
88
- @endpoint = cache_data['url']
89
- return
90
- end
91
-
92
- options = {'access_token' => self.access_token}
93
- response = get(self.base_api_url + "/platform/v1/endpoints/soap", options)
94
- @endpoint = response['url']
95
- set_soap_endpoint_to_file @endpoint
96
- rescue => e
97
- @endpoint = 'https://webservice.exacttarget.com/Service.asmx'
98
- end
99
- end
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 'json'
38
+
39
+ module MarketingCloudSDK::Targeting
40
+ attr_accessor :access_token
41
+ attr_reader :endpoint, :soap_endpoint
42
+
43
+ include MarketingCloudSDK::HTTPRequest
44
+
45
+ def cache_file
46
+ 'soap_cache_file.json'
47
+ end
48
+
49
+ def endpoint
50
+ unless @endpoint
51
+ get_soap_endpoint
52
+ end
53
+ @endpoint
54
+ end
55
+
56
+ def get_soap_endpoint_from_file
57
+ data_hash = nil
58
+
59
+ if File.exist? cache_file
60
+ file = File.read(cache_file)
61
+ data_hash = JSON.parse(file)
62
+ end
63
+
64
+ data_hash
65
+ end
66
+
67
+ def set_soap_endpoint_to_file url
68
+ data_hash = {
69
+ 'url' => url,
70
+ 'timestamp' => Time.new.to_f + (10 * 60)
71
+ }
72
+
73
+ File.open(cache_file, 'w') do |f|
74
+ f.write(JSON.generate(data_hash))
75
+ end
76
+ end
77
+
78
+ protected
79
+ def get_soap_endpoint
80
+ if self.soap_endpoint
81
+ @endpoint = self.soap_endpoint
82
+ return
83
+ end
84
+
85
+ cache_data = get_soap_endpoint_from_file
86
+
87
+ if cache_data.nil? === false and not cache_data['url'].nil? and cache_data['timestamp'].to_f > Time.new.to_f
88
+ @endpoint = cache_data['url']
89
+ return
90
+ end
91
+
92
+ options = {'access_token' => self.access_token}
93
+ response = get(self.base_api_url + "/platform/v1/endpoints/soap", options)
94
+ @endpoint = response['url']
95
+ set_soap_endpoint_to_file @endpoint
96
+ rescue => e
97
+ @endpoint = 'https://webservice.exacttarget.com/Service.asmx'
98
+ end
99
+ end
@@ -1,47 +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 MarketingCloudSDK
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
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_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
@@ -1,39 +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 MarketingCloudSDK
38
- VERSION = "1.1.0"
39
- end
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
+ VERSION = "1.2.0"
39
+ end
data/lib/new.rb CHANGED
@@ -1,1240 +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 "marketingcloudsdk/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 MarketingCloudSDK
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, :attributes
1180
- def initialize
1181
- super
1182
- @obj = 'TriggeredSendDefinition'
1183
- end
1184
-
1185
- def send
1186
- @tscall = {"TriggeredSendDefinition" => @props, "Subscribers" => @subscribers, "Attributes" => @attributes}
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
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 "marketingcloudsdk/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 MarketingCloudSDK
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, :attributes
1180
+ def initialize
1181
+ super
1182
+ @obj = 'TriggeredSendDefinition'
1183
+ end
1184
+
1185
+ def send
1186
+ @tscall = {"TriggeredSendDefinition" => @props, "Subscribers" => @subscribers, "Attributes" => @attributes}
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