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,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,282 +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
- raise 'Require legacy token for soap header' unless internal_token
125
- {
126
- 'oAuth' => {'oAuthToken' => internal_token},
127
- :attributes! => { 'oAuth' => { 'xmlns' => 'http://exacttarget.com' }}
128
- }
129
- end
130
-
131
- def debug
132
- @debug ||= false
133
- end
134
-
135
- def wsdl
136
- @wsdl ||= 'https://webservice.exacttarget.com/etframework.wsdl'
137
- end
138
-
139
- def soap_client
140
- self.refresh
141
- @soap_client = Savon.client(
142
- soap_header: header,
143
- wsdl: wsdl,
144
- endpoint: endpoint,
145
- wsse_auth: ["*", "*"],
146
- raise_errors: false,
147
- log: debug,
148
- open_timeout:180,
149
- read_timeout: 180,
150
- headers: {'User-Agent' => 'FuelSDK-Ruby-v' + MarketingCloudSDK::VERSION}
151
- )
152
- end
153
-
154
- def soap_describe object_type
155
- message = {
156
- 'DescribeRequests' => {
157
- 'ObjectDefinitionRequest' => {
158
- 'ObjectType' => object_type
159
- }
160
- }
161
- }
162
- soap_request :describe, message
163
- end
164
-
165
- def soap_perform object_type, action, properties
166
- message = {}
167
- message['Action'] = action
168
- message['Definitions'] = {'Definition' => properties}
169
- message['Definitions'][:attributes!] = { 'Definition' => { 'xsi:type' => ('tns:' + object_type) }}
170
-
171
- soap_request :perform, message
172
- end
173
-
174
-
175
- def soap_configure object_type, action, properties
176
- message = {}
177
- message['Action'] = action
178
- message['Configurations'] = {}
179
- if properties.is_a? Array then
180
- message['Configurations']['Configuration'] = []
181
- properties.each do |configItem|
182
- message['Configurations']['Configuration'] << configItem
183
- end
184
- else
185
- message['Configurations'] = {'Configuration' => properties}
186
- end
187
- message['Configurations'][:attributes!] = { 'Configuration' => { 'xsi:type' => ('tns:' + object_type) }}
188
-
189
- soap_request :configure, message
190
- end
191
-
192
- def soap_get object_type, properties=nil, filter=nil
193
- if properties.nil? or properties.empty?
194
- rsp = soap_describe object_type
195
- if rsp.success?
196
- properties = rsp.retrievable
197
- else
198
- rsp.instance_variable_set(:@message, "Unable to get #{object_type}") # back door update
199
- return rsp
200
- end
201
- elsif properties.kind_of? Hash
202
- properties = properties.keys
203
- elsif properties.kind_of? String
204
- properties = [properties]
205
- end
206
-
207
- message = {'ObjectType' => object_type, 'Properties' => properties}
208
-
209
- if filter and filter.kind_of? Hash
210
- message['Filter'] = filter
211
- message[:attributes!] = { 'Filter' => { 'xsi:type' => 'tns:SimpleFilterPart' } }
212
-
213
- if filter.has_key?('LogicalOperator')
214
- message[:attributes!] = { 'Filter' => { 'xsi:type' => 'tns:ComplexFilterPart' }}
215
- message['Filter'][:attributes!] = {
216
- 'LeftOperand' => { 'xsi:type' => 'tns:SimpleFilterPart' },
217
- 'RightOperand' => { 'xsi:type' => 'tns:SimpleFilterPart' }}
218
- end
219
- end
220
- message = {'RetrieveRequest' => message}
221
-
222
- soap_request :retrieve, message
223
- end
224
-
225
- def soap_post object_type, properties
226
- soap_cud :create, object_type, properties
227
- end
228
-
229
- def soap_patch object_type, properties
230
- soap_cud :update, object_type, properties
231
- end
232
-
233
- def soap_delete object_type, properties
234
- soap_cud :delete, object_type, properties
235
- end
236
-
237
- def soap_put object_type, properties
238
- soap_cud :update, object_type, properties, true
239
- end
240
-
241
- private
242
-
243
- def soap_cud action, object_type, properties, upsert=nil
244
- # get a list of attributes so we can seperate
245
- # them from standard object properties
246
- #type_attrs = soap_describe(object_type).editable
247
-
248
- #
249
- # properties = [properties] unless properties.kind_of? Array
250
- # properties.each do |p|
251
- # formated_attrs = []
252
- # p.each do |k, v|
253
- # if type_attrs.include? k
254
- # p.delete k
255
- # attrs = MarketingCloudSDK.format_name_value_pairs k => v
256
- # formated_attrs.concat attrs
257
- # end
258
- # end
259
- # (p['Attributes'] ||= []).concat formated_attrs unless formated_attrs.empty?
260
- # end
261
- #
262
-
263
- message = {
264
- 'Objects' => properties,
265
- :attributes! => { 'Objects' => { 'xsi:type' => ('tns:' + object_type) } }
266
- }
267
-
268
- if upsert
269
- message['Options'] = {"SaveOptions" => {"SaveOption" => {"PropertyName"=> "*", "SaveAction" => "UpdateAdd"}}}
270
- end
271
-
272
- soap_request action, message
273
- end
274
-
275
- def soap_request action, message
276
- response = action.eql?(:describe) ? DescribeResponse : SoapResponse
277
-
278
- rsp = soap_client.call(action, :message => message)
279
- response.new rsp, self
280
- end
281
- end
282
- 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