fuelsdk 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,13 @@
1
+ FuelSDK-Ruby
2
+ ============
3
+
4
+ 2013-09-11: Version 0.1.1
5
+ ```
6
+ Added ChangeLog
7
+
8
+ soap_configure, soap_perform with supporting tests
9
+
10
+ make soap_cud more rubular and easier to read and support
11
+
12
+ fixed some issues when trying to make requests after being passed a jwt
13
+ ```
@@ -27,4 +27,5 @@ Gem::Specification.new do |spec|
27
27
  spec.add_dependency "savon", "~> 2.0"
28
28
  spec.add_dependency "json", "~> 1.7.0"
29
29
  spec.add_dependency "jwt", "~> 0.1.6"
30
+ spec.add_dependency "activesupport", "~> 3.2.8"
30
31
  end
@@ -3,6 +3,7 @@ require "fuelsdk/version"
3
3
  require 'rubygems'
4
4
  require 'date'
5
5
  require 'jwt'
6
+ require 'active_support/inflector'
6
7
 
7
8
  module FuelSDK
8
9
  require 'fuelsdk/utils'
@@ -3,7 +3,7 @@ module FuelSDK
3
3
  # not doing accessor so user, can't update these values from response.
4
4
  # You will see in the code some of these
5
5
  # items are being updated via back doors and such.
6
- attr_reader :code, :message, :results, :request_id, :body, :raw
6
+ attr_reader :code, :message, :results, :request_id, :body, :raw, :decoded_jwt
7
7
 
8
8
  # some defaults
9
9
  def success
@@ -17,11 +17,11 @@ module FuelSDK
17
17
  end
18
18
  alias :more? :more
19
19
 
20
- def initialize raw, client
20
+ def initialize raw=nil, client=nil
21
21
  @client = client # keep connection with client in case we request more
22
22
  @results = []
23
23
  @raw = raw
24
- unpack raw
24
+ unpack raw if raw
25
25
  rescue => ex # all else fails return raw
26
26
  puts ex.message
27
27
  raw
@@ -38,16 +38,26 @@ module FuelSDK
38
38
  end
39
39
 
40
40
  class Client
41
- attr_accessor :debug, :access_token, :auth_token, :internal_token, :refresh_token,
41
+ attr_accessor :debug, :auth_token, :internal_token, :refresh_token,
42
42
  :id, :secret, :signature
43
43
 
44
44
  include FuelSDK::Soap
45
45
  include FuelSDK::Rest
46
46
 
47
+ def cache
48
+ @cache ||= {
49
+ :retrievable => {},
50
+ :editable => {}
51
+ }
52
+ end
53
+
47
54
  def jwt= encoded_jwt
48
55
  raise 'Require app signature to decode JWT' unless self.signature
49
- decoded_jwt = JWT.decode(encoded_jwt, self.signature, true)
56
+ self.decoded_jwt=JWT.decode(encoded_jwt, self.signature, true)
57
+ end
50
58
 
59
+ def decoded_jwt= decoded_jwt
60
+ @decoded_jwt = decoded_jwt
51
61
  self.auth_token = decoded_jwt['request']['user']['oauthToken']
52
62
  self.internal_token = decoded_jwt['request']['user']['internalOauthToken']
53
63
  self.refresh_token = decoded_jwt['request']['user']['refreshToken']
@@ -69,29 +79,32 @@ module FuelSDK
69
79
  self.wsdl = params["defaultwsdl"] if params["defaultwsdl"]
70
80
  end
71
81
 
72
- def refresh force=false
82
+ def request_token_data
73
83
  raise 'Require Client Id and Client Secret to refresh tokens' unless (id && secret)
84
+ Hash.new.tap do |h|
85
+ h['clientId']= id
86
+ h['clientSecret'] = secret
87
+ h['refreshToken'] = refresh_token if refresh_token
88
+ h['accessType'] = 'offline'
89
+ end
90
+ end
74
91
 
75
- if (self.access_token.nil? || force)
76
- payload = Hash.new.tap do |h|
77
- h['clientId']= id
78
- h['clientSecret'] = secret
79
- h['refreshToken'] = refresh_token if refresh_token
80
- h['accessType'] = 'offline'
81
- end
82
-
83
- options = Hash.new.tap do |h|
84
- h['data'] = payload
85
- h['content_type'] = 'application/json'
86
- h['params'] = {'legacy' => 1}
87
- end
92
+ def request_token_options data
93
+ Hash.new.tap do |h|
94
+ h['data'] = data
95
+ h['content_type'] = 'application/json'
96
+ h['params'] = {'legacy' => 1}
97
+ end
98
+ end
88
99
 
100
+ def refresh force=false
101
+ if (self.auth_token.nil? || force)
102
+ options = request_token_options(request_token_data)
89
103
  response = post("https://auth.exacttargetapis.com/v1/requestToken", options)
90
104
  raise "Unable to refresh token: #{response['message']}" unless response.has_key?('accessToken')
91
105
 
92
- self.access_token = response['accessToken']
106
+ self.auth_token = response['accessToken']
93
107
  self.internal_token = response['legacyToken']
94
- #@authTokenExpiration = Time.new + tokenResponse['expiresIn']
95
108
  self.refresh_token = response['refreshToken'] if response.has_key?("refreshToken")
96
109
  end
97
110
  end
@@ -67,7 +67,7 @@ module FuelSDK
67
67
  def rest_request action, url, options={}
68
68
  retried = false
69
69
  begin
70
- (options['params'] ||= {}).merge! 'access_token' => access_token
70
+ (options['params'] ||= {}).merge! 'access_token' => auth_token
71
71
  rsp = rest_client.send(action, url, options)
