sfmc-fuelsdk-ruby 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
data/Rakefile CHANGED
@@ -1 +1 @@
1
- require "bundler/gem_tasks"
1
+ require "bundler/gem_tasks"
@@ -1,74 +1,74 @@
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 'date'
41
- require 'jwt'
42
-
43
- module MarketingCloudSDK
44
- require 'marketingcloudsdk/utils'
45
- autoload :HTTPRequest, 'marketingcloudsdk/http_request'
46
- autoload :Targeting, 'marketingcloudsdk/targeting'
47
- autoload :Soap, 'marketingcloudsdk/soap'
48
- autoload :Rest, 'marketingcloudsdk/rest'
49
- require 'marketingcloudsdk/client'
50
- require 'marketingcloudsdk/objects'
51
- end
52
-
53
- # backwards compatability
54
- ET_Client = MarketingCloudSDK::Client
55
- ET_BounceEvent = MarketingCloudSDK::BounceEvent
56
- ET_ClickEvent = MarketingCloudSDK::ClickEvent
57
- ET_ContentArea = MarketingCloudSDK::ContentArea
58
- ET_DataExtension = MarketingCloudSDK::DataExtension
59
- ET_DataFolder = MarketingCloudSDK::DataFolder
60
- ET_Folder = MarketingCloudSDK::Folder
61
- ET_Email = MarketingCloudSDK::Email
62
- ET_List = MarketingCloudSDK::List
63
- ET_OpenEvent = MarketingCloudSDK::OpenEvent
64
- ET_SentEvent = MarketingCloudSDK::SentEvent
65
- ET_Subscriber = MarketingCloudSDK::Subscriber
66
- ET_UnsubEvent = MarketingCloudSDK::UnsubEvent
67
- ET_TriggeredSend = MarketingCloudSDK::TriggeredSend
68
- ET_Campaign = MarketingCloudSDK::Campaign
69
- ET_Get = MarketingCloudSDK::Get
70
- ET_Post = MarketingCloudSDK::Post
71
- ET_Delete = MarketingCloudSDK::Delete
72
- ET_Patch = MarketingCloudSDK::Patch
73
- ET_ProfileAttribute = MarketingCloudSDK::ProfileAttribute
74
- ET_Import = MarketingCloudSDK::Import
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 'date'
41
+ require 'jwt'
42
+
43
+ module MarketingCloudSDK
44
+ require 'marketingcloudsdk/utils'
45
+ autoload :HTTPRequest, 'marketingcloudsdk/http_request'
46
+ autoload :Targeting, 'marketingcloudsdk/targeting'
47
+ autoload :Soap, 'marketingcloudsdk/soap'
48
+ autoload :Rest, 'marketingcloudsdk/rest'
49
+ require 'marketingcloudsdk/client'
50
+ require 'marketingcloudsdk/objects'
51
+ end
52
+
53
+ # backwards compatability
54
+ ET_Client = MarketingCloudSDK::Client
55
+ ET_BounceEvent = MarketingCloudSDK::BounceEvent
56
+ ET_ClickEvent = MarketingCloudSDK::ClickEvent
57
+ ET_ContentArea = MarketingCloudSDK::ContentArea
58
+ ET_DataExtension = MarketingCloudSDK::DataExtension
59
+ ET_DataFolder = MarketingCloudSDK::DataFolder
60
+ ET_Folder = MarketingCloudSDK::Folder
61
+ ET_Email = MarketingCloudSDK::Email
62
+ ET_List = MarketingCloudSDK::List
63
+ ET_OpenEvent = MarketingCloudSDK::OpenEvent
64
+ ET_SentEvent = MarketingCloudSDK::SentEvent
65
+ ET_Subscriber = MarketingCloudSDK::Subscriber
66
+ ET_UnsubEvent = MarketingCloudSDK::UnsubEvent
67
+ ET_TriggeredSend = MarketingCloudSDK::TriggeredSend
68
+ ET_Campaign = MarketingCloudSDK::Campaign
69
+ ET_Get = MarketingCloudSDK::Get
70
+ ET_Post = MarketingCloudSDK::Post
71
+ ET_Delete = MarketingCloudSDK::Delete
72
+ ET_Patch = MarketingCloudSDK::Patch
73
+ ET_ProfileAttribute = MarketingCloudSDK::ProfileAttribute
74
+ ET_Import = MarketingCloudSDK::Import
@@ -1,296 +1,348 @@
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
-
36
- =end
37
-
38
- require 'securerandom'
39
- module MarketingCloudSDK
40
- class Response
41
- # not doing accessor so user, can't update these values from response.
42
- # You will see in the code some of these
43
- # items are being updated via back doors and such.
44
- attr_reader :code, :message, :results, :request_id, :body, :raw
45
-
46
- # some defaults
47
- def success
48
- @success ||= false
49
- end
50
- alias :success? :success
51
- alias :status :success # backward compatibility
52
-
53
- def more
54
- @more ||= false
55
- end
56
- alias :more? :more
57
-
58
- def initialize raw, client
59
- @client = client # keep connection with client in case we request more
60
- @results = []
61
- @raw = raw
62
- unpack raw
63
- rescue => ex # all else fails return raw
64
- puts ex.message
65
- raw
66
- end
67
-
68
- def continue
69
- raise NotImplementedError
70
- end
71
-
72
- private
73
- def unpack raw
74
- raise NotImplementedError
75
- end
76
- end
77
-
78
- class Client
79
- attr_accessor :debug, :access_token, :auth_token, :internal_token, :refresh_token,
80
- :id, :secret, :signature, :base_api_url, :package_name, :package_folders, :parent_folders, :auth_token_expiration,
81
- :request_token_url, :soap_endpoint
82
-
83
- include MarketingCloudSDK::Soap
84
- include MarketingCloudSDK::Rest
85
-
86
- def jwt= encoded_jwt
87
- raise 'Require app signature to decode JWT' unless self.signature
88
- decoded_jwt = JWT.decode(encoded_jwt, self.signature, true)
89
-
90
- self.auth_token = decoded_jwt['request']['user']['oauthToken']
91
- self.internal_token = decoded_jwt['request']['user']['internalOauthToken']
92
- self.refresh_token = decoded_jwt['request']['user']['refreshToken']
93
- self.auth_token_expiration = Time.new + decoded_jwt['request']['user']['expiresIn']
94
- self.package_name = decoded_jwt['request']['application']['package']
95
- end
96
-
97
- def initialize(params={}, debug=false)
98
- @refresh_mutex = Mutex.new
99
- self.debug = debug
100
- client_config = params['client']
101
- if client_config
102
- self.id = client_config["id"]
103
- self.secret = client_config["secret"]
104
- self.signature = client_config["signature"]
105
- self.base_api_url = !(client_config["base_api_url"].to_s.strip.empty?) ? client_config["base_api_url"] : 'https://www.exacttargetapis.com'
106
- self.request_token_url = !(client_config["request_token_url"].to_s.strip.empty?) ? client_config["request_token_url"] : 'https://auth.exacttargetapis.com/v1/requestToken'
107
- self.soap_endpoint = client_config["soap_endpoint"]
108
- end
109
-
110
- # Set a default value in case no 'client' params is sent
111
- if (!self.base_api_url)
112
- self.base_api_url = 'https://www.exacttargetapis.com'
113
- end
114
-
115
- # Leaving this for backwards compatibility
116
- if (!self.request_token_url)
117
- self.request_token_url = params['request_token_url'] ? params['request_token_url'] : 'https://auth.exacttargetapis.com/v1/requestToken'
118
- end
119
-
120
- self.jwt = params['jwt'] if params['jwt']
121
- self.refresh_token = params['refresh_token'] if params['refresh_token']
122
-
123
- self.wsdl = params["defaultwsdl"] if params["defaultwsdl"]
124
- end
125
-
126
- def refresh force=false
127
- @refresh_mutex.synchronize do
128
- raise 'Require Client Id and Client Secret to refresh tokens' unless (id && secret)
129
- #If we don't already have a token or the token expires within 5 min(300 seconds)
130
- if (self.access_token.nil? || Time.new + 300 > self.auth_token_expiration || force) then
131
- payload = Hash.new.tap do |h|
132
- h['clientId']= id
133
- h['clientSecret'] = secret
134
- h['refreshToken'] = refresh_token if refresh_token
135
- h['accessType'] = 'offline'
136
- end
137
-
138
- options = Hash.new.tap do |h|
139
- h['data'] = payload
140
- h['content_type'] = 'application/json'
141
- h['params'] = {'legacy' => 1}
142
- end
143
- response = post(request_token_url, options)
144
- raise "Unable to refresh token: #{response['message']}" unless response.has_key?('accessToken')
145
-
146
- self.access_token = response['accessToken']
147
- self.internal_token = response['legacyToken']
148
- self.auth_token_expiration = Time.new + response['expiresIn']
149
- self.refresh_token = response['refreshToken'] if response.has_key?("refreshToken")
150
- return true
151
- else
152
- return false
153
- end
154
- end
155
- end
156
-
157
- def refresh!
158
- refresh true
159
- end
160
-
161
- def AddSubscriberToList(email, ids, subscriber_key = nil)
162
- s = MarketingCloudSDK::Subscriber.new
163
- s.client = self
164
- lists = ids.collect{|id| {'ID' => id}}
165
- s.properties = {"EmailAddress" => email, "Lists" => lists}
166
- p s.properties
167
- s.properties['SubscriberKey'] = subscriber_key if subscriber_key
168
-
169
- # Try to add the subscriber
170
- if(rsp = s.post and rsp.results.first[:error_code] == '12014')
171
- # subscriber already exists we need to update.
172
- rsp = s.patch
173
- end
174
- rsp
175
- end
176
-
177
- def CreateDataExtensions(definitions)
178
- de = MarketingCloudSDK::DataExtension.new
179
- de.client = self
180
- de.properties = definitions
181
- de.post
182
- end
183
- def SendTriggeredSends(arrayOfTriggeredRecords)
184
- sendTS = ET_TriggeredSend.new
185
- sendTS.authStub = self
186
-
187
- sendTS.properties = arrayOfTriggeredRecords
188
- sendResponse = sendTS.send
189
-
190
- return sendResponse
191
- end
192
- def SendEmailToList(emailID, listID, sendClassificationCustomerKey)
193
- email = ET_Email::SendDefinition.new
194
- email.properties = {"Name"=>SecureRandom.uuid, "CustomerKey"=>SecureRandom.uuid, "Description"=>"Created with RubySDK"}
195
- email.properties["SendClassification"] = {"CustomerKey"=>sendClassificationCustomerKey}
196
- email.properties["SendDefinitionList"] = {"List"=> {"ID"=>listID}, "DataSourceTypeID"=>"List"}
197
- email.properties["Email"] = {"ID"=>emailID}
198
- email.authStub = self
199
- result = email.post
200
- if result.status then
201
- sendresult = email.send
202
- if sendresult.status then
203
- deleteresult = email.delete
204
- return sendresult
205
- else
206
- raise "Unable to send using send definition due to: #{result.results[0][:status_message]}"
207
- end
208
- else
209
- raise "Unable to create send definition due to: #{result.results[0][:status_message]}"
210
- end
211
- end
212
-
213
- def SendEmailToDataExtension(emailID, sendableDataExtensionCustomerKey, sendClassificationCustomerKey)
214
- email = ET_Email::SendDefinition.new
215
- email.properties = {"Name"=>SecureRandom.uuid, "CustomerKey"=>SecureRandom.uuid, "Description"=>"Created with RubySDK"}
216
- email.properties["SendClassification"] = {"CustomerKey"=> sendClassificationCustomerKey}
217
- email.properties["SendDefinitionList"] = {"CustomerKey"=> sendableDataExtensionCustomerKey, "DataSourceTypeID"=>"CustomObject"}
218
- email.properties["Email"] = {"ID"=>emailID}
219
- email.authStub = self
220
- result = email.post
221
- if result.status then
222
- sendresult = email.send
223
- if sendresult.status then
224
- deleteresult = email.delete
225
- return sendresult
226
- else
227
- raise "Unable to send using send definition due to: #{result.results[0][:status_message]}"
228
- end
229
- else
230
- raise "Unable to create send definition due to: #{result.results[0][:status_message]}"
231
- end
232
- end
233
- def CreateAndStartListImport(listId,fileName)
234
- import = ET_Import.new
235
- import.authStub = self
236
- import.properties = {"Name"=> "SDK Generated Import #{DateTime.now.to_s}"}
237
- import.properties["CustomerKey"] = SecureRandom.uuid
238
- import.properties["Description"] = "SDK Generated Import"
239
- import.properties["AllowErrors"] = "true"
240
- import.properties["DestinationObject"] = {"ID"=>listId}
241
- import.properties["FieldMappingType"] = "InferFromColumnHeadings"
242
- import.properties["FileSpec"] = fileName
243
- import.properties["FileType"] = "CSV"
244
- import.properties["RetrieveFileTransferLocation"] = {"CustomerKey"=>"ExactTarget Enhanced FTP"}
245
- import.properties["UpdateType"] = "AddAndUpdate"
246
- result = import.post
247
-
248
- if result.status then
249
- return import.start
250
- else
251
- raise "Unable to create import definition due to: #{result.results[0][:status_message]}"
252
- end
253
- end
254
-
255
- def CreateAndStartDataExtensionImport(dataExtensionCustomerKey, fileName, overwrite)
256
- import = ET_Import.new
257
- import.authStub = self
258
- import.properties = {"Name"=> "SDK Generated Import #{DateTime.now.to_s}"}
259
- import.properties["CustomerKey"] = SecureRandom.uuid
260
- import.properties["Description"] = "SDK Generated Import"
261
- import.properties["AllowErrors"] = "true"
262
- import.properties["DestinationObject"] = {"CustomerKey"=>dataExtensionCustomerKey}
263
- import.properties["FieldMappingType"] = "InferFromColumnHeadings"
264
- import.properties["FileSpec"] = fileName
265
- import.properties["FileType"] = "CSV"
266
- import.properties["RetrieveFileTransferLocation"] = {"CustomerKey"=>"ExactTarget Enhanced FTP"}
267
- if overwrite then
268
- import.properties["UpdateType"] = "Overwrite"
269
- else
270
- import.properties["UpdateType"] = "AddAndUpdate"
271
- end
272
- result = import.post
273
-
274
- if result.status then
275
- return import.start
276
- else
277
- raise "Unable to create import definition due to: #{result.results[0][:status_message]}"
278
- end
279
- end
280
-
281
- def CreateProfileAttributes(allAttributes)
282
- attrs = ET_ProfileAttribute.new
283
- attrs.authStub = self
284
- attrs.properties = allAttributes
285
- return attrs.post
286
- end
287
-
288
- def CreateContentAreas(arrayOfContentAreas)
289
- postC = ET_ContentArea.new
290
- postC.authStub = self
291
- postC.properties = arrayOfContentAreas
292
- sendResponse = postC.post
293
- return sendResponse
294
- end
295
- end
296
- 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
+
36
+ =end
37
+
38
+ require 'securerandom'
39
+ module MarketingCloudSDK
40
+ class Response
41
+ # not doing accessor so user, can't update these values from response.
42
+ # You will see in the code some of these
43
+ # items are being updated via back doors and such.
44
+ attr_reader :code, :message, :results, :request_id, :body, :raw
45
+
46
+ # some defaults
47
+ def success
48
+ @success ||= false
49
+ end
50
+ alias :success? :success
51
+ alias :status :success # backward compatibility
52
+
53
+ def more
54
+ @more ||= false
55
+ end
56
+ alias :more? :more
57
+
58
+ def initialize raw, client
59
+ @client = client # keep connection with client in case we request more
60
+ @results = []
61
+ @raw = raw
62
+ unpack raw
63
+ rescue => ex # all else fails return raw
64
+ puts ex.message
65
+ raw
66
+ end
67
+
68
+ def continue
69
+ raise NotImplementedError
70
+ end
71
+
72
+ private
73
+ def unpack raw
74
+ raise NotImplementedError
75
+ end
76
+ end
77
+
78
+ class Client
79
+ attr_accessor :debug, :access_token, :auth_token, :internal_token, :refresh_token,
80
+ :id, :secret, :signature, :base_api_url, :package_name, :package_folders, :parent_folders, :auth_token_expiration,
81
+ :request_token_url, :soap_endpoint, :use_oAuth2_authentication, :account_id, :scope
82
+
83
+ include MarketingCloudSDK::Soap
84
+ include MarketingCloudSDK::Rest
85
+
86
+ def jwt= encoded_jwt
87
+ raise 'Require app signature to decode JWT' unless self.signature
88
+ decoded_jwt = JWT.decode(encoded_jwt, self.signature, true)
89
+
90
+ self.auth_token = decoded_jwt['request']['user']['oauthToken']
91
+ self.internal_token = decoded_jwt['request']['user']['internalOauthToken']
92
+ self.refresh_token = decoded_jwt['request']['user']['refreshToken']
93
+ self.auth_token_expiration = Time.new + decoded_jwt['request']['user']['expiresIn']
94
+ self.package_name = decoded_jwt['request']['application']['package']
95
+ end
96
+
97
+ def initialize(params={}, debug=false)
98
+ @refresh_mutex = Mutex.new
99
+ self.debug = debug
100
+ client_config = params['client']
101
+ if client_config
102
+ self.id = client_config["id"]
103
+ self.secret = client_config["secret"]
104
+ self.signature = client_config["signature"]
105
+ self.base_api_url = !(client_config["base_api_url"].to_s.strip.empty?) ? client_config["base_api_url"] : 'https://www.exacttargetapis.com'
106
+ self.request_token_url = client_config["request_token_url"]
107
+ self.soap_endpoint = client_config["soap_endpoint"]
108
+ self.use_oAuth2_authentication = client_config["use_oAuth2_authentication"]
109
+ self.account_id = client_config["account_id"]
110
+ self.scope = client_config["scope"]
111
+ end
112
+
113
+ # Set a default value in case no 'client' params is sent
114
+ if (!self.base_api_url)
115
+ self.base_api_url = 'https://www.exacttargetapis.com'
116
+ end
117
+
118
+ if (self.request_token_url.to_s.strip.empty?)
119
+ if(use_oAuth2_authentication == true)
120
+ raise 'request_token_url (Auth TSE) is mandatory when using OAuth2 authentication'
121
+ else
122
+ self.request_token_url = 'https://auth.exacttargetapis.com/v1/requestToken'
123
+ end
124
+ end
125
+
126
+ self.jwt = params['jwt'] if params['jwt']
127
+ self.refresh_token = params['refresh_token'] if params['refresh_token']
128
+
129
+ self.wsdl = params["defaultwsdl"] if params["defaultwsdl"]
130
+
131
+ self.refresh
132
+ end
133
+
134
+ def refresh force=false
135
+ @refresh_mutex.synchronize do
136
+ raise 'Require Client Id and Client Secret to refresh tokens' unless (id && secret)
137
+
138
+ if (self.use_oAuth2_authentication == true)
139
+ self.refreshWithOAuth2(force)
140
+ return
141
+ end
142
+
143
+ #If we don't already have a token or the token expires within 5 min(300 seconds)
144
+ if (self.access_token.nil? || Time.new + 300 > self.auth_token_expiration || force) then
145
+ payload = Hash.new.tap do |h|
146
+ h['clientId']= id
147
+ h['clientSecret'] = secret
148
+ h['refreshToken'] = refresh_token if refresh_token
149
+ h['accessType'] = 'offline'
150
+ end
151
+
152
+ options = Hash.new.tap do |h|
153
+ h['data'] = payload
154
+ h['content_type'] = 'application/json'
155
+ h['params'] = {'legacy' => 1}
156
+ end
157
+ response = post(request_token_url, options)
158
+ raise "Unable to refresh token: #{response['message']}" unless response.has_key?('accessToken')
159
+
160
+ self.access_token = response['accessToken']
161
+ self.internal_token = response['legacyToken']
162
+ self.auth_token_expiration = Time.new + response['expiresIn']
163
+ self.refresh_token = response['refreshToken'] if response.has_key?("refreshToken")
164
+ return true
165
+ else
166
+ return false
167
+ end
168
+ end
169
+ end
170
+
171
+ def refreshWithOAuth2 force=false
172
+ raise 'Require Client Id and Client Secret to refresh tokens' unless (id && secret)
173
+ #If we don't already have a token or the token expires within 5 min(300 seconds)
174
+ if (self.access_token.nil? || Time.new + 300 > self.auth_token_expiration || force) then
175
+ payload = Hash.new.tap do |h|
176
+ h['client_id']= id
177
+ h['client_secret'] = secret
178
+ h['grant_type'] = 'client_credentials'
179
+
180
+ if (not self.account_id.to_s.strip.empty?)then
181
+ h['account_id'] = account_id
182
+ end
183
+
184
+ if (not self.scope.to_s.strip.empty?)then
185
+ h['scope'] = scope
186
+ end
187
+ end
188
+
189
+ options = Hash.new.tap do |h|
190
+ h['data'] = payload
191
+ h['content_type'] = 'application/json'
192
+ end
193
+
194
+ self.request_token_url += '/v2/token'
195
+
196
+ response = post(request_token_url, options)
197
+ raise "Unable to refresh token: #{response['message']}" unless response.has_key?('access_token')
198
+
199
+ self.access_token = response['access_token']
200
+ self.auth_token_expiration = Time.new + response['expires_in']
201
+ self.soap_endpoint = response['soap_instance_url'] + 'service.asmx'
202
+ self.base_api_url = response['rest_instance_url']
203
+ return true
204
+ else
205
+ return false
206
+ end
207
+ end
208
+
209
+ def refresh!
210
+ refresh true
211
+ end
212
+
213
+ def AddSubscriberToList(email, ids, subscriber_key = nil)
214
+ s = MarketingCloudSDK::Subscriber.new
215
+ s.client = self
216
+ lists = ids.collect{|id| {'ID' => id}}
217
+ s.properties = {"EmailAddress" => email, "Lists" => lists}
218
+ p s.properties
219
+ s.properties['SubscriberKey'] = subscriber_key if subscriber_key
220
+
221
+ # Try to add the subscriber
222
+ if(rsp = s.post and rsp.results.first[:error_code] == '12014')
223
+ # subscriber already exists we need to update.
224
+ rsp = s.patch
225
+ end
226
+ rsp
227
+ end
228
+
229
+ def CreateDataExtensions(definitions)
230
+ de = MarketingCloudSDK::DataExtension.new
231
+ de.client = self
232
+ de.properties = definitions
233
+ de.post
234
+ end
235
+ def SendTriggeredSends(arrayOfTriggeredRecords)
236
+ sendTS = ET_TriggeredSend.new
237
+ sendTS.authStub = self
238
+
239
+ sendTS.properties = arrayOfTriggeredRecords
240
+ sendResponse = sendTS.send
241
+
242
+ return sendResponse
243
+ end
244
+ def SendEmailToList(emailID, listID, sendClassificationCustomerKey)
245
+ email = ET_Email::SendDefinition.new
246
+ email.properties = {"Name"=>SecureRandom.uuid, "CustomerKey"=>SecureRandom.uuid, "Description"=>"Created with RubySDK"}
247
+ email.properties["SendClassification"] = {"CustomerKey"=>sendClassificationCustomerKey}
248
+ email.properties["SendDefinitionList"] = {"List"=> {"ID"=>listID}, "DataSourceTypeID"=>"List"}
249
+ email.properties["Email"] = {"ID"=>emailID}
250
+ email.authStub = self
251
+ result = email.post
252
+ if result.status then
253
+ sendresult = email.send
254
+ if sendresult.status then
255
+ deleteresult = email.delete
256
+ return sendresult
257
+ else
258
+ raise "Unable to send using send definition due to: #{result.results[0][:status_message]}"
259
+ end
260
+ else
261
+ raise "Unable to create send definition due to: #{result.results[0][:status_message]}"
262
+ end
263
+ end
264
+
265
+ def SendEmailToDataExtension(emailID, sendableDataExtensionCustomerKey, sendClassificationCustomerKey)
266
+ email = ET_Email::SendDefinition.new
267
+ email.properties = {"Name"=>SecureRandom.uuid, "CustomerKey"=>SecureRandom.uuid, "Description"=>"Created with RubySDK"}
268
+ email.properties["SendClassification"] = {"CustomerKey"=> sendClassificationCustomerKey}
269
+ email.properties["SendDefinitionList"] = {"CustomerKey"=> sendableDataExtensionCustomerKey, "DataSourceTypeID"=>"CustomObject"}
270
+ email.properties["Email"] = {"ID"=>emailID}
271
+ email.authStub = self
272
+ result = email.post
273
+ if result.status then
274
+ sendresult = email.send
275
+ if sendresult.status then
276
+ deleteresult = email.delete
277
+ return sendresult
278
+ else
279
+ raise "Unable to send using send definition due to: #{result.results[0][:status_message]}"
280
+ end
281
+ else
282
+ raise "Unable to create send definition due to: #{result.results[0][:status_message]}"
283
+ end
284
+ end
285
+ def CreateAndStartListImport(listId,fileName)
286
+ import = ET_Import.new
287
+ import.authStub = self
288
+ import.properties = {"Name"=> "SDK Generated Import #{DateTime.now.to_s}"}
289
+ import.properties["CustomerKey"] = SecureRandom.uuid
290
+ import.properties["Description"] = "SDK Generated Import"
291
+ import.properties["AllowErrors"] = "true"
292
+ import.properties["DestinationObject"] = {"ID"=>listId}
293
+ import.properties["FieldMappingType"] = "InferFromColumnHeadings"
294
+ import.properties["FileSpec"] = fileName
295
+ import.properties["FileType"] = "CSV"
296
+ import.properties["RetrieveFileTransferLocation"] = {"CustomerKey"=>"ExactTarget Enhanced FTP"}
297
+ import.properties["UpdateType"] = "AddAndUpdate"
298
+ result = import.post
299
+
300
+ if result.status then
301
+ return import.start
302
+ else
303
+ raise "Unable to create import definition due to: #{result.results[0][:status_message]}"
304
+ end
305
+ end
306
+
307
+ def CreateAndStartDataExtensionImport(dataExtensionCustomerKey, fileName, overwrite)
308
+ import = ET_Import.new
309
+ import.authStub = self
310
+ import.properties = {"Name"=> "SDK Generated Import #{DateTime.now.to_s}"}
311
+ import.properties["CustomerKey"] = SecureRandom.uuid
312
+ import.properties["Description"] = "SDK Generated Import"
313
+ import.properties["AllowErrors"] = "true"
314
+ import.properties["DestinationObject"] = {"CustomerKey"=>dataExtensionCustomerKey}
315
+ import.properties["FieldMappingType"] = "InferFromColumnHeadings"
316
+ import.properties["FileSpec"] = fileName
317
+ import.properties["FileType"] = "CSV"
318
+ import.properties["RetrieveFileTransferLocation"] = {"CustomerKey"=>"ExactTarget Enhanced FTP"}
319
+ if overwrite then
320
+ import.properties["UpdateType"] = "Overwrite"
321
+ else
322
+ import.properties["UpdateType"] = "AddAndUpdate"
323
+ end
324
+ result = import.post
325
+
326
+ if result.status then
327
+ return import.start
328
+ else
329
+ raise "Unable to create import definition due to: #{result.results[0][:status_message]}"
330
+ end
331
+ end
332
+
333
+ def CreateProfileAttributes(allAttributes)
334
+ attrs = ET_ProfileAttribute.new
335
+ attrs.authStub = self
336
+ attrs.properties = allAttributes
337
+ return attrs.post
338
+ end
339
+
340
+ def CreateContentAreas(arrayOfContentAreas)
341
+ postC = ET_ContentArea.new
342
+ postC.authStub = self
343
+ postC.properties = arrayOfContentAreas
344
+ sendResponse = postC.post
345
+ return sendResponse
346
+ end
347
+ end
348
+ end