sfmc-fuelsdk-ruby 1.3.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +39 -39
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -20
  4. data/.gitignore +29 -29
  5. data/Gemfile +3 -3
  6. data/Gemfile.lock +104 -92
  7. data/Guardfile +8 -8
  8. data/LICENSE.md +13 -13
  9. data/README.md +200 -194
  10. data/Rakefile +1 -1
  11. data/lib/marketingcloudsdk.rb +74 -74
  12. data/lib/marketingcloudsdk/client.rb +395 -395
  13. data/lib/marketingcloudsdk/http_request.rb +118 -118
  14. data/lib/marketingcloudsdk/objects.rb +757 -757
  15. data/lib/marketingcloudsdk/rest.rb +118 -118
  16. data/lib/marketingcloudsdk/soap.rb +296 -296
  17. data/lib/marketingcloudsdk/targeting.rb +99 -99
  18. data/lib/marketingcloudsdk/utils.rb +47 -47
  19. data/lib/marketingcloudsdk/version.rb +39 -39
  20. data/lib/new.rb +1240 -1240
  21. data/marketingcloudsdk.gemspec +30 -30
  22. data/samples/sample-AddSubscriberToList.rb +56 -56
  23. data/samples/sample-CreateAndStartDataExtensionImport.rb +29 -29
  24. data/samples/sample-CreateAndStartListImport.rb +27 -27
  25. data/samples/sample-CreateContentAreas.rb +48 -48
  26. data/samples/sample-CreateDataExtensions.rb +54 -54
  27. data/samples/sample-CreateProfileAttributes.rb +48 -48
  28. data/samples/sample-SendEmailToDataExtension.rb +23 -23
  29. data/samples/sample-SendEmailToList.rb +23 -23
  30. data/samples/sample-SendTriggeredSends.rb +30 -30
  31. data/samples/sample-bounceevent.rb +70 -70
  32. data/samples/sample-campaign.rb +211 -211
  33. data/samples/sample-clickevent.rb +71 -71
  34. data/samples/sample-contentarea.rb +122 -122
  35. data/samples/sample-dataextension.rb +209 -209
  36. data/samples/sample-directverb.rb +54 -54
  37. data/samples/sample-email.rb +122 -122
  38. data/samples/sample-email.senddefinition.rb +134 -134
  39. data/samples/sample-folder.rb +143 -143
  40. data/samples/sample-import.rb +103 -103
  41. data/samples/sample-list.rb +105 -105
  42. data/samples/sample-list.subscriber.rb +97 -97
  43. data/samples/sample-openevent.rb +70 -70
  44. data/samples/sample-profileattribute.rb +56 -56
  45. data/samples/sample-sentevent.rb +70 -70
  46. data/samples/sample-subscriber.rb +135 -135
  47. data/samples/sample-triggeredsend.rb +129 -129
  48. data/samples/sample-unsubevent.rb +72 -72
  49. data/samples/sample_helper.rb.template +10 -10
  50. data/spec/client_spec.rb +416 -416
  51. data/spec/default_values_fallback_spec.rb +30 -30
  52. data/spec/helper_funcs_spec.rb +11 -11
  53. data/spec/http_request_spec.rb +61 -61
  54. data/spec/objects_helper_spec.rb +32 -32
  55. data/spec/objects_spec.rb +484 -484
  56. data/spec/public_or_web_integration_credentials.rb.template +11 -11
  57. data/spec/rest_spec.rb +48 -48
  58. data/spec/soap_spec.rb +140 -140
  59. data/spec/spec_helper.rb +14 -14
  60. data/spec/targeting_spec.rb +44 -44
  61. metadata +14 -27