72
72
  raise 'Unauthorized' if rsp.message == 'Unauthorized'
73
73
  rescue
@@ -1,6 +1,15 @@
1
1
  require 'savon'
2
2
  module FuelSDK
3
3
 
4
+ class DescribeError < StandardError
5
+ attr_reader :response
6
+ def initialize response=nil, message=nil
7
+ response.instance_variable_set(:@message, message) # back door update
8
+ @response = response
9
+ super message
10
+ end
11
+ end
12
+
4
13
  class SoapResponse < FuelSDK::Response
5
14
 
6
15
  def continue
@@ -124,37 +133,112 @@ module FuelSDK
124
133
  soap_request :describe, message
125
134
  end
126
135
 
127
- def soap_get object_type, properties=nil, filter=nil
128
- if properties.nil? or properties.empty?
129
- rsp = soap_describe object_type
130
- if rsp.success?
131
- properties = rsp.retrievable
132
- else
133
- rsp.instance_variable_set(:@message, "Unable to get #{object_type}") # back door update
134
- return rsp
135
- end
136
+ def get_all_object_properties object_type
137
+ rsp = soap_describe object_type
138
+ raise DescribeError.new(rsp, "Unable to get #{object_type}") unless rsp.success?
139
+ rsp
140
+ end
141
+
142
+ def cache_properties action, object_type, properties
143
+ raise 'Properties should be in cache as a list' unless properties.kind_of? Array
144
+ cache[action][object_type] = properties
145
+ end
146
+
147
+ def cached_properties? action, object_type
148
+ cache[action][object_type] rescue nil
149
+ end
150
+
151
+ def retrievable_properties_cached? object_type
152
+ cached_properties? :retrievable, object_type
153
+ end
154
+
155
+ def cache_retrievable object_type, properties
156
+ cache_properties :retrievable, object_type, properties
157
+ end
158
+
159
+ def get_retrievable_properties object_type
160
+ if props=retrievable_properties_cached?(object_type)
161
+ props
162
+ else
163
+ cache_retrievable object_type, get_all_object_properties(object_type).retrievable
164
+ end
165
+ end
166
+
167
+ def editable_properties_cached? object_type
168
+ cached_properties? :editable, object_type
169
+ end
170
+
171
+ def cache_editable object_type, properties
172
+ cache_properties :editable, object_type, properties
173
+ end
174
+
175
+ def get_editable_properties object_type
176
+ if props=editable_properties_cached?(object_type)
177
+ props
178
+ else
179
+ cache_editable object_type, get_all_object_properties(object_type).editable
180
+ end
181
+ end
182
+
183
+ def normalize_properties_for_retrieve object_type, properties
184
+ if properties.nil? or properties.blank?
185
+ get_retrievable_properties object_type
136
186
  elsif properties.kind_of? Hash
137
- properties = properties.keys
187
+ properties.keys
138
188
  elsif properties.kind_of? String
139
- properties = [properties]
189
+ [properties]
190
+ elsif properties.kind_of? Symbol
191
+ [properties.to_s]
192
+ else
193
+ properties
140
194
  end
195
+ end
141
196
 
142
- message = {'ObjectType' => object_type, 'Properties' => properties}
197
+ def add_simple_filter_part filter
198
+ {
199
+ 'Filter' => filter,
200
+ :attributes! => { 'Filter' => { 'xsi:type' => 'tns:SimpleFilterPart' }}
201
+ }
202
+ end
143
203
 
144
- if filter and filter.kind_of? Hash
145
- message['Filter'] = filter
146
- message[:attributes!] = { 'Filter' => { 'xsi:type' => 'tns:SimpleFilterPart' } }
204
+ def add_complex_filter_part filter
205
+ filter[:attributes!] = {
206
+ 'LeftOperand' => { 'xsi:type' => 'tns:SimpleFilterPart' },
207
+ 'RightOperand' => { 'xsi:type' => 'tns:SimpleFilterPart' }
208
+ }
147
209
 
210
+ {
211
+ 'Filter' => filter,
212
+ :attributes! => { 'Filter' => { 'xsi:type' => 'tns:ComplexFilterPart' }}
213
+ }
214
+ end
215
+
216
+ def normalize_filter filter
217
+ if filter and filter.kind_of? Hash
148
218
  if filter.has_key?('LogicalOperator')
149
- message[:attributes!] = { 'Filter' => { 'xsi:type' => 'tns:ComplexFilterPart' }}
150
- message['Filter'][:attributes!] = {
151
- 'LeftOperand' => { 'xsi:type' => 'tns:SimpleFilterPart' },
152
- 'RightOperand' => { 'xsi:type' => 'tns:SimpleFilterPart' }}
219
+ add_complex_filter_part filter
220
+ else
221
+ add_simple_filter_part filter
153
222
  end
223
+ else
224
+ {}
154
225
  end
155
- message = {'RetrieveRequest' => message}
226
+ end
227
+
228
+ def create_object_type_message object_type, properties, filter
229
+ {'ObjectType' => object_type, 'Properties' => properties}.merge filter
230
+ end
231
+
232
+ def soap_get object_type, properties=nil, filter=nil
233
+
234
+ properties = normalize_properties_for_retrieve object_type, properties
235
+ filter = normalize_filter filter
236
+ message = create_object_type_message(object_type, properties, filter)
237
+
238
+ soap_request :retrieve, 'RetrieveRequest' => message
156
239
 
157
- soap_request :retrieve, message
240
+ rescue DescribeError => err
241
+ return err.response
158
242
  end
159
243
 
160
244
  def soap_post object_type, properties
@@ -169,43 +253,65 @@ module FuelSDK
169
253
  soap_cud :delete, object_type, properties
