loyalty_lab_sdk 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +10 -0
- data/LICENSE.txt +9 -0
- data/README.rdoc +45 -0
- data/lib/loyalty_lab_sdk/config.rb +62 -0
- data/lib/loyalty_lab_sdk/exceptions.rb +18 -0
- data/lib/loyalty_lab_sdk/loyalty_api.rb +329 -0
- data/lib/loyalty_lab_sdk/version.rb +3 -0
- data/lib/loyalty_lab_sdk.rb +24 -0
- metadata +115 -0
data/CHANGELOG.rdoc
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
= Loyalty Lab SDK
|
2
|
+
|
3
|
+
== Version 0.0.0
|
4
|
+
* Initial release
|
5
|
+
* Supports all LoyaltyAPI calls
|
6
|
+
* Supports lazy authentication
|
7
|
+
* Supports environment variable authentication
|
8
|
+
* Helpers for creating shoppers
|
9
|
+
* Supports retries on timeout errors
|
10
|
+
* Supports re-authentication when authentication token expires
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
Copyright (c) 2012 RevPAR Collective, Inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
|
+
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
8
|
+
|
9
|
+
THIS SOFTWARE IS IN NO WAY AFFILIATED WITH THE LOYALTY LAB ORGANIZATION.
|
data/README.rdoc
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
= Loyalty Lab SDK
|
2
|
+
|
3
|
+
A ruby library for accessing the Loyalty Lab API documented here: http://api.loyaltylab.com/loyaltyapi/help/index.html
|
4
|
+
|
5
|
+
This library attempts to be as transparent as possible by making all method calls, parameters, and objects identical to those documented at the above URL. Capitalization goes against typical ruby conventions, and is instead as documented by Loyalty Lab.
|
6
|
+
|
7
|
+
NOTE: This software is in no way affiliated with the Loyalty Lab organization.
|
8
|
+
|
9
|
+
== Synopsis
|
10
|
+
|
11
|
+
LoyaltyLabSDK.config(:username => 'foo', :password => 'bar')
|
12
|
+
|
13
|
+
api = LoyaltyLabSDK::LoyaltyAPI.new
|
14
|
+
shopper = api.GetShopper 'shopperId' => 1234
|
15
|
+
shopper_id = shopper['ShopperId']
|
16
|
+
point_balance = api.AdjustShopperPoints 'shopperId' => shopper_id,
|
17
|
+
'pointChange' => 1000,
|
18
|
+
'pointType' => 'Base',
|
19
|
+
'description' => 'Surprise bonus'
|
20
|
+
|
21
|
+
new_retailer_shopper_id = 1234
|
22
|
+
new_shopper = api.build_default_shopper(new_retailer_shopper_id)
|
23
|
+
new_shopper['EmailAddress'] = 'test@gmail.com'
|
24
|
+
new_shopper['FirstName'] = 'Joe'
|
25
|
+
new_shopper['LastName'] = 'Schmoe'
|
26
|
+
new_card = api.build_default_card(new_retailer_shopper_id)
|
27
|
+
api.CreateShopperWithCard 'shopper' => new_shopper, 'card' => new_card
|
28
|
+
|
29
|
+
== Authentication
|
30
|
+
|
31
|
+
By default, a new LoyaltyAPI instance will authenticate itself against the API (using #authenticate!) and store the authentication token when it is constructed. Authentication may be skipped by setting the :lazy_authentication option to true when calling the constructor. In this case, authentication will occur when the first API call is made (or it may be done explicitly).
|
32
|
+
|
33
|
+
According to Loyalty Lab, the authentication token will expire after 20 minutes of inactivity. By default, LoyaltyAPI has an option :allow_reauthenticate that defaults to true (may be overridden). If it's true, then when you receive an AuthenticationError while making an API call on an already-authenticated LoyaltyAPI instance, it will try once to re-authenticate automatically. This behavior could be overridden by the client by setting :allow_reauthenticate to false and then either ignoring the error, pro-actively calling #authenticate!, or catching AuthenticationError exceptions and re-authenticating then.
|
34
|
+
|
35
|
+
== Timeouts
|
36
|
+
|
37
|
+
By default, all requests will timeout (for opening connections and making requests) after 15 seconds. This may be overridden by setting the :open_timeout and :read_timeout options when calling LoyaltyLabSDK.config.
|
38
|
+
|
39
|
+
== Environment Variables
|
40
|
+
|
41
|
+
For convenience in a command-line environment, configuration of username and password may be skipped by setting the LOYALTY_LAB_SDK_USERNAME and LOYALTY_LAB_SDK_PASSWORD environment variables.
|
42
|
+
|
43
|
+
== Rails
|
44
|
+
|
45
|
+
If running in a rails environment, this configuration will automatically use the global Rails.logger instance. This behavior may be overridden by passing in a :logger option to LoyaltyLabSDK.config.
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module LoyaltyLabSDK
|
4
|
+
|
5
|
+
DEFAULT_TIMEOUT = 15
|
6
|
+
DEFAULT_RETRIES = 2
|
7
|
+
|
8
|
+
# Globally configures and retrieves configuration for the Loyalty Lab SDK.
|
9
|
+
#
|
10
|
+
# == Environment Variables
|
11
|
+
#
|
12
|
+
# For convenience in a command-line environment, configuration may be skipped
|
13
|
+
# by setting the LOYALTY_LAB_SDK_USERNAME and LOYALTY_LAB_SDK_PASSWORD
|
14
|
+
# environment variables, which are self-explanatory.
|
15
|
+
#
|
16
|
+
# == Rails
|
17
|
+
#
|
18
|
+
# If running in a rails environment, this configuration will automatically use
|
19
|
+
# the global Rails.logger instance. This behavior may be overridden by passing
|
20
|
+
# in a :logger option.
|
21
|
+
#
|
22
|
+
# @param [Hash] options
|
23
|
+
# @option options [String] :username (nil) Loyalty Lab account username
|
24
|
+
# @option options [String] :password (nil) Loyalty Lab account password
|
25
|
+
# @option options [Logger] :logger (Rails.logger) Logger to use
|
26
|
+
# @option options [Numeric] :open_timeout (LoyaltyLabSDK::DEFAULT_TIMEOUT)
|
27
|
+
# Number of seconds to wait for the connection to open
|
28
|
+
# (see Net::HTTP#open_timeout)
|
29
|
+
# @option options [Numeric] :read_timeout (LoyaltyLabSDK::DEFAULT_TIMEOUT)
|
30
|
+
# Number of seconds to wait for one block to be read
|
31
|
+
# (see Net::HTTP#read_timeout)
|
32
|
+
# @option options [Integer] :connection_error_retries
|
33
|
+
# (LoyaltyLabSDK::DEFAULT_RETRIES) Number of retries that will be attempted
|
34
|
+
# if a connection error (timeout) occurs
|
35
|
+
def self.config(options = nil)
|
36
|
+
@config ||= {
|
37
|
+
:username => ENV['LOYALTY_LAB_SDK_USERNAME'],
|
38
|
+
:password => ENV['LOYALTY_LAB_SDK_PASSWORD'],
|
39
|
+
:logger => default_logger,
|
40
|
+
:open_timeout => DEFAULT_TIMEOUT,
|
41
|
+
:read_timeout => DEFAULT_TIMEOUT,
|
42
|
+
:connection_error_retries => DEFAULT_RETRIES,
|
43
|
+
}
|
44
|
+
|
45
|
+
@config.merge!(options) if options
|
46
|
+
|
47
|
+
@config
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def self.default_logger
|
53
|
+
if defined?(::Rails)
|
54
|
+
::Rails.logger
|
55
|
+
else
|
56
|
+
logger = ::Logger.new(STDERR)
|
57
|
+
logger.level = ::Logger::INFO
|
58
|
+
logger
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module LoyaltyLabSDK
|
2
|
+
|
3
|
+
# An abstract super-class of all other LoyaltyLabApi Error classes
|
4
|
+
class Error < StandardError; end
|
5
|
+
|
6
|
+
# Indicates an error establishing a connection to the API, or a timeout that occurs while
|
7
|
+
# making an API call. Is relatively common and transient- worth retrying in important cases.
|
8
|
+
class ConnectionError < Error; end
|
9
|
+
|
10
|
+
# Indicates an error authenticating with the provided credentials.
|
11
|
+
class AuthenticationError < Error; end
|
12
|
+
|
13
|
+
# Indicates any other API error (generally should be non-transient and not worth retrying). The
|
14
|
+
# error code and string will be present in this error's message if one was provided in the
|
15
|
+
# response.
|
16
|
+
class UnknownError < Error; end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,329 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'loyalty_lab_sdk/exceptions'
|
3
|
+
require 'active_support/inflector'
|
4
|
+
require 'savon'
|
5
|
+
|
6
|
+
module LoyaltyLabSDK
|
7
|
+
class LoyaltyAPI
|
8
|
+
|
9
|
+
ENDPOINT = 'https://api.loyaltylab.com/loyaltyapi/loyaltyapi.asmx'
|
10
|
+
NAMESPACE = 'http://www.loyaltylab.com/loyaltyapi/'
|
11
|
+
|
12
|
+
# Constructs an object-oriented API to loyalty lab.
|
13
|
+
#
|
14
|
+
# In addition to the options specified below, any of the options documented
|
15
|
+
# in LoyaltyLabSDK#config may be overridden.
|
16
|
+
#
|
17
|
+
# It will establish a connection, authenticate the username/password, and store
|
18
|
+
# the authentication data upon construction unless the lazy_authentication
|
19
|
+
# parameter is set. This authentication token times out after 20 minutes
|
20
|
+
# (double-check Loyalty Lab's documentation), so a client wishing to use this
|
21
|
+
# object should accommodate that, and re-call #authenticate! if necessary.
|
22
|
+
#
|
23
|
+
# @param [Hash] options
|
24
|
+
# @option options [boolean] :lazy_authentication (false) Delays authentication
|
25
|
+
# until either the first call is made or it is done explicitly.
|
26
|
+
# @option options [boolean] :allow_reauthenticate (true) If true, will
|
27
|
+
# attempt to re-authenticate the client once automatically if an
|
28
|
+
# AuthenticationError is thrown (on any call).
|
29
|
+
def initialize(options = {})
|
30
|
+
self.config = {
|
31
|
+
:lazy_authentication => false,
|
32
|
+
:allow_reauthenticate => true,
|
33
|
+
}.merge!(LoyaltyLabSDK.config).merge!(options)
|
34
|
+
|
35
|
+
Savon.configure do |c|
|
36
|
+
c.logger = config[:logger]
|
37
|
+
c.raise_errors = false
|
38
|
+
end
|
39
|
+
|
40
|
+
initialize_client
|
41
|
+
|
42
|
+
@authenticated = false
|
43
|
+
|
44
|
+
unless config[:lazy_authentication]
|
45
|
+
authenticate!
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Authenticates the client.
|
50
|
+
#
|
51
|
+
# This method is called by the constructor unless lazy_authentication is
|
52
|
+
# on, so this will not typically need to be invoked directly.
|
53
|
+
#
|
54
|
+
# An object that hasn't been called in 20 minutes will have its
|
55
|
+
# authentication headers expired by Loyalty Lab, requiring this method to
|
56
|
+
# be invoked again on long-lived objects.
|
57
|
+
def authenticate!
|
58
|
+
response = self.AuthenticateUser(
|
59
|
+
{ :username => config[:username], :password => config[:password] },
|
60
|
+
:is_authenticate => true,
|
61
|
+
:allow_reauthenticate => false)
|
62
|
+
|
63
|
+
auth_data = {
|
64
|
+
:retailer_guid => response['RetailerGuid'],
|
65
|
+
:authenticated => response['Authenticated'],
|
66
|
+
:token => response['Token'],
|
67
|
+
:ics_user_id => response['ICSUserID']
|
68
|
+
}
|
69
|
+
|
70
|
+
if !auth_data[:authenticated]
|
71
|
+
raise AuthenticationError, 'authentication failed'
|
72
|
+
end
|
73
|
+
|
74
|
+
self.retailer_guid = auth_data[:retailer_guid]
|
75
|
+
|
76
|
+
self.auth_header = {
|
77
|
+
"wsdl:AuthenticationResult" => {
|
78
|
+
"wsdl:RetailerGuid" => auth_data[:retailer_guid],
|
79
|
+
"wsdl:Authenticated" => auth_data[:authenticated],
|
80
|
+
"wsdl:Token" => auth_data[:token],
|
81
|
+
"wsdl:ICSUserID" => auth_data[:ics_user_id],
|
82
|
+
}
|
83
|
+
}
|
84
|
+
|
85
|
+
@authenticated = true
|
86
|
+
end
|
87
|
+
|
88
|
+
# Implements most API calls. The API method should be passed as defined in Loyalty
|
89
|
+
# Lab's API documentation here: http://api.loyaltylab.com/loyaltyapi/help/index.html
|
90
|
+
#
|
91
|
+
# Each of the parameters should be passed in a hash as documented.
|
92
|
+
#
|
93
|
+
# Responses will be in the documented format (a "string" return value with respond
|
94
|
+
# with a string, etc...), or if it's an object (such as a Shopper object), the
|
95
|
+
# result will be a hash with string keys for object
|
96
|
+
#
|
97
|
+
# == Examples:
|
98
|
+
#
|
99
|
+
# === Retrieve a shopper
|
100
|
+
# shopper = api.GetShopperByEmail 'email' => 'test@gmail.com'
|
101
|
+
#
|
102
|
+
# === Adjust shopper point balance
|
103
|
+
# point_balance = api.AdjustShopperPoints 'shopperId' => shopper['ShopperId'],
|
104
|
+
# 'pointChange' => 1000,
|
105
|
+
# 'pointType' => 'Base',
|
106
|
+
# 'description' => 'Bonus'
|
107
|
+
#
|
108
|
+
# === Create a shopper with card
|
109
|
+
# new_shopper = api.build_default_shopper(1234)
|
110
|
+
# new_shopper['EmailAddress'] = 'test2@gmail.com'
|
111
|
+
# new_shopper['FirstName'] = 'Joe'
|
112
|
+
# new_shopper['LastName'] = 'Schmoe'
|
113
|
+
# new_card = api.build_default_card(1234)
|
114
|
+
# api.CreateShopperWithCard 'shopper' => new_shopper, 'card' => new_card
|
115
|
+
def method_missing(method_name, *args)
|
116
|
+
call_api_method(method_name, *args)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Initializes and returns a shopper object with default fields set for use with a
|
120
|
+
# CreateShopper call.
|
121
|
+
#
|
122
|
+
# This object should be updated with all relevant fields (ie. email address, first
|
123
|
+
# name, phone number, etc) before being saved.
|
124
|
+
def build_default_shopper(retailer_shopper_id)
|
125
|
+
now = DateTime.now
|
126
|
+
|
127
|
+
{
|
128
|
+
'ShopperId' => 0,
|
129
|
+
'RetailerGUID' => retailer_guid,
|
130
|
+
'EmailAddress' => '',
|
131
|
+
'EmailFrequency' => 1,
|
132
|
+
'EmailFrequencyUnit' => 'D',
|
133
|
+
'EmailFormat' => 'HTML',
|
134
|
+
'Password' => ' ',
|
135
|
+
'Status' => 'A',
|
136
|
+
'LastName' => '',
|
137
|
+
'MiddleInitial' => '',
|
138
|
+
'FirstName' => '',
|
139
|
+
'Address1' => '',
|
140
|
+
'City' => '',
|
141
|
+
'State' => '',
|
142
|
+
'Zip' => '',
|
143
|
+
'PhoneNumber' => '',
|
144
|
+
'ProfileCreateDateTime' => now,
|
145
|
+
'ProfileUpdateDateTime' => now,
|
146
|
+
'CreateDateTime' => now,
|
147
|
+
'PasswordLastChanged' => now,
|
148
|
+
'Origin' => 'W',
|
149
|
+
'RetailerShopperId' => retailer_shopper_id.to_s,
|
150
|
+
'FileImportId' => 0,
|
151
|
+
'BulkEmail' => 1,
|
152
|
+
'LoyaltyMember' => true,
|
153
|
+
'PersonStatus' => 'P',
|
154
|
+
'RetailerRegistered' => false,
|
155
|
+
'MailOptIn' => false,
|
156
|
+
'PhoneOptIn' => false,
|
157
|
+
'RetailerShopperCreationDate' => now,
|
158
|
+
'LoyaltyLabCreateDateTime' => now,
|
159
|
+
'StatusUpdateDateTime' => now
|
160
|
+
}
|
161
|
+
end
|
162
|
+
|
163
|
+
# Initializes and returns a card object with default fields set for use with a
|
164
|
+
# CreateShopper call.
|
165
|
+
#
|
166
|
+
# This object should be updated with all relevant fields before being saved (if
|
167
|
+
# necessary).
|
168
|
+
def build_default_card(retailer_shopper_id)
|
169
|
+
now = DateTime.now
|
170
|
+
|
171
|
+
{
|
172
|
+
'RegisteredCardId' => 0,
|
173
|
+
'ShopperId' => 0,
|
174
|
+
'CommonName' => 'loyalty member id',
|
175
|
+
'AlternateCardIdentifier' => retailer_shopper_id.to_s,
|
176
|
+
'CardType' => 'L',
|
177
|
+
'ExpirationMonth' => 12,
|
178
|
+
'ExpirationYear' => 3010,
|
179
|
+
'LastFour' => ' ',
|
180
|
+
'CardHolderName' => ' ',
|
181
|
+
'Status' => 'A',
|
182
|
+
'CreateDateTime' => now,
|
183
|
+
'IsPreferred' => ' ',
|
184
|
+
'FileImportId' => 0
|
185
|
+
}
|
186
|
+
end
|
187
|
+
|
188
|
+
private
|
189
|
+
|
190
|
+
ERROR_HANDLERS = {
|
191
|
+
'100' => AuthenticationError
|
192
|
+
}
|
193
|
+
|
194
|
+
SHOPPER_FIELD_MAPPINGS = {
|
195
|
+
:retailer_guid => 'RetailerGUID'
|
196
|
+
}
|
197
|
+
|
198
|
+
FIELD_MAPPING = {
|
199
|
+
'AuthenticateUser' => {
|
200
|
+
:ics_user_id => 'ICSUserID'
|
201
|
+
},
|
202
|
+
'GetShopperByID' => SHOPPER_FIELD_MAPPINGS,
|
203
|
+
'GetShopperByEmail' => SHOPPER_FIELD_MAPPINGS,
|
204
|
+
'GetShopperByRetailerID' => SHOPPER_FIELD_MAPPINGS
|
205
|
+
}
|
206
|
+
|
207
|
+
attr_accessor :config, :client, :retailer_guid, :auth_header
|
208
|
+
|
209
|
+
def initialize_client
|
210
|
+
self.client = ::Savon::Client.new do
|
211
|
+
wsdl.endpoint = ENDPOINT
|
212
|
+
wsdl.namespace = NAMESPACE
|
213
|
+
http.open_timeout = config[:open_timeout]
|
214
|
+
http.read_timeout = config[:read_timeout]
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def call_api_method(method_name, request_body = nil, options = {})
|
219
|
+
options = {
|
220
|
+
:is_authenticate => false,
|
221
|
+
:connection_error_retries => config[:connection_error_retries],
|
222
|
+
:allow_reauthenticate => config[:allow_reauthenticate],
|
223
|
+
}.merge(options)
|
224
|
+
|
225
|
+
if !@authenticated && !options[:is_authenticate]
|
226
|
+
authenticate!
|
227
|
+
end
|
228
|
+
|
229
|
+
method_name = method_name.to_s
|
230
|
+
|
231
|
+
request_body ||= {}
|
232
|
+
|
233
|
+
prepared_request_body = prepare_request(request_body)
|
234
|
+
|
235
|
+
begin
|
236
|
+
response = client.request "wsdl:#{method_name}" do |soap, wsdl, http, wsse|
|
237
|
+
http.headers["SOAPAction"] = "http://www.loyaltylab.com/loyaltyapi/#{method_name}"
|
238
|
+
soap.header = auth_header unless options[:is_authenticate]
|
239
|
+
soap.body = prepared_request_body
|
240
|
+
end
|
241
|
+
rescue Exception => e
|
242
|
+
config[:logger].debug "communication exception during request: #{e.message}"
|
243
|
+
if options[:connection_error_retries] > 0
|
244
|
+
config[:logger].debug "#{options[:connection_error_retries]} retry attempt(s) remaining; retrying..."
|
245
|
+
options[:connection_error_retries] -= 1
|
246
|
+
return call_api_method(method_name, request_body, options)
|
247
|
+
else
|
248
|
+
config[:logger].debug 'no retry attempts remaining'
|
249
|
+
raise ConnectionError, e.message
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
begin
|
254
|
+
check_response_for_errors(response)
|
255
|
+
rescue AuthenticationError => e
|
256
|
+
if options[:allow_reauthenticate]
|
257
|
+
authenticate!
|
258
|
+
options[:allow_reauthenticate] = false
|
259
|
+
return call_api_method(method_name, request_body, options)
|
260
|
+
else
|
261
|
+
raise e
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
modified_method_name = method_name.underscore
|
266
|
+
|
267
|
+
response = response.to_hash["#{modified_method_name}_response".to_sym]
|
268
|
+
response = response["#{modified_method_name}_result".to_sym] unless response.nil?
|
269
|
+
|
270
|
+
normalize_response method_name, response
|
271
|
+
end
|
272
|
+
|
273
|
+
# Returns a request hash with proper namespacing prefixes and transformations applied
|
274
|
+
# to the given request hash.
|
275
|
+
def prepare_request(request_hash = {}, nested_object = false)
|
276
|
+
result = {}
|
277
|
+
request_hash.each do |key, value|
|
278
|
+
# handle nested objects in the request (like a shopper)
|
279
|
+
value = prepare_request(value, true) if value.instance_of?(Hash)
|
280
|
+
|
281
|
+
# add the wsdl: prefix to the key
|
282
|
+
result["wsdl:#{key}"] = value
|
283
|
+
end
|
284
|
+
result
|
285
|
+
end
|
286
|
+
|
287
|
+
# Takes a raw response and checks it for errors, throwing proper exceptions if
|
288
|
+
# present.
|
289
|
+
def check_response_for_errors(response)
|
290
|
+
# take no action if there are no errors
|
291
|
+
return unless response.soap_fault? || response.http_error?
|
292
|
+
|
293
|
+
response_hash = response.to_hash
|
294
|
+
if response_hash[:fault] && response_hash[:fault][:detail]
|
295
|
+
# we can parse the response, so check the specific error code
|
296
|
+
error_code = response_hash[:fault][:detail][:code]
|
297
|
+
|
298
|
+
# if we can recognize the specific code, dispatch the proper exception
|
299
|
+
if ERROR_HANDLERS.has_key? error_code
|
300
|
+
raise ERROR_HANDLERS[error_code], response_hash[:fault][:faultstring]
|
301
|
+
end
|
302
|
+
|
303
|
+
# otherwise return a well-defined error string with the code and error message
|
304
|
+
raise UnknownError, "#{response_hash[:fault][:detail][:code]} #{response_hash[:fault][:detail][:description]}: #{response_hash[:fault][:faultstring]}"
|
305
|
+
end
|
306
|
+
|
307
|
+
# we can't parse the error, so just pass back the entire response body
|
308
|
+
raise UnknownError, response.to_s
|
309
|
+
end
|
310
|
+
|
311
|
+
def normalize_response(method_name, response, mapping = FIELD_MAPPING[method_name])
|
312
|
+
if response.instance_of?(Hash)
|
313
|
+
mapping ||= {}
|
314
|
+
response.inject({}) do |hash, (key, value)|
|
315
|
+
new_key = mapping[key] || key.to_s.camelize
|
316
|
+
hash[new_key] = normalize_response(nil, value, nil) # TODO: implement nested mappings
|
317
|
+
hash
|
318
|
+
end
|
319
|
+
elsif response.instance_of?(Array)
|
320
|
+
response.collect do |value|
|
321
|
+
normalize_response(nil, value, nil) # TODO: implement nested mappings
|
322
|
+
end
|
323
|
+
else
|
324
|
+
response
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
end
|
329
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'httpi'
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
# prevents debug log messages that clutter output
|
5
|
+
HTTPI.log = false
|
6
|
+
|
7
|
+
# prevents warning
|
8
|
+
class Net::HTTP
|
9
|
+
alias_method :old_initialize, :initialize
|
10
|
+
def initialize(*args)
|
11
|
+
old_initialize(*args)
|
12
|
+
@ssl_context = OpenSSL::SSL::SSLContext.new
|
13
|
+
@ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
require 'loyalty_lab_sdk/config'
|
18
|
+
require 'loyalty_lab_sdk/exceptions'
|
19
|
+
|
20
|
+
module LoyaltyLabSDK
|
21
|
+
|
22
|
+
autoload :LoyaltyAPI, 'loyalty_lab_sdk/loyalty_api'
|
23
|
+
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: loyalty_lab_sdk
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- David Dawson
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-01-09 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activesupport
|
16
|
+
requirement: &70125150677680 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '3.0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70125150677680
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: httpi
|
27
|
+
requirement: &70125150676260 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0.9'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70125150676260
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: i18n
|
38
|
+
requirement: &70125150674620 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0.6'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70125150674620
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: savon
|
49
|
+
requirement: &70125150673640 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.9'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70125150673640
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: guid
|
60
|
+
requirement: &70125150672220 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ~>
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0.1'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *70125150672220
|
69
|
+
description: Full documentation of the API is at http://api.loyaltylab.com/loyaltyapi/help/index.html.
|
70
|
+
email: david@stashrewards.com
|
71
|
+
executables: []
|
72
|
+
extensions: []
|
73
|
+
extra_rdoc_files:
|
74
|
+
- README.rdoc
|
75
|
+
- CHANGELOG.rdoc
|
76
|
+
- LICENSE.txt
|
77
|
+
files:
|
78
|
+
- lib/loyalty_lab_sdk/config.rb
|
79
|
+
- lib/loyalty_lab_sdk/exceptions.rb
|
80
|
+
- lib/loyalty_lab_sdk/loyalty_api.rb
|
81
|
+
- lib/loyalty_lab_sdk/version.rb
|
82
|
+
- lib/loyalty_lab_sdk.rb
|
83
|
+
- README.rdoc
|
84
|
+
- CHANGELOG.rdoc
|
85
|
+
- LICENSE.txt
|
86
|
+
homepage: http://www.stashrewards.com
|
87
|
+
licenses: []
|
88
|
+
post_install_message:
|
89
|
+
rdoc_options:
|
90
|
+
- --main
|
91
|
+
- README.rdoc
|
92
|
+
require_paths:
|
93
|
+
- lib
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ! '>='
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
segments:
|
101
|
+
- 0
|
102
|
+
hash: -3780691586861817189
|
103
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
105
|
+
requirements:
|
106
|
+
- - ! '>='
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
requirements: []
|
110
|
+
rubyforge_project:
|
111
|
+
rubygems_version: 1.8.10
|
112
|
+
signing_key:
|
113
|
+
specification_version: 3
|
114
|
+
summary: Library for accessing the Loyalty Lab API.
|
115
|
+
test_files: []
|