@@ -1,118 +1,118 @@
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 Rest
39
-
40
- include MarketingCloudSDK::Targeting
41
-
42
- def rest_client
43
- self
44
- end
45
-
46
- def normalize_keys obj
47
- if obj and obj.is_a? Hash
48
- obj.keys.each do |k|
49
- obj[(k.to_sym rescue k) || k] = obj.delete(k)
50
- end
51
- end
52
- obj
53
- end
54
-
55
- def get_url_properties url, properties
56
- url_property_names = url.scan(/(%{(.+?)})/).collect{|frmt, name| name}
57
- url_properties = {}
58
- properties.keys.each do |k|
59
- if url_property_names.include? k
60
- url_properties[k] = properties.delete(k)
61
- end
62
- end
63
- url_properties
64
- end
65
-
66
- def complete_url url, url_properties
67
- normalize_keys(url_properties)
68
- url = url % url_properties if url_properties
69
- url.end_with?('/') ? url.chop : url
70
- rescue KeyError => ex
71
- raise "#{ex.message} to complete #{url}"
72
- end
73
-
74
- def parse_properties url, properties
75
- url_properties = get_url_properties url, properties
76
- url = complete_url url, url_properties
77
- [url, properties]
78
- end
79
-
80
- def rest_get url, properties={}
81
- url, properties = parse_properties url, properties
82
- rest_request :get, url, {'params' => properties}
83
- end
84
-
85
- def rest_delete url, properties={}
86
- url, properties = parse_properties url, properties
87
- rest_request :delete, url
88
- end
89
-
90
- def rest_patch url, properties={}
91
- url, payload = parse_properties url, properties
92
- rest_request :patch, url, {'data' => payload,
93
- 'content_type' => 'application/json'}
94
- end
95
-
96
- def rest_post url, properties={}
97
- url, payload = parse_properties url, properties
98
- rest_request :post, url, {'data' => payload,
99
- 'content_type' => 'application/json'}
100
- end
101
-
102
- private
103
- def rest_request action, url, options={}
104
- #Try to refresh the token and if we do then we need to regenerate the header as well.
105
- self.refresh
106
- (options['params'] ||= {})
107
-
108
- if access_token
109
- options['access_token'] = access_token
110
- end
111
-
112
- rsp = rest_client.send(action, url, options)
113
- raise 'Unauthorized' if rsp.message == 'Unauthorized'
114
-
115
- rsp
116
- end
117
- end
118
- 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 Rest
39
+
40
+ include MarketingCloudSDK::Targeting
41
+
42
+ def rest_client
43
+ self
44
+ end
45
+
46
+ def normalize_keys obj
47
+ if obj and obj.is_a? Hash
48
+ obj.keys.each do |k|
49
+ obj[(k.to_sym rescue k) || k] = obj.delete(k)
50
+ end
51
+ end
52
+ obj
53
+ end
54
+
55
+ def get_url_properties url, properties
56
+ url_property_names = url.scan(/(%{(.+?)})/).collect{|frmt, name| name}
57
+ url_properties = {}
58
+ properties.keys.each do |k|
59
+ if url_property_names.include? k
60
+ url_properties[k] = properties.delete(k)
61
+ end
62
+ end
63
+ url_properties
64
+ end
65
+
66
+ def complete_url url, url_properties
67
+ normalize_keys(url_properties)
68
+ url = url % url_properties if url_properties
69
+ url.end_with?('/') ? url.chop : url
70
+ rescue KeyError => ex
71
+ raise "#{ex.message} to complete #{url}"
72
+ end
73
+
74
+ def parse_properties url, properties
75
+ url_properties = get_url_properties url, properties
76
+ url = complete_url url, url_properties
77
+ [url, properties]
78
+ end
79
+
80
+ def rest_get url, properties={}
81
+ url, properties = parse_properties url, properties
82
+ rest_request :get, url, {'params' => properties}
83
+ end
84
+
85
+ def rest_delete url, properties={}
86
+ url, properties = parse_properties url, properties
87
+ rest_request :delete, url
88
+ end
89
+
90
+ def rest_patch url, properties={}
91
+ url, payload = parse_properties url, properties
92
+ rest_request :patch, url, {'data' => payload,
93
+ 'content_type' => 'application/json'}
94
+ end
95
+
96
+ def rest_post url, properties={}
97
+ url, payload = parse_properties url, properties
98
+ rest_request :post, url, {'data' => payload,
99
+ 'content_type' => 'application/json'}
100
+ end
101
+
102
+ private
103
+ def rest_request action, url, options={}
104
+ #Try to refresh the token and if we do then we need to regenerate the header as well.
105
+ self.refresh
106
+ (options['params'] ||= {})
107
+
108
+ if access_token
109
+ options['access_token'] = access_token
110
+ end
111
+
112
+ rsp = rest_client.send(action, url, options)
113
+ raise 'Unauthorized' if rsp.message == 'Unauthorized'
114
+
115
+ rsp
116
+ end
117
+ end
118
+ end
@@ -1,296 +1,296 @@
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 'savon'
38
- require 'marketingcloudsdk/version'
39
-
40
- module MarketingCloudSDK
41
-
42
- class SoapResponse < MarketingCloudSDK::Response
43
-
44
- def continue
45
- rsp = nil
46
- if more?
47
- rsp = unpack @client.soap_client.call(:retrieve, :message => {'ContinueRequest' => request_id})
48
- else
49
- puts 'No more data'
50
- end
51
-
52
- rsp
53
- end
54
-
55
- private
56
- def unpack_body raw
57
- @body = raw.body
58
- @request_id = raw.body[raw.body.keys.first][:request_id]
59
- unpack_msg raw
60
- rescue
61
- @message = raw.http.body
62
- @body = raw.http.body unless @body
63
- end
64
-
65
- def unpack raw
66
- @code = raw.http.code
67
- unpack_body raw
68
- @success = @message == 'OK'
69
- @results += (unpack_rslts raw)
70
- end
71
-
72
- def unpack_msg raw
73
- @message = raw.soap_fault? ? raw.body[:fault][:faultstring] : raw.body[raw.body.keys.first][:overall_status]
74
- end
75
-
76
- def unpack_rslts raw
77
- @more = (raw.body[raw.body.keys.first][:overall_status] == 'MoreDataAvailable')
78
- rslts = raw.body[raw.body.keys.first][:results] || []
79
- rslts = [rslts] unless rslts.kind_of? Array
80
- rslts
81
- rescue
82
- []
83
- end
84
- end
85
-
86
- class DescribeResponse < SoapResponse
87
- attr_reader :properties, :retrievable, :updatable, :required, :extended, :viewable, :editable
88
- private
89
-
90
- def unpack_rslts raw
91
- @retrievable, @updatable, @required, @properties, @extended, @viewable, @editable = [], [], [], [], [], [], [], []
92
- definition = raw.body[raw.body.keys.first][:object_definition]
93
- _props = definition[:properties]
94
- _props.each do |p|
95
- @retrievable << p[:name] if p[:is_retrievable] and (p[:name] != 'DataRetentionPeriod')
96
- @updatable << p[:name] if p[:is_updatable]
97
- @required << p[:name] if p[:is_required]
98
- @properties << p[:name]
99
- end
100
- # ugly, but a necessary evil
101
- _exts = definition[:extended_properties].nil? ? {} : definition[:extended_properties] # if they have no extended properties nil is returned
102
- _exts = _exts[:extended_property] || [] # if no properties nil and we need an array to iterate
103
- _exts = [_exts] unless _exts.kind_of? Array # if they have only one extended property we need to wrap it in array to iterate
104
- _exts.each do |p|
105
- @viewable << p[:name] if p[:is_viewable]
106
- @editable << p[:name] if p[:is_editable]
107
- @extended << p[:name]
108
- end
109
- @success = true # overall_status is missing from definition response, so need to set here manually
110
- _props + _exts
111
- rescue
112
- @message = "Unable to describe #{raw.locals[:message]['DescribeRequests']['ObjectDefinitionRequest']['ObjectType']}"
113
- @success = false
114
- []
115
- end
116
- end
117
-
118
- module Soap
119
- attr_accessor :wsdl, :debug#, :internal_token
120
-
121
- include MarketingCloudSDK::Targeting
122
-
123
- def header
124
- if use_oAuth2_authentication == true then
125
- {
126
- 'fueloauth' => {'fueloauth' => access_token},
127
- :attributes! => { 'fueloauth'=>{ 'xmlns' => 'http://exacttarget.com' }}
128
- }
129
- else
130
- raise 'Require legacy token for soap header' unless internal_token
131
- {
132
- 'oAuth' => {'oAuthToken' => internal_token},
133
- :attributes! => { 'oAuth' => { 'xmlns' => 'http://exacttarget.com' }}
134
- }
135
- end
136
- end
137
-
138
- def debug
139
- @debug ||= false
140
- end
141
-
142
- def wsdl
143
- @wsdl ||= 'https://webservice.exacttarget.com/etframework.wsdl'
144
- end
145
-
146
- def soap_client
147
- self.refresh
148
-
149
- soap_client_options = {
150
- soap_header: header,
151
- wsdl: wsdl,
152
- endpoint: endpoint,
153
- wsse_auth: ["*", "*"],
154
- raise_errors: false,
155
- log: debug,
156
- open_timeout:180,
157
- read_timeout: 180,
158
- headers: {'User-Agent' => 'FuelSDK-Ruby-v' + MarketingCloudSDK::VERSION}
159
- }
160
-
161
- if use_oAuth2_authentication == true then
162
- soap_client_options.delete(:wsse_auth)
163
- end
164
-
165
- @soap_client = Savon.client(soap_client_options)
166
- end
167
-
168
- def soap_describe object_type
169
- message = {
170
- 'DescribeRequests' => {
171
- 'ObjectDefinitionRequest' => {
172
- 'ObjectType' => object_type
173
- }
174
- }
175
- }
176
- soap_request :describe, message
177
- end
178
-
179
- def soap_perform object_type, action, properties
180
- message = {}
181
- message['Action'] = action
182
- message['Definitions'] = {'Definition' => properties}
183
- message['Definitions'][:attributes!] = { 'Definition' => { 'xsi:type' => ('tns:' + object_type) }}
184
-
185
- soap_request :perform, message
186
- end
187
-
188
-
189
- def soap_configure object_type, action, properties
190
- message = {}
191
- message['Action'] = action
192
- message['Configurations'] = {}
193
- if properties.is_a? Array then
194
- message['Configurations']['Configuration'] = []
195
- properties.each do |configItem|
196
- message['Configurations']['Configuration'] << configItem
197
- end
198
- else
199
- message['Configurations'] = {'Configuration' => properties}
200
- end
201
- message['Configurations'][:attributes!] = { 'Configuration' => { 'xsi:type' => ('tns:' + object_type) }}
202
-
203
- soap_request :configure, message
204
- end
205
-
206
- def soap_get object_type, properties=nil, filter=nil
207
- if properties.nil? or properties.empty?
208
- rsp = soap_describe object_type
209
- if rsp.success?
210
- properties = rsp.retrievable
211
- else
212
- rsp.instance_variable_set(:@message, "Unable to get #{object_type}") # back door update
213
- return rsp
214
- end
215
- elsif properties.kind_of? Hash
216
- properties = properties.keys
217
- elsif properties.kind_of? String
218
- properties = [properties]
219
- end
220
-
221
- message = {'ObjectType' => object_type, 'Properties' => properties}
222
-
223
- if filter and filter.kind_of? Hash
224
- message['Filter'] = filter
225
- message[:attributes!] = { 'Filter' => { 'xsi:type' => 'tns:SimpleFilterPart' } }
226
-
227
- if filter.has_key?('LogicalOperator')
228
- message[:attributes!] = { 'Filter' => { 'xsi:type' => 'tns:ComplexFilterPart' }}
229
- message['Filter'][:attributes!] = {
230
- 'LeftOperand' => { 'xsi:type' => 'tns:SimpleFilterPart' },
231
- 'RightOperand' => { 'xsi:type' => 'tns:SimpleFilterPart' }}
232
- end
233
- end
234
- message = {'RetrieveRequest' => message}
235
-
236
- soap_request :retrieve, message
237
- end
238
-
239
- def soap_post object_type, properties
240
- soap_cud :create, object_type, properties
241
- end
242
-
243
- def soap_patch object_type, properties
244
- soap_cud :update, object_type, properties
245
- end
246
-
247
- def soap_delete object_type, properties
248
- soap_cud :delete, object_type, properties
249
- end
250
-
251
- def soap_put object_type, properties
252
- soap_cud :update, object_type, properties, true
253
- end
254
-
255
- private
256
-
257
- def soap_cud action, object_type, properties, upsert=nil
258
- # get a list of attributes so we can seperate
259
- # them from standard object properties
260
- #type_attrs = soap_describe(object_type).editable
261
-
262
- #
263
- # properties = [properties] unless properties.kind_of? Array
264
- # properties.each do |p|
265
- # formated_attrs = []
266
- # p.each do |k, v|
267
- # if type_attrs.include? k
268
- # p.delete k
269
- # attrs = MarketingCloudSDK.format_name_value_pairs k => v
270
- # formated_attrs.concat attrs
271
- # end
272
- # end
273
- # (p['Attributes'] ||= []).concat formated_attrs unless formated_attrs.empty?
274
- # end
275
- #
276
-
277
- message = {
278
- 'Objects' => properties,
279
- :attributes! => { 'Objects' => { 'xsi:type' => ('tns:' + object_type) } }
280
- }
281
-
282
- if upsert
283
- message['Options'] = {"SaveOptions" => {"SaveOption" => {"PropertyName"=> "*", "SaveAction" => "UpdateAdd"}}}
284
- end
285
-
286
- soap_request action, message
287
- end
288
-
289
- def soap_request action, message
290
- response = action.eql?(:describe) ? DescribeResponse : SoapResponse
291
-
292
- rsp = soap_client.call(action, :message => message)
293
- response.new rsp, self
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
+ =end
36
+
37
+ require 'savon'
38
+ require 'marketingcloudsdk/version'
39
+
40
+ module MarketingCloudSDK
41
+
42
+ class SoapResponse < MarketingCloudSDK::Response
43
+
44
+ def continue
45
+ rsp = nil
46
+ if more?
47
+ rsp = unpack @client.soap_client.call(:retrieve, :message => {'ContinueRequest' => request_id})
48
+ else
49
+ puts 'No more data'
50
+ end
51
+
52
+ rsp
53
+ end
54
+
55
+ private
56
+ def unpack_body raw
57
+ @body = raw.body
58
+ @request_id = raw.body[raw.body.keys.first][:request_id]
59
+ unpack_msg raw
60
+ rescue
61
+ @message = raw.http.body
62
+ @body = raw.http.body unless @body
63
+ end
64
+
65
+ def unpack raw
66
+ @code = raw.http.code
67
+ unpack_body raw
68
+ @success = @message == 'OK'
69
+ @results += (unpack_rslts raw)
70
+ end
71
+
72
+ def unpack_msg raw
73
+ @message = raw.soap_fault? ? raw.body[:fault][:faultstring] : raw.body[raw.body.keys.first][:overall_status]
74
+ end
75
+
76
+ def unpack_rslts raw
77
+ @more = (raw.body[raw.body.keys.first][:overall_status] == 'MoreDataAvailable')
78
+ rslts = raw.body[raw.body.keys.first][:results] || []
79
+ rslts = [rslts] unless rslts.kind_of? Array
80
+ rslts
81
+ rescue
82
+ []
83
+ end
84
+ end
85
+
86
+ class DescribeResponse < SoapResponse
87
+ attr_reader :properties, :retrievable, :updatable, :required, :extended, :viewable, :editable
88
+ private
89
+
90
+ def unpack_rslts raw
91
+ @retrievable, @updatable, @required, @properties, @extended, @viewable, @editable = [], [], [], [], [], [], [], []
92
+ definition = raw.body[raw.body.keys.first][:object_definition]
93
+ _props = definition[:properties]
94
+ _props.each do |p|
95
+ @retrievable << p[:name] if p[:is_retrievable] and (p[:name] != 'DataRetentionPeriod')
96
+ @updatable << p[:name] if p[:is_updatable]
97
+ @required << p[:name] if p[:is_required]
98
+ @properties << p[:name]
99
+ end
100
+ # ugly, but a necessary evil
101
+ _exts = definition[:extended_properties].nil? ? {} : definition[:extended_properties] # if they have no extended properties nil is returned
102
+ _exts = _exts[:extended_property] || [] # if no properties nil and we need an array to iterate
103
+ _exts = [_exts] unless _exts.kind_of? Array # if they have only one extended property we need to wrap it in array to iterate
104
+ _exts.each do |p|
105
+ @viewable << p[:name] if p[:is_viewable]
106
+ @editable << p[:name] if p[:is_editable]
107
+ @extended << p[:name]
108
+ end
109
+ @success = true # overall_status is missing from definition response, so need to set here manually
110
+ _props + _exts
111
+ rescue
112
+ @message = "Unable to describe #{raw.locals[:message]['DescribeRequests']['ObjectDefinitionRequest']['ObjectType']}"
113
+ @success = false
114
+ []
115
+ end
116
+ end
117
+
118
+ module Soap
119
+ attr_accessor :wsdl, :debug#, :internal_token
120
+
121
+ include MarketingCloudSDK::Targeting
122
+
123
+ def header
124
+ if use_oAuth2_authentication == true then
125
+ {
126
+ 'fueloauth' => {'fueloauth' => access_token},
127
+ :attributes! => { 'fueloauth'=>{ 'xmlns' => 'http://exacttarget.com' }}
128
+ }
129
+ else
130
+ raise 'Require legacy token for soap header' unless internal_token
131
+ {
132
+ 'oAuth' => {'oAuthToken' => internal_token},
133
+ :attributes! => { 'oAuth' => { 'xmlns' => 'http://exacttarget.com' }}
134
+ }
135
+ end
136
+ end
137
+
138
+ def debug
139
+ @debug ||= false
140
+ end
141
+
142
+ def wsdl
143
+ @wsdl ||= 'https://webservice.exacttarget.com/etframework.wsdl'
144
+ end
145
+
146
+ def soap_client
147
+ self.refresh
148
+
149
+ soap_client_options = {
150
+ soap_header: header,
151
+ wsdl: wsdl,
152
+ endpoint: endpoint,
153
+ wsse_auth: ["*", "*"],
154
+ raise_errors: false,
155
+ log: debug,
156
+ open_timeout:180,
157
+ read_timeout: 180,
158
+ headers: {'User-Agent' => 'FuelSDK-Ruby-v' + MarketingCloudSDK::VERSION}
159
+ }
160
+
161
+ if use_oAuth2_authentication == true then
162
+ soap_client_options.delete(:wsse_auth)
163
+ end
164
+
165
+ @soap_client = Savon.client(soap_client_options)
166
+ end
167
+
168
+ def soap_describe object_type
169
+ message = {
170
+ 'DescribeRequests' => {
171
+ 'ObjectDefinitionRequest' => {
172
+ 'ObjectType' => object_type
173
+ }
174
+ }
175
+ }
176
+ soap_request :describe, message
177
+ end
178
+
179
+ def soap_perform object_type, action, properties
180
+ message = {}
181
+ message['Action'] = action
182
+ message['Definitions'] = {'Definition' => properties}
183
+ message['Definitions'][:attributes!] = { 'Definition' => { 'xsi:type' => ('tns:' + object_type) }}
184
+
185
+ soap_request :perform, message
186
+ end
187
+
188
+
189
+ def soap_configure object_type, action, properties
190
+ message = {}
191
+ message['Action'] = action
192
+ message['Configurations'] = {}
193
+ if properties.is_a? Array then
194
+ message['Configurations']['Configuration'] = []
195
+ properties.each do |configItem|
196
+ message['Configurations']['Configuration'] << configItem
197
+ end
198
+ else
199
+ message['Configurations'] = {'Configuration' => properties}
200
+ end
201
+ message['Configurations'][:attributes!] = { 'Configuration' => { 'xsi:type' => ('tns:' + object_type) }}
202
+
203
+ soap_request :configure, message
204
+ end
205
+
206
+ def soap_get object_type, properties=nil, filter=nil
207
+ if properties.nil? or properties.empty?
208
+ rsp = soap_describe object_type
209
+ if rsp.success?
210
+ properties = rsp.retrievable
211
+ else
212
+ rsp.instance_variable_set(:@message, "Unable to get #{object_type}") # back door update
213
+ return rsp
214
+ end
215
+ elsif properties.kind_of? Hash
216
+ properties = properties.keys
217
+ elsif properties.kind_of? String
218
+ properties = [properties]
219
+ end
220
+
221
+ message = {'ObjectType' => object_type, 'Properties' => properties}
222
+
223
+ if filter and filter.kind_of? Hash
224
+ message['Filter'] = filter
225
+ message[:attributes!] = { 'Filter' => { 'xsi:type' => 'tns:SimpleFilterPart' } }
226
+
227
+ if filter.has_key?('LogicalOperator')
228
+ message[:attributes!] = { 'Filter' => { 'xsi:type' => 'tns:ComplexFilterPart' }}
229
+ message['Filter'][:attributes!] = {
230
+ 'LeftOperand' => { 'xsi:type' => 'tns:SimpleFilterPart' },
231
+ 'RightOperand' => { 'xsi:type' => 'tns:SimpleFilterPart' }}
232
+ end
233
+ end
234
+ message = {'RetrieveRequest' => message}
235
+
236
+ soap_request :retrieve, message
237
+ end
238
+
239
+ def soap_post object_type, properties
240
+ soap_cud :create, object_type, properties
241
+ end
242
+
243
+ def soap_patch object_type, properties
244
+ soap_cud :update, object_type, properties
245
+ end
246
+
247
+ def soap_delete object_type, properties
248
+ soap_cud :delete, object_type, properties
249
+ end
250
+
251
+ def soap_put object_type, properties
252
+ soap_cud :update, object_type, properties, true
253
+ end
254
+
255
+ private
256
+
257
+ def soap_cud action, object_type, properties, upsert=nil
258
+ # get a list of attributes so we can seperate
259
+ # them from standard object properties
260
+ #type_attrs = soap_describe(object_type).editable
261
+
262
+ #
263
+ # properties = [properties] unless properties.kind_of? Array
264
+ # properties.each do |p|
265
+ # formated_attrs = []
266
+ # p.each do |k, v|
267
+ # if type_attrs.include? k
268
+ # p.delete k
269
+ # attrs = MarketingCloudSDK.format_name_value_pairs k => v
270
+ # formated_attrs.concat attrs
271
+ # end
272
+ # end
273
+ # (p['Attributes'] ||= []).concat formated_attrs unless formated_attrs.empty?
274
+ # end
275
+ #
276
+
277
+ message = {
278
+ 'Objects' => properties,
279
+ :attributes! => { 'Objects' => { 'xsi:type' => ('tns:' + object_type) } }
280
+ }
281
+
282
+ if upsert
283
+ message['Options'] = {"SaveOptions" => {"SaveOption" => {"PropertyName"=> "*", "SaveAction" => "UpdateAdd"}}}
284
+ end
285
+
286
+ soap_request action, message
287
+ end
288
+
289
+ def soap_request action, message
290
+ response = action.eql?(:describe) ? DescribeResponse : SoapResponse
291
+
292
+ rsp = soap_client.call(action, :message => message)
293
+ response.new rsp, self
294
+ end
295
+ end
296
+ end