170
254
  end
171
255
 
172
- def soap_perform object_type, properties, action
173
- message = {
256
+ def create_action_message message_type, object_type, properties, action
257
+ properties = [properties] unless properties.kind_of? Array
258
+ {
174
259
  'Action' => action,
175
- 'Definitions' => {
176
- 'Definition' => properties,
260
+ message_type => {
261
+ message_type.singularize => properties,
177
262
  :attributes! => {
178
- 'Definition' => {'xsi:type' => ('tns:' + object_type)}
263
+ message_type.singularize => { 'xsi:type' => ('tns:' + object_type) }
179
264
  }
180
265
  }
181
266
  }
267
+ end
268
+
269
+ def soap_perform object_type, properties, action
270
+ message = create_action_message 'Definitions', object_type, properties, action
182
271
  soap_request :perform, message
183
272
  end
184
273
 
185
- private
274
+ def soap_configure object_type, properties, action
275
+ message = create_action_message 'Configurations', object_type, properties, action
276
+ soap_request :configure, message
277
+ end
186
278
 
187
- def soap_cud action, object_type, properties
188
- # get a list of attributes so we can seperate
189
- # them from standard object properties
190
- type_attrs = soap_describe(object_type).editable
191
-
192
- properties = [properties] unless properties.kind_of? Array
193
- properties.each do |p|
194
- formated_attrs = []
195
- p.each do |k, v|
196
- if type_attrs.include? k
197
- p.delete k
198
- attrs = FuelSDK.format_name_value_pairs k => v
199
- formated_attrs.concat attrs
200
- end
279
+ def create_objects_message object_type, object_properties
280
+ raise 'Object properties must be a List' unless object_properties.kind_of? Array
281
+ raise 'Object properties must be a List of Hashes' unless object_properties.first.kind_of? Hash
282
+
283
+ {
284
+ 'Objects' => object_properties,
285
+ :attributes! => {'Objects' => { 'xsi:type' => ('tns:' + object_type) }}
286
+ }
287
+ end
288
+
289
+ def normalize_properties_for_cud object_type, properties
290
+ properties = [properties] unless properties.kind_of? Array
291
+ raise 'Object properties must be a Hash' unless properties.first.kind_of? Hash
292
+
293
+ # get a list of attributes so we can seperate
294
+ # them from standard object properties
295
+ type_attrs = get_editable_properties object_type
296
+
297
+ properties.each do |p|
298
+ formated_attrs = []
299
+ p.each do |k, v|
300
+ if type_attrs.include? k
301
+ p.delete k
302
+ attrs = FuelSDK.format_name_value_pairs k => v
303
+ formated_attrs.concat attrs
201
304
  end
202
- (p['Attributes'] ||= []).concat formated_attrs unless formated_attrs.empty?
203
305
  end
306
+ (p['Attributes'] ||= []).concat formated_attrs unless formated_attrs.blank?
307
+ end
308
+ end
204
309
 
205
- message = {
206
- 'Objects' => properties,
207
- :attributes! => { 'Objects' => { 'xsi:type' => ('tns:' + object_type) } }
208
- }
310
+ private
311
+
312
+ def soap_cud action, object_type, properties
313
+ properties = normalize_properties_for_cud object_type, properties
314
+ message = create_objects_message object_type, properties
209
315
  soap_request action, message
210
316
  end
211
317
 
@@ -1,22 +1,23 @@
1
1
  module FuelSDK::Targeting
2
- attr_accessor :access_token
2
+ attr_accessor :auth_token
3
3
  attr_reader :endpoint
4
4
 
5
5
  include FuelSDK::HTTPRequest
6
6
 
7
+ def refresh
8
+ raise NotImplementedError
9
+ end
10
+
7
11
  def endpoint
8
- unless @endpoint
9
- determine_stack
10
- end
11
- @endpoint
12
+ @endpoint ||= determine_stack
12
13
  end
13
14
 
14
15
  protected
15
16
  def determine_stack
16
- options = {'params' => {'access_token' => self.access_token}}
17
+ refresh unless self.auth_token
18
+ options = {'params' => {'access_token' => self.auth_token}}
17
19
  response = get("https://www.exacttargetapis.com/platform/v1/endpoints/soap", options)
18
- @endpoint = response['url']
19
- rescue => e
20
- raise 'Unable to determine stack using: ' + e.message
20
+ raise 'Unable to determine stack' unless response.success?
21
+ response['url']
21
22
  end
22
23
  end
@@ -1,3 +1,3 @@
1
1
  module FuelSDK
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
@@ -2,6 +2,16 @@ require 'spec_helper.rb'
2
2
 
3
3
  describe FuelSDK::Client do
4
4
 
5
+ it { should respond_to :cache }
6
+
7
+ describe '#cache' do
8
+ let(:client) { FuelSDK::Client.new }
9
+
10
+ it 'its default keys are retrievable and editable' do
11
+ expect(client.cache).to eq({:retrievable => {}, :editable => {}})
12
+ end
13
+ end
14
+
5
15
  context 'initialized' do
6
16
 
7
17
  it 'with client parameters' do
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe FuelSDK::Response do
4
+ it_behaves_like 'Response Object'
5
+ end
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper.rb'
2
- require 'objects_helper_spec.rb'
3
2
 
4
3
  describe FuelSDK::Objects::Base do
5
4
 
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe FuelSDK::Soap do
4
+
5
+ let(:client) { FuelSDK::Client.new }
6
+ subject {client}
7
+
8
+ it 'has inflector support' do
9
+ expect('Configurations'.singularize).to eq 'Configuration'
10
+ end
11
+
12
+ describe '#soap_configure' do
13
+ it 'makes a soap configure request with message' do
14
+ subject.should_receive(:create_action_message).with('Configurations', 'Subscriber', [], 'Do it').and_return({})
15
+ subject.should_receive(:soap_request).with(:configure, {})
16
+ subject.soap_configure 'Subscriber', [], 'Do it'
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,132 @@
1
+ require 'spec_helper'
2
+
3
+ describe FuelSDK::Soap do
4
+
5
+ let(:client) { FuelSDK::Client.new }
6
+
7
+ subject { client }
8
+
9
+ describe '#editable_properties_cached?' do
10
+ it 'returns a list of editable properties for the object' do
11
+ subject.should_receive(:cached_properties?)
12
+ .with(:editable, 'item')
13
+ .and_return(['prop'])
14
+
15
+ expect(subject.editable_properties_cached? 'item').to eq(['prop'])
16
+ end
17
+
18
+ it 'returns nil if not cached' do
19
+ expect(subject.editable_properties_cached? 'missing').to be_nil
20
+ end
21
+ end
22
+
23
+ describe '#get_editable_properties' do
24
+ it 'returns cached properties' do
25
+ subject.should_receive(:editable_properties_cached?)
26
+ .with('object')
27
+ .and_return(['prop'])
28
+
29
+ subject.should_not_receive(:get_all_object_properties)
30
+ subject.should_not_receive(:cache_editable)
31
+
32
+ expect(subject.get_editable_properties('object')).to eq ['prop']
33
+ end
34
+
35
+ it 'requests and caches properties when not in cache' do
36
+ subject.should_receive(:editable_properties_cached?)
37
+ .with('object')
38
+ .and_return(nil)
39
+
40
+ response = double(FuelSDK::DescribeResponse)
41
+ response.stub(:editable).and_return(['prop'])
42
+ subject.should_receive(:get_all_object_properties)
43
+ .and_return(response)
44
+
45
+ subject.should_receive(:cache_editable)
46
+ .with('object', ['prop'])
47
+ .and_return(['prop'])
48
+
49
+ expect(subject.get_editable_properties('object')).to eq ['prop']
50
+ end
51
+ end
52
+
53
+ describe '#cache_editable' do
54
+ it 'caches object properties to :editable' do
55
+ subject.cache_editable('Subscriber', ['Email'])
56
+ expect(subject.cache[:editable]).to eq 'Subscriber' => ['Email']
57
+ end
58
+ end
59
+
60
+ describe '#normalize_properties_for_cud' do
61
+
62
+ it 'creates soap objects properties hash putting ' \
63
+ 'custom attributes into name value pairs' do
64
+
65
+ subject.should_receive(:get_editable_properties)
66
+ .with('Subscriber')
67
+ .and_return(['FirstName'])
68
+
69
+ expect(subject.normalize_properties_for_cud(
70
+ 'Subscriber',
71
+ [{'Email' => 'dev@exacttarget.com', 'FirstName' => 'Devy'}]
72
+ )).to eq(
73
+ [{
74
+ 'Email' => 'dev@exacttarget.com',
75
+ 'Attributes' => [{'Name' => 'FirstName', 'Value' => 'Devy'}]
76
+ }]
77
+ )
78
+ end
79
+
80
+ it 'converts properties into an array' do
81
+ subject.should_receive(:get_editable_properties)
82
+ .with('Subscriber')
83
+ .and_return(['FirstName'])
84
+
85
+ expect(subject.normalize_properties_for_cud(
86
+ 'Subscriber',
87
+ {'Email' => 'dev@exacttarget.com', 'FirstName' => 'Devy'}
88
+ )).to eq(
89
+ [{
90
+ 'Email' => 'dev@exacttarget.com',
91
+ 'Attributes' => [{'Name' => 'FirstName', 'Value' => 'Devy'}]
92
+ }]
93
+ )
94
+ end
95
+
96
+ it 'raises an exception if properties are not a hash' do
97
+ expect { subject.normalize_properties_for_cud('Subscriber', 'Email') }
98
+ .to raise_error
99
+ end
100
+ end
101
+
102
+ describe '#create_objects_message' do
103
+ it 'creates hash for soap message' do
104
+ obj_attributes = [{'Name' => 'First Name', 'Value' => 'Justin'}]
105
+ expect(subject.create_objects_message('object', obj_attributes)).to eq(
106
+ {
107
+ 'Objects' => [{'Name' => 'First Name', 'Value' => 'Justin'}],
108
+ :attributes! => {'Objects' => { 'xsi:type' => 'tns:object' }}
109
+ }
110
+ )
111
+ end
112
+
113
+ it 'raises an exception if object attributes is not an Array' do
114
+ expect{ subject.create_objects_message('object', '1') }.to raise_error
115
+ end
116
+
117
+ it 'raises an exception if object attributes are not stored in a hash' do
118
+ expect{ subject.create_objects_message('object', ['1']) }.to raise_error
119
+ end
120
+ end
121
+
122
+ describe '#soap_cud' do
123
+ it 'request with message created with normalized properties' do
124
+
125
+ subject.should_receive(:normalize_properties_for_cud)
126
+ subject.should_receive(:create_objects_message)
127
+ subject.should_receive(:soap_request)
128
+
129
+ subject.send :soap_cud, :post, 'Subscriber', [{'EmailAddress' => 'dev@exacttarget.com'}]
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe FuelSDK::DescribeError do
4
+
5
+ it { should respond_to(:response) }
6
+
7
+ let (:triggering) { FuelSDK::SoapResponse.new }
8
+
9
+ it 'has passed message as error' do
10
+ error = FuelSDK::DescribeError.new(triggering, 'i am an error message')
11
+ expect(error.message).to eq 'i am an error message'
12
+ end
13
+
14
+ it 'triggering response is available' do
15
+ error = FuelSDK::DescribeError.new(triggering, 'i am an error message')
16
+ expect(error.response).to eq triggering
17
+ end
18
+
19
+ it 'sets message on response' do
20
+ expect(triggering.message).to be_nil
21
+ error = FuelSDK::DescribeError.new(triggering, 'i am an error message')
22
+ expect(triggering.message).to eq 'i am an error message'
23
+ end
24
+
25
+ end
@@ -0,0 +1,257 @@
1
+ require 'spec_helper'
2
+
3
+ describe FuelSDK::Soap do
4
+
5
+ let(:client) { FuelSDK::Client.new }
6
+ subject { client }
7
+
8
+ describe '#get_all_object_properties' do
9
+
10
+ it 'returns properties for object_type' do
11
+ response = mock(FuelSDK::DescribeResponse)
12
+ response.should_receive(:success?).and_return(true)
13
+
14
+ subject.should_receive(:soap_describe)
15
+ .with('some object')
16
+ .and_return(response)
17
+
18
+ expect(subject.get_all_object_properties('some object'))
19
+ .to eq response
20
+ end
21
+
22
+ it 'raises an DescribeError when describe is unsuccessful' do
23
+ response = mock(FuelSDK::DescribeResponse)
24
+ response.should_receive(:success?).and_return(false)
25
+ response.stub(:status).and_return('ERROR')
26
+
27
+ subject.should_receive(:soap_describe)
28
+ .with('some object')
29
+ .and_return(response)
30
+
31
+ expect { subject.get_all_object_properties('some object') }
32
+ .to raise_error FuelSDK::DescribeError
33
+ end
34
+ end
35
+
36
+ describe '#normalize_properties_for_retrieve' do
37
+ it 'when properties are nil gets_all_object_properties' do
38
+ subject.should_receive(:get_retrievable_properties)
39
+ .with('object').and_return('called all')
40
+
41
+ expect(subject.normalize_properties_for_retrieve('object', nil)).to eq 'called all'
42
+ end
43
+
44
+ describe 'when properties is a' do
45
+ subject {
46
+ client.should_not_receive(:get_retrievable_properties)
47
+ client
48
+ }
49
+
50
+ it 'Hash returns keys' do
51
+ expect(subject.normalize_properties_for_retrieve('object', {'Prop1' => 'a', 'Prop2' => 'b'}))
52
+ .to eq ['Prop1', 'Prop2']
53
+ end
54
+
55
+ it 'String returns Array' do
56
+ expect(subject.normalize_properties_for_retrieve('object', 'Prop1'))
57
+ .to eq ['Prop1']
58
+ end
59
+
60
+ it 'Symbol returns Array' do
61
+ expect(subject.normalize_properties_for_retrieve('object', :Prop1))
62
+ .to eq ['Prop1']
63
+ end
64
+
65
+ it 'Array returns Array' do
66
+ expect(subject.normalize_properties_for_retrieve('object', ['Prop1']))
67
+ .to eq ['Prop1']
68
+ end
69
+ end
70
+ end
71
+
72
+ describe '#normalize_filter' do
73
+ it 'returns complex filter part when filter contains LogicalOperator key' do
74
+ expect(subject.normalize_filter({'LogicalOperator' => 'AND'}))
75
+ .to eq(
76
+ {
77
+ 'Filter' => {
78
+ 'LogicalOperator' => 'AND',
79
+ :attributes! => {
80
+ 'LeftOperand' => { 'xsi:type' => 'tns:SimpleFilterPart' },
81
+ 'RightOperand' => { 'xsi:type' => 'tns:SimpleFilterPart' }
82
+ },
83
+ },
84
+ :attributes! => { 'Filter' => { 'xsi:type' => 'tns:ComplexFilterPart' }}
85
+ }
86
+ )
87
+ end
88
+
89
+ it 'returns simple filter part by default' do
90
+ expect(subject.normalize_filter({'SimpleOperator' => 'equals'}))
91
+ .to eq(
92
+ {
93
+ 'Filter' => {
94
+ 'SimpleOperator' => 'equals',
95
+ },
96
+ :attributes! => { 'Filter' => { 'xsi:type' => 'tns:SimpleFilterPart' }}
97
+ }
98
+ )
99
+ end
100
+
101
+ it 'returns empty hash when no filter' do
102
+ expect(subject.normalize_filter(nil)).to eq({})
103
+ end
104
+
105
+ it 'returns empty hash when filter is unparsable' do
106
+ expect(subject.normalize_filter(['unparsable'])).to eq({})
107
+ end
108
+ end
109
+
110
+ describe '#cache_properties' do
111
+ it 'raise an error if properties is not an Array' do
112
+
113
+ subject.should_not_receive(:cache)
114
+ expect { subject.cache_properties :retrievable, 'Subscriber', 'EmailAddress' }
115
+ .to raise_error
116
+ end
117
+
118
+ it 'caches properties' do
119
+ subject.should_receive(:cache).and_return({:retrievable => {}})
120
+ expect(subject.cache_properties :retrievable, 'Subscriber', ['EmailAddress'])
121
+ .to eq(['EmailAddress'])
122
+ end
123
+ end
124
+
125
+ describe '#cached_properties?' do
126
+ it 'returns cached properties' do
127
+ subject.should_receive(:cache).and_return(
128
+ {
129
+ :retrievable => {
130
+ 'Subscriber' => ['EmailAddress']}
131
+ }
132
+ )
133
+
134
+ expect(subject.cached_properties?(:retrievable, 'Subscriber'))
135
+ .to eq ['EmailAddress']
136
+ end
137
+
138
+ it 'returns nil on error access cache' do
139
+ subject.should_receive(:cache).and_return(1)
140
+ expect(subject.cached_properties?(:retrievable, 'Subscriber'))
141
+ .to be_nil
142
+ end
143
+ end
144
+
145
+ describe '#retrievable_properties_cached?' do
146
+ it 'returns a list of retrievable properties for the object' do
147
+ subject.should_receive(:cached_properties?)
148
+ .with(:retrievable, 'item')
149
+ .and_return(['prop'])
150
+
151
+ expect(subject.retrievable_properties_cached? 'item').to eq(['prop'])
152
+ end
153
+
154
+ it 'returns nil if not cached' do
155
+ expect(subject.retrievable_properties_cached? 'missing').to be_nil
156
+ end
157
+ end
158
+
159
+ describe '#get_retrievable_properties' do
160
+ it 'returns cached properties' do
161
+ subject.should_receive(:retrievable_properties_cached?)
162
+ .with('object')
163
+ .and_return(['prop'])
164
+
165
+ subject.should_not_receive(:get_all_object_properties)
166
+ subject.should_not_receive(:cache_retrievable)
167
+
168
+ expect(subject.get_retrievable_properties('object')).to eq ['prop']
169
+ end
170
+
171
+ it 'requests and caches properties when not in cache' do
172
+ subject.should_receive(:retrievable_properties_cached?)
173
+ .with('object')
174
+ .and_return(nil)
175
+
176
+ response = mock(FuelSDK::DescribeResponse)
177
+ response.stub(:retrievable).and_return(['prop'])
178
+ subject.should_receive(:get_all_object_properties)
179
+ .and_return(response)
180
+
181
+ subject.should_receive(:cache_retrievable)
182
+ .with('object', ['prop'])
183
+ .and_return(['prop'])
184
+
185
+ expect(subject.get_retrievable_properties('object')).to eq ['prop']
186
+ end
187
+ end
188
+
189
+ describe '#cache_retrievable' do
190
+ it 'caches object properties to :retrievable' do
191
+ subject.cache_retrievable('Subscriber', ['Email'])
192
+ expect(subject.cache[:retrievable]).to eq 'Subscriber' => ['Email']
193
+ end
194
+ end
195
+
196
+ describe '#soap_get' do
197
+ it 'request with message created with normalized properties, filters' do
198
+
199
+ subject.should_receive(:normalize_properties_for_retrieve)
200
+ .with('end to end', nil).and_return([])
201
+
202
+ subject.should_receive(:normalize_filter)
203
+ .with(nil).and_return({})
204
+
205
+ subject.should_receive(:create_object_type_message)
206
+ .with('end to end', [], {}).and_return('message')
207
+
208
+ subject.should_receive(:soap_request)
209
+ .with(:retrieve, 'RetrieveRequest' => 'message')
210
+
211
+ subject.soap_get 'end to end'
212
+ end
213
+
214
+ it 'request an object without passing properties or a filter' do
215
+
216
+ subject.should_receive(:get_retrievable_properties)
217
+ .with('no criteria').and_return(['Props1'])
218
+
219
+ subject.should_not_receive(:add_complex_filter_part)
220
+ subject.should_not_receive(:add_simple_filter_part)
221
+
222
+ subject.should_receive(:soap_request).with(:retrieve, 'RetrieveRequest' => {
223
+ 'ObjectType' => 'no criteria',
224
+ 'Properties' => ['Props1']
225
+ }
226
+ )
227
+
228
+ subject.soap_get 'no criteria'
229
+ end
230
+
231
+ it 'request an object with limited properties' do
232
+
233
+ subject.should_not_receive(:get_retrievable_properties)
234
+ subject.should_not_receive(:add_complex_fitler_part)
235
+ subject.should_not_receive(:add_simple_fitler_part)
236
+
237
+ subject.should_receive(:soap_request).with(:retrieve, 'RetrieveRequest' => {
238
+ 'ObjectType' => 'limited',
239
+ 'Properties' => ['Props1']
240
+ }
241
+ )
242
+
243
+ subject.soap_get('limited', ['Props1'])
244
+ end
245
+
246
+ it 'request an invalid object without properties' do
247
+ subject.should_receive(:get_retrievable_properties) { raise FuelSDK::DescribeError.new(
248
+ FuelSDK::DescribeResponse.new, "Unable to get invalid"
249
+ )
250
+ }
251
+
252
+ rsp = subject.soap_get('invalid')
253
+ expect(rsp.success?).to be_false
254
+ end
255
+ end
256
+
257
+ end
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ describe FuelSDK::Soap do
4
+
5
+ let(:client) { FuelSDK::Client.new }
6
+ subject {client}
7
+
8
+ it 'has inflector support' do
9
+ expect('Definitions'.singularize).to eq 'Definition'
10
+ end
11
+
12
+ describe '#create_action_message' do
13
+ it 'returns message' do
14
+ expect(subject.create_action_message('Definitions', 'QueryDefinition', [{'ObjectID' => 1}], 'start'))
15
+ .to eq(
16
+ {
17
+ 'Action' => 'start',
18
+ 'Definitions' => {
19
+ 'Definition' => [{'ObjectID' => 1}],
20
+ :attributes! => {
21
+ 'Definition' => { 'xsi:type' => 'tns:QueryDefinition'}
22
+ }
23
+ }
24
+ }
25
+ )
26
+ end
27
+
28
+ it 'standardizes properties to an array' do
29
+ expect(subject.create_action_message('Definitions', 'QueryDefinition', {'ObjectID' => 1}, 'start'))
30
+ .to eq(
31
+ {
32
+ 'Action' => 'start',
33
+ 'Definitions' => {
34
+ 'Definition' => [{'ObjectID' => 1}],
35
+ :attributes! => {
36
+ 'Definition' => { 'xsi:type' => 'tns:QueryDefinition'}
37
+ }
38
+ }
39
+ }
40
+ )
41
+ end
42
+ end
43
+
44
+ describe '#soap_perform' do
45
+ it 'starts a defined query' do
46
+ subject.should_receive(:create_action_message)
47
+ .with('Definitions', 'QueryDefinition', [{'ObjectID' => 1}], 'start')
48
+ .and_return 'Do It'
49
+ subject.should_receive(:soap_request).with(:perform, 'Do It')
50
+ subject.soap_perform 'QueryDefinition', [{'ObjectID' => 1}], 'start'
51
+ end
52
+ end
53
+ end
@@ -6,11 +6,11 @@ describe FuelSDK::Soap do
6
6
 
7
7
  subject { client }
8
8
 
9
- it { should respond_to(:soap_get) }
10
9
  it { should respond_to(:soap_post) }
11
10
  it { should respond_to(:soap_patch) }
12
11
  it { should respond_to(:soap_delete) }
13
12
  it { should respond_to(:soap_describe) }
13
+ it { should respond_to(:soap_perform) }
14
14
 
15
15
  it { should respond_to(:header) }
16
16
  it { should_not respond_to(:header=) }
@@ -62,8 +62,7 @@ describe FuelSDK::Soap do
62
62
  client.stub(:soap_request) do |action, message|
63
63
  [action, message]
64
64
  end
65
-
66
- client.stub_chain(:soap_describe,:editable)
65
+ client.should_receive(:get_editable_properties)
67
66
  .and_return(['First Name', 'Last Name', 'Gender'])
68
67
  client
69
68
  }
@@ -12,3 +12,51 @@ RSpec.configure do |config|
12
12
  # Use the specified formatter
13
13
  config.formatter = :documentation
14
14
  end
15
+
16
+ shared_examples_for 'Response Object' do
17
+ it { should respond_to(:code) }
18
+ it { should respond_to(:message) }
19
+ it { should respond_to(:results) }
20
+ it { should respond_to(:request_id) }
21
+ it { should respond_to(:body) }
22
+ it { should respond_to(:raw) }
23
+ it { should respond_to(:more) }
24
+ it { should respond_to(:more?) }
25
+ it { should respond_to(:success) }
26
+ it { should respond_to(:success?) }
27
+ it { should respond_to(:status) }
28
+ it { should respond_to(:continue) }
29
+ end
30
+
31
+ # Everything will be readable so test for shared from Read behavior
32
+ shared_examples_for 'Soap Read Object' do
33
+ # begin backwards compat
34
+ it { should respond_to :props= }
35
+ it { should respond_to :authStub= }
36
+ # end
37
+ it { should respond_to :id }
38
+ it { should respond_to :properties }
39
+ it { should respond_to :client }
40
+ it { should respond_to :filter }
41
+ it { should respond_to :info }
42
+ it { should respond_to :get }
43
+ end
44
+
45
+ shared_examples_for 'Soap CUD Object' do
46
+ it { should respond_to :post }
47
+ it { should respond_to :patch }
48
+ it { should respond_to :delete }
49
+ end
50
+
51
+ shared_examples_for 'Soap Object' do
52
+ it_behaves_like 'Soap Read Object'
53
+ it_behaves_like 'Soap CUD Object'
54
+ end
55
+
56
+ shared_examples_for 'Soap Read Only Object' do
57
+ it_behaves_like 'Soap Read Object'
58
+ it { should_not respond_to :post }
59
+ it { should_not respond_to :patch }
60
+ it { should_not respond_to :delete }
61
+ end
62
+
@@ -11,28 +11,59 @@ describe FuelSDK::Targeting do
11
11
  it { should respond_to(:post) }
12
12
  it { should respond_to(:patch) }
13
13
  it { should respond_to(:delete) }
14
- it { should respond_to(:access_token) }
14
+ it { should respond_to(:auth_token) }
15
+
16
+ let(:response) {
17
+ rsp = double(FuelSDK::HTTPResponse)
18
+ rsp.stub(:success?).and_return(true)
19
+ rsp.stub(:[]).with('url').and_return('S#.authentication.target')
20
+ rsp
21
+ }
22
+
23
+ let(:client) {
24
+ Class.new.new.extend(FuelSDK::Targeting)
25
+ }
15
26
 
16
27
  describe '#determine_stack' do
17
- let(:client) { c = Class.new.new.extend(FuelSDK::Targeting)
18
- c.stub(:access_token).and_return('open_sesame')
19
- c.stub(:get)
20
- .with('https://www.exacttargetapis.com/platform/v1/endpoints/soap',{'params'=>{'access_token'=>'open_sesame'}})
21
- .and_return({'url' => 'S#.authentication.target'})
22
- c
23
- }
24
- it 'sets @endpoint' do
25
- expect(client.send(:determine_stack)).to eq 'S#.authentication.target'
28
+ describe 'without auth_token' do
29
+ it 'calls refresh' do
30
+ client.stub(:refresh) {
31
+ client.instance_variable_set('@auth_token', 'open_sesame')
32
+ }
33
+ client.stub(:get)
34
+ .with('https://www.exacttargetapis.com/platform/v1/endpoints/soap',
35
+ {'params'=>{'access_token'=>'open_sesame'}})
36
+ .and_return(response)
37
+ end
38
+ end
39
+
40
+ describe 'with valid auth_token' do
41
+ before :each do
42
+ client.should_receive(:auth_token).twice.and_return('open_sesame')
43
+ end
44
+
45
+ it 'when successful returns endpoint' do
46
+ client.stub(:get)
47
+ .with('https://www.exacttargetapis.com/platform/v1/endpoints/soap',
48
+ {'params'=>{'access_token'=>'open_sesame'}})
49
+ .and_return(response)
50
+ expect(client.send(:determine_stack)).to eq 'S#.authentication.target'
51
+ end
52
+
53
+ it 'raises error on unsuccessful responses' do
54
+ client.stub(:get).and_return{
55
+ rsp = double(FuelSDK::HTTPResponse)
56
+ rsp.stub(:success?).and_return(false)
57
+ rsp
58
+ }
59
+ expect{ client.send(:determine_stack) }.to raise_error 'Unable to determine stack'
60
+ end
26
61
  end
27
62
  end
28
63
 
29
64
  describe '#endpoint' do
30
- let(:client) { c = Class.new.new.extend(FuelSDK::Targeting)
31
- c.stub(:get).and_return({'url' => 'S#.authentication.target'})
32
- c
33
- }
34
-
35
65
  it 'calls determine_stack to find target' do
66
+ client.should_receive(:determine_stack).and_return('S#.authentication.target')
36
67
  expect(client.endpoint).to eq 'S#.authentication.target'
37
68
  end
38
69
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fuelsdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-08-29 00:00:00.000000000 Z
13
+ date: 2013-09-12 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bundler
@@ -140,6 +140,22 @@ dependencies:
140
140
  - - ~>
141
141
  - !ruby/object:Gem::Version
142
142
  version: 0.1.6
143
+ - !ruby/object:Gem::Dependency
144
+ name: activesupport
145
+ requirement: !ruby/object:Gem::Requirement
146
+ none: false
147
+ requirements:
148
+ - - ~>
149
+ - !ruby/object:Gem::Version
150
+ version: 3.2.8
151
+ type: :runtime
152
+ prerelease: false
153
+ version_requirements: !ruby/object:Gem::Requirement
154
+ none: false
155
+ requirements:
156
+ - - ~>
157
+ - !ruby/object:Gem::Version
158
+ version: 3.2.8
143
159
  description: Fuel SDK for Ruby
144
160
  email: []
145
161
  executables: []
@@ -147,6 +163,7 @@ extensions: []
147
163
  extra_rdoc_files: []
148
164
  files:
149
165
  - .gitignore
166
+ - ChangeLog.md
150
167
  - Gemfile
151
168
  - Guardfile
152
169
  - README.md
@@ -180,12 +197,17 @@ files:
180
197
  - samples/sample-unsubevent.rb
181
198
  - samples/sample_helper.rb
182
199
  - spec/client_spec.rb
200
+ - spec/fuelsdk_response_spec.rb
183
201
  - spec/helper_funcs_spec.rb
184
202
  - spec/http_request_spec.rb
185
- - spec/objects_helper_spec.rb
186
203
  - spec/objects_spec.rb
187
204
  - spec/rest_spec.rb
188
- - spec/soap_spec.rb
205
+ - spec/soap/configure_spec.rb
206
+ - spec/soap/cud_spec.rb
207
+ - spec/soap/describe_error_spec.rb
208
+ - spec/soap/get_spec.rb
209
+ - spec/soap/perform_spec.rb
210
+ - spec/soap/soap_spec.rb
189
211
  - spec/spec_helper.rb
190
212
  - spec/targeting_spec.rb
191
213
  homepage: https://code.exacttarget.com/sdks
@@ -233,11 +255,16 @@ test_files:
233
255
  - samples/sample-unsubevent.rb
234
256
  - samples/sample_helper.rb
235
257
  - spec/client_spec.rb
258
+ - spec/fuelsdk_response_spec.rb
236
259
  - spec/helper_funcs_spec.rb
237
260
  - spec/http_request_spec.rb
238
- - spec/objects_helper_spec.rb
239
261
  - spec/objects_spec.rb
240
262
  - spec/rest_spec.rb
241
- - spec/soap_spec.rb
263
+ - spec/soap/configure_spec.rb
264
+ - spec/soap/cud_spec.rb
265
+ - spec/soap/describe_error_spec.rb
266
+ - spec/soap/get_spec.rb
267
+ - spec/soap/perform_spec.rb
268
+ - spec/soap/soap_spec.rb
242
269
  - spec/spec_helper.rb
243
270
  - spec/targeting_spec.rb
@@ -1,32 +0,0 @@
1
-
2
- # Everything will be readable so test for shared from Read behavior
3
- shared_examples_for 'Soap Read Object' do
4
- # begin backwards compat
5
- it { should respond_to :props= }
6
- it { should respond_to :authStub= }
7
- # end
8
- it { should respond_to :id }
9
- it { should respond_to :properties }
10
- it { should respond_to :client }
11
- it { should respond_to :filter }
12
- it { should respond_to :info }
13
- it { should respond_to :get }
14
- end
15
-
16
- shared_examples_for 'Soap CUD Object' do
17
- it { should respond_to :post }
18
- it { should respond_to :patch }
19
- it { should respond_to :delete }
20
- end
21
-
22
- shared_examples_for 'Soap Object' do
23
- it_behaves_like 'Soap Read Object'
24
- it_behaves_like 'Soap CUD Object'
25
- end
26
-
27
- shared_examples_for 'Soap Read Only Object' do
28
- it_behaves_like 'Soap Read Object'
29
- it { should_not respond_to :post }
30
- it { should_not respond_to :patch }
31
- it { should_not respond_to :delete }
32
- end