sailthru-client 1.01 → 1.02
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.
- data/README.md +2 -2
- data/lib/sailthru.rb +401 -346
- metadata +18 -4
data/README.md
CHANGED
data/lib/sailthru.rb
CHANGED
@@ -1,38 +1,3 @@
|
|
1
|
-
################################################################################
|
2
|
-
#
|
3
|
-
# A simple client library to remotely access the Sailthru REST API.
|
4
|
-
#
|
5
|
-
################################################################################
|
6
|
-
#
|
7
|
-
# Copyright (c) 2007 Sailthru, Inc.
|
8
|
-
# All rights reserved.
|
9
|
-
#
|
10
|
-
# Special thanks to the iminlikewithyou.com team for the development
|
11
|
-
# of this library.
|
12
|
-
#
|
13
|
-
# Redistribution and use in source and binary forms, with or without
|
14
|
-
# modification, are permitted provided that the following conditions
|
15
|
-
# are met:
|
16
|
-
#
|
17
|
-
# 1. Redistributions of source code must retain the above copyright
|
18
|
-
# notice, this list of conditions and the following disclaimer.
|
19
|
-
# 2. Redistributions in binary form must reproduce the above copyright
|
20
|
-
# notice, this list of conditions and the following disclaimer in the
|
21
|
-
# documentation and/or other materials provided with the distribution.
|
22
|
-
#
|
23
|
-
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
24
|
-
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
25
|
-
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
26
|
-
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
27
|
-
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
28
|
-
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
29
|
-
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
30
|
-
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
31
|
-
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
32
|
-
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
33
|
-
#
|
34
|
-
################################################################################
|
35
|
-
|
36
1
|
require 'net/http'
|
37
2
|
require 'uri'
|
38
3
|
require 'cgi'
|
@@ -40,335 +5,425 @@ require 'rubygems'
|
|
40
5
|
require 'json'
|
41
6
|
require 'md5'
|
42
7
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
VERSION = '1.01'
|
50
|
-
|
51
|
-
# params:
|
52
|
-
# api_key, String
|
53
|
-
# secret, String
|
54
|
-
# api_uri, String
|
55
|
-
#
|
56
|
-
# Instantiate a new client; constructor optionally takes overrides for key/secret/uri.
|
57
|
-
def initialize(api_key, secret, api_uri)
|
58
|
-
@api_key = api_key
|
59
|
-
@secret = secret
|
60
|
-
@api_uri = api_uri
|
8
|
+
module Sailthru
|
9
|
+
|
10
|
+
Version = VERSION = '1.02'
|
11
|
+
|
12
|
+
class SailthruClientException < Exception
|
61
13
|
end
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
14
|
+
|
15
|
+
module Helpers
|
16
|
+
# params:
|
17
|
+
# params, Hash
|
18
|
+
# returns:
|
19
|
+
# Array, values of each item in the Hash (and nested hashes)
|
20
|
+
#
|
21
|
+
# Extracts the values of a set of parameters, recursing into nested assoc arrays.
|
22
|
+
def extract_param_values(params)
|
23
|
+
values = []
|
24
|
+
params.each do |k, v|
|
25
|
+
if v.class == Hash
|
26
|
+
values.concat extract_param_values(v)
|
27
|
+
elsif v.class == Array
|
28
|
+
temp_hash = Hash.new()
|
29
|
+
v.each_with_index do |v_,i_|
|
30
|
+
temp_hash[i_.to_s] = v_
|
31
|
+
end
|
32
|
+
values.concat extract_param_values(temp_hash)
|
33
|
+
else
|
34
|
+
values.push v.to_s
|
35
|
+
end
|
36
|
+
end
|
37
|
+
return values
|
80
38
|
end
|
81
|
-
return self.api_post(:send, post)
|
82
|
-
end
|
83
39
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
# params:
|
144
|
-
# email, String
|
145
|
-
# vars, Hash
|
146
|
-
# lists, Hash mapping list name => 1 for subscribed, 0 for unsubscribed
|
147
|
-
# returns:
|
148
|
-
# Hash, response data from server
|
149
|
-
#
|
150
|
-
# Set replacement vars and/or list subscriptions for an email address.
|
151
|
-
def set_email(email, vars = {}, lists = {}, templates = {})
|
152
|
-
data = {:email => email}
|
153
|
-
data[:vars] = vars unless vars.empty?
|
154
|
-
data[:lists] = lists unless lists.empty?
|
155
|
-
data[:templates] = templates unless templates.empty?
|
156
|
-
self.api_post(:email, data)
|
157
|
-
end
|
158
|
-
|
159
|
-
# params:
|
160
|
-
# email, String
|
161
|
-
# password, String
|
162
|
-
# with_names, Boolean
|
163
|
-
# returns:
|
164
|
-
# Hash, response data from server
|
165
|
-
#
|
166
|
-
# Fetch email contacts from an address book at one of the major email providers (aol/gmail/hotmail/yahoo)
|
167
|
-
# Use the with_names parameter if you want to fetch the contact names as well as emails
|
168
|
-
def import_contacts(email, password, with_names = false)
|
169
|
-
data = { :email => email, :password => password }
|
170
|
-
data[:names] = 1 if with_names
|
171
|
-
self.api_post(:contacts, data)
|
172
|
-
end
|
173
|
-
|
174
|
-
|
175
|
-
# params:
|
176
|
-
# template_name, String
|
177
|
-
# returns:
|
178
|
-
# Hash of response data.
|
179
|
-
#
|
180
|
-
# Get a template.
|
181
|
-
def get_template(template_name)
|
182
|
-
self.api_get(:template, {:template => template_name})
|
183
|
-
end
|
184
|
-
|
185
|
-
|
186
|
-
# params:
|
187
|
-
# template_name, String
|
188
|
-
# template_fields, Hash
|
189
|
-
# returns:
|
190
|
-
# Hash containg response from the server.
|
191
|
-
#
|
192
|
-
# Save a template.
|
193
|
-
def save_template(template_name, template_fields)
|
194
|
-
data = template_fields
|
195
|
-
data[:template] = template_name
|
196
|
-
self.api_post(:template, data)
|
197
|
-
end
|
198
|
-
|
199
|
-
|
200
|
-
# params:
|
201
|
-
# params, Hash
|
202
|
-
# request, String
|
203
|
-
# returns:
|
204
|
-
# TrueClass or FalseClass, Returns true if the incoming request is an authenticated verify post.
|
205
|
-
def receive_verify_post(params, request)
|
206
|
-
if request.post?
|
207
|
-
[:action, :email, :send_id, :sig].each { |key| return false unless params.has_key?(key) }
|
208
|
-
|
209
|
-
return false unless params[:action] == :verify
|
210
|
-
|
211
|
-
sig = params[:sig]
|
212
|
-
params.delete(:sig)
|
213
|
-
return false unless sig == SailthruClient.get_signature_hash(params, @secret)
|
214
|
-
|
215
|
-
_send = self.get_send(params[:send_id])
|
216
|
-
return false unless _send.has_key?(:email)
|
217
|
-
|
218
|
-
return false unless _send[:email] == params[:email]
|
219
|
-
|
220
|
-
return true
|
221
|
-
else
|
40
|
+
# params:
|
41
|
+
# params, Hash
|
42
|
+
# secret, String
|
43
|
+
# returns:
|
44
|
+
# String
|
45
|
+
#
|
46
|
+
# Returns the unhashed signature string (secret + sorted list of param values) for an API call.
|
47
|
+
def get_signature_string(params, secret)
|
48
|
+
return secret + extract_param_values(params).sort.join("")
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
# params:
|
53
|
+
# params, Hash
|
54
|
+
# secret, String
|
55
|
+
# returns:
|
56
|
+
# String
|
57
|
+
#
|
58
|
+
# Returns an MD5 hash of the signature string for an API call.
|
59
|
+
def get_signature_hash(params, secret)
|
60
|
+
MD5.md5(get_signature_string(params, secret)).to_s # debuggin
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
# Flatten nested hash for GET / POST request.
|
65
|
+
def flatten_nested_hash(hash, brackets = true)
|
66
|
+
f = {}
|
67
|
+
hash.each do |key, value|
|
68
|
+
_key = brackets ? "[#{key}]" : key.to_s
|
69
|
+
if value.class == Hash
|
70
|
+
flatten_nested_hash(value).each do |k, v|
|
71
|
+
f["#{_key}#{k}"] = v
|
72
|
+
end
|
73
|
+
elsif value.class == Array
|
74
|
+
temp_hash = Hash.new()
|
75
|
+
value.each_with_index do |v, i|
|
76
|
+
temp_hash[i.to_s] = v
|
77
|
+
end
|
78
|
+
flatten_nested_hash(temp_hash).each do |k, v|
|
79
|
+
f["#{_key}#{k}"] = v
|
80
|
+
end
|
81
|
+
|
82
|
+
else
|
83
|
+
f[_key] = value
|
84
|
+
end
|
85
|
+
end
|
86
|
+
return f
|
87
|
+
end
|
88
|
+
|
89
|
+
def verify_purchase_items (items)
|
90
|
+
if items.class == Array and !items.empty?
|
91
|
+
required_item_fields = ['qty', 'title', 'price', 'id', 'url'].sort
|
92
|
+
items.each do |v|
|
93
|
+
keys = v.keys.sort
|
94
|
+
return false if keys != required_item_fields
|
95
|
+
end
|
96
|
+
return true
|
97
|
+
end
|
222
98
|
return false
|
223
99
|
end
|
224
100
|
end
|
225
|
-
|
226
|
-
|
227
|
-
# Perform API GET request
|
228
|
-
def api_get(action, data)
|
229
|
-
api_request(action, data, 'GET')
|
230
|
-
end
|
231
|
-
|
232
|
-
# Perform API POST request
|
233
|
-
def api_post(action, data)
|
234
|
-
api_request(action, data, 'POST')
|
235
|
-
end
|
236
|
-
|
237
|
-
# params:
|
238
|
-
# action, String
|
239
|
-
# data, Hash
|
240
|
-
# request, String "GET" or "POST"
|
241
|
-
# returns:
|
242
|
-
# Hash
|
243
|
-
#
|
244
|
-
# Perform an API request, using the shared-secret auth hash.
|
245
|
-
#
|
246
|
-
def api_request(action, data, request_type)
|
247
|
-
data[:api_key] = @api_key
|
248
|
-
data[:format] ||= 'json'
|
249
|
-
data[:sig] = SailthruClient.get_signature_hash(data, @secret)
|
250
|
-
_result = self.http_request("#{@api_uri}/#{action}", data, request_type)
|
251
|
-
|
252
101
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
# puts data.inspect
|
268
|
-
if method == 'POST'
|
269
|
-
post_data = data
|
270
|
-
else
|
271
|
-
uri += "?" + data.map{ |key, value| "#{CGI::escape(key)}=#{CGI::escape(value)}" }.join("&")
|
102
|
+
class SailthruClient
|
103
|
+
|
104
|
+
include Helpers
|
105
|
+
|
106
|
+
# params:
|
107
|
+
# api_key, String
|
108
|
+
# secret, String
|
109
|
+
# api_uri, String
|
110
|
+
#
|
111
|
+
# Instantiate a new client; constructor optionally takes overrides for key/secret/uri.
|
112
|
+
def initialize(api_key, secret, api_uri)
|
113
|
+
@api_key = api_key
|
114
|
+
@secret = secret
|
115
|
+
@api_uri = api_uri
|
272
116
|
end
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
117
|
+
|
118
|
+
# params:
|
119
|
+
# template_name, String
|
120
|
+
# email, String
|
121
|
+
# replacements, Hash
|
122
|
+
# options, Hash
|
123
|
+
# replyto: override Reply-To header
|
124
|
+
# test: send as test email (subject line will be marked, will not count towards stats)
|
125
|
+
# returns:
|
126
|
+
# Hash, response data from server
|
127
|
+
def send(template_name, email, vars={}, options = {}, schedule_time = nil)
|
128
|
+
post = {}
|
129
|
+
post[:template] = template_name
|
130
|
+
post[:email] = email
|
131
|
+
post[:options] = options
|
132
|
+
|
133
|
+
if vars.length > 0
|
134
|
+
post[:vars] = vars
|
135
|
+
end
|
136
|
+
|
137
|
+
if schedule_time != nil
|
138
|
+
post[:schedule_time] = schedule_time
|
139
|
+
end
|
140
|
+
return self.api_post(:send, post)
|
282
141
|
end
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
142
|
+
|
143
|
+
|
144
|
+
def multi_send(template_name, emails, vars={}, options = {}, schedule_time = nil)
|
145
|
+
post = {}
|
146
|
+
post[:template] = template_name
|
147
|
+
post[:email] = emails
|
148
|
+
post[:options] = options
|
149
|
+
|
150
|
+
if schedule_time != nil
|
151
|
+
post[:schedule_time] = schedule_time
|
152
|
+
end
|
153
|
+
|
154
|
+
if vars.length > 0
|
155
|
+
post[:vars] = vars
|
156
|
+
end
|
157
|
+
|
158
|
+
return self.api_post(:send, post)
|
291
159
|
end
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
160
|
+
|
161
|
+
|
162
|
+
# params:
|
163
|
+
# send_id, Fixnum
|
164
|
+
# returns:
|
165
|
+
# Hash, response data from server
|
166
|
+
#
|
167
|
+
# Get the status of a send.
|
168
|
+
def get_send(send_id)
|
169
|
+
self.api_get(:send, {:send_id => send_id.to_s})
|
297
170
|
end
|
171
|
+
|
298
172
|
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
173
|
+
def cancel_send(send_id)
|
174
|
+
self.api_delete(:send, {:send_id => send_id.to_s})
|
175
|
+
end
|
176
|
+
|
177
|
+
# params:
|
178
|
+
# name, String
|
179
|
+
# list, String
|
180
|
+
# schedule_time, String
|
181
|
+
# from_name, String
|
182
|
+
# from_email, String
|
183
|
+
# subject, String
|
184
|
+
# content_html, String
|
185
|
+
# content_text, String
|
186
|
+
# options, Hash
|
187
|
+
# returns:
|
188
|
+
# Hash, response data from server
|
189
|
+
#
|
190
|
+
# Schedule a mass mail blast
|
191
|
+
def schedule_blast(name, list, schedule_time, from_name, from_email, subject, content_html, content_text, options = {})
|
192
|
+
post = options ? options : {}
|
193
|
+
post[:name] = name
|
194
|
+
post[:list] = list
|
195
|
+
post[:schedule_time] = schedule_time
|
196
|
+
post[:from_name] = from_name
|
197
|
+
post[:from_email] = from_email
|
198
|
+
post[:subject] = subject
|
199
|
+
post[:content_html] = content_html
|
200
|
+
post[:content_text] = content_text
|
201
|
+
self.api_post(:blast, post)
|
202
|
+
end
|
203
|
+
|
204
|
+
|
205
|
+
# params:
|
206
|
+
# blast_id, Fixnum
|
207
|
+
# returns:
|
208
|
+
# Hash, response data from server
|
209
|
+
#
|
210
|
+
# Get information on a previously scheduled email blast
|
211
|
+
def get_blast(blast_id)
|
212
|
+
self.api_get(:blast, {:blast_id => blast_id.to_s})
|
213
|
+
end
|
214
|
+
|
215
|
+
# params:
|
216
|
+
# email, String
|
217
|
+
# returns:
|
218
|
+
# Hash, response data from server
|
219
|
+
#
|
220
|
+
# Return information about an email address, including replacement vars and lists.
|
221
|
+
def get_email(email)
|
222
|
+
self.api_get(:email, {:email => email})
|
223
|
+
end
|
224
|
+
|
225
|
+
# params:
|
226
|
+
# email, String
|
227
|
+
# vars, Hash
|
228
|
+
# lists, Hash mapping list name => 1 for subscribed, 0 for unsubscribed
|
229
|
+
# returns:
|
230
|
+
# Hash, response data from server
|
231
|
+
#
|
232
|
+
# Set replacement vars and/or list subscriptions for an email address.
|
233
|
+
def set_email(email, vars = {}, lists = {}, templates = {})
|
234
|
+
data = {:email => email}
|
235
|
+
data[:vars] = vars unless vars.empty?
|
236
|
+
data[:lists] = lists unless lists.empty?
|
237
|
+
data[:templates] = templates unless templates.empty?
|
238
|
+
self.api_post(:email, data)
|
239
|
+
end
|
240
|
+
|
241
|
+
# params:
|
242
|
+
# email, String
|
243
|
+
# password, String
|
244
|
+
# with_names, Boolean
|
245
|
+
# returns:
|
246
|
+
# Hash, response data from server
|
247
|
+
#
|
248
|
+
# Fetch email contacts from an address book at one of the major email providers (aol/gmail/hotmail/yahoo)
|
249
|
+
# Use the with_names parameter if you want to fetch the contact names as well as emails
|
250
|
+
def import_contacts(email, password, with_names = false)
|
251
|
+
data = { :email => email, :password => password }
|
252
|
+
data[:names] = 1 if with_names
|
253
|
+
self.api_post(:contacts, data)
|
254
|
+
end
|
255
|
+
|
256
|
+
|
257
|
+
# params:
|
258
|
+
# template_name, String
|
259
|
+
# returns:
|
260
|
+
# Hash of response data.
|
261
|
+
#
|
262
|
+
# Get a template.
|
263
|
+
def get_template(template_name)
|
264
|
+
self.api_get(:template, {:template => template_name})
|
265
|
+
end
|
318
266
|
|
267
|
+
|
268
|
+
# params:
|
269
|
+
# template_name, String
|
270
|
+
# template_fields, Hash
|
271
|
+
# returns:
|
272
|
+
# Hash containg response from the server.
|
273
|
+
#
|
274
|
+
# Save a template.
|
275
|
+
def save_template(template_name, template_fields)
|
276
|
+
data = template_fields
|
277
|
+
data[:template] = template_name
|
278
|
+
self.api_post(:template, data)
|
279
|
+
end
|
280
|
+
|
281
|
+
|
282
|
+
# params:
|
283
|
+
# params, Hash
|
284
|
+
# request, String
|
285
|
+
# returns:
|
286
|
+
# TrueClass or FalseClass, Returns true if the incoming request is an authenticated verify post.
|
287
|
+
def receive_verify_post(params, request)
|
288
|
+
if request.post?
|
289
|
+
[:action, :email, :send_id, :sig].each { |key| return false unless params.has_key?(key) }
|
290
|
+
|
291
|
+
return false unless params[:action] == :verify
|
292
|
+
|
293
|
+
sig = params[:sig]
|
294
|
+
params.delete(:sig)
|
295
|
+
return false unless sig == get_signature_hash(params, @secret)
|
296
|
+
|
297
|
+
_send = self.get_send(params[:send_id])
|
298
|
+
return false unless _send.has_key?(:email)
|
299
|
+
|
300
|
+
return false unless _send[:email] == params[:email]
|
301
|
+
|
302
|
+
return true
|
319
303
|
else
|
320
|
-
|
304
|
+
return false
|
321
305
|
end
|
322
306
|
end
|
323
|
-
return f
|
324
|
-
end
|
325
307
|
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
values.push v.to_s()
|
308
|
+
# params:
|
309
|
+
# email, String
|
310
|
+
# items, String
|
311
|
+
# incomplete, Integer
|
312
|
+
# message_id, String
|
313
|
+
# returns:
|
314
|
+
# hash, response from server
|
315
|
+
#
|
316
|
+
# Record that a user has made a purchase, or has added items to their purchase total.
|
317
|
+
def purchase(email, items, incomplete = nil, message_id = nil)
|
318
|
+
data = {}
|
319
|
+
data[:email] = email
|
320
|
+
|
321
|
+
if verify_purchase_items(items)
|
322
|
+
data[:items] = items
|
323
|
+
end
|
324
|
+
|
325
|
+
if incomplete != nil
|
326
|
+
data[:incomplete] = incomplete.to_i
|
346
327
|
end
|
328
|
+
|
329
|
+
if message_id != nil
|
330
|
+
data[:message_id] = message_id
|
331
|
+
end
|
332
|
+
api_post(:purchase, data)
|
347
333
|
end
|
348
|
-
return values
|
349
|
-
end
|
350
334
|
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
335
|
+
# params:
|
336
|
+
# stat, String
|
337
|
+
#
|
338
|
+
# returns:
|
339
|
+
# hash, response from server
|
340
|
+
# Request various stats from Sailthru.
|
341
|
+
def get_stats(stat)
|
342
|
+
api_get(:stats, {:stat => stat})
|
343
|
+
end
|
344
|
+
|
345
|
+
|
346
|
+
protected
|
347
|
+
|
348
|
+
# Perform API GET request
|
349
|
+
def api_get(action, data)
|
350
|
+
api_request(action, data, 'GET')
|
351
|
+
end
|
352
|
+
|
353
|
+
# Perform API POST request
|
354
|
+
def api_post(action, data)
|
355
|
+
api_request(action, data, 'POST')
|
356
|
+
end
|
357
|
+
|
358
|
+
#Perform API DELETE request
|
359
|
+
def api_delete(action, data)
|
360
|
+
api_request(action, data, 'DELETE')
|
361
|
+
end
|
362
|
+
|
363
|
+
# params:
|
364
|
+
# action, String
|
365
|
+
# data, Hash
|
366
|
+
# request, String "GET" or "POST"
|
367
|
+
# returns:
|
368
|
+
# Hash
|
369
|
+
#
|
370
|
+
# Perform an API request, using the shared-secret auth hash.
|
371
|
+
#
|
372
|
+
def api_request(action, data, request_type)
|
373
|
+
data[:api_key] = @api_key
|
374
|
+
data[:format] ||= 'json'
|
375
|
+
data[:sig] = get_signature_hash(data, @secret)
|
376
|
+
_result = self.http_request("#{@api_uri}/#{action}", data, request_type)
|
377
|
+
|
378
|
+
|
379
|
+
# NOTE: don't do the unserialize here
|
380
|
+
unserialized = JSON.parse(_result)
|
381
|
+
return unserialized ? unserialized : _result
|
382
|
+
end
|
383
|
+
|
384
|
+
|
385
|
+
# params:
|
386
|
+
# uri, String
|
387
|
+
# data, Hash
|
388
|
+
# method, String "GET" or "POST"
|
389
|
+
# returns:
|
390
|
+
# String, body of response
|
391
|
+
def http_request(uri, data, method = 'POST')
|
392
|
+
data = flatten_nested_hash(data, false)
|
393
|
+
if method == 'POST'
|
394
|
+
post_data = data
|
395
|
+
else
|
396
|
+
uri += "?" + data.map{ |key, value| "#{CGI::escape(key.to_s)}=#{CGI::escape(value.to_s)}" }.join("&")
|
397
|
+
end
|
398
|
+
req = nil
|
399
|
+
headers = {"User-Agent" => "Sailthru API Ruby Client #{VERSION}"}
|
400
|
+
|
401
|
+
_uri = URI.parse(uri)
|
402
|
+
if method == 'POST'
|
403
|
+
req = Net::HTTP::Post.new(_uri.path, headers)
|
404
|
+
req.set_form_data(data)
|
405
|
+
else
|
406
|
+
request_uri = "#{_uri.path}?#{_uri.query}"
|
407
|
+
if method == 'DELETE'
|
408
|
+
req = Net::HTTP::Delete.new(request_uri, headers)
|
409
|
+
else
|
410
|
+
req = Net::HTTP::Get.new(request_uri, headers)
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
begin
|
415
|
+
response = Net::HTTP.start(_uri.host, _uri.port) {|http|
|
416
|
+
http.request(req)
|
417
|
+
}
|
418
|
+
rescue Exception => e
|
419
|
+
raise SailthruClientException.new("Unable to open stream: #{_uri.to_s}");
|
420
|
+
end
|
421
|
+
|
422
|
+
if response.body
|
423
|
+
return response.body
|
424
|
+
else
|
425
|
+
raise SailthruClientException.new("No response received from stream: #{_uri.to_s}")
|
426
|
+
end
|
427
|
+
end
|
372
428
|
end
|
373
|
-
|
374
|
-
end
|
429
|
+
end
|
metadata
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sailthru-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 11
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: "1.
|
8
|
+
- 2
|
9
|
+
version: "1.02"
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Prajwal Tuladhar
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-01-
|
17
|
+
date: 2011-01-25 00:00:00 -05:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -31,6 +31,20 @@ dependencies:
|
|
31
31
|
version: "0"
|
32
32
|
type: :runtime
|
33
33
|
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: fakeweb
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
hash: 3
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
version: "0"
|
46
|
+
type: :development
|
47
|
+
version_requirements: *id002
|
34
48
|
description:
|
35
49
|
email: praj@sailthru.com
|
36
50
|
executables: []
|