forked from campaignmonitor/createsend-ruby
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcreatesend.rb
289 lines (258 loc) · 9.79 KB
/
createsend.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
require 'cgi'
require 'uri'
require 'httparty'
require 'hashie'
require 'json'
module CreateSend
USER_AGENT_STRING = "createsend-ruby-#{VERSION}-#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}-#{RUBY_PLATFORM}"
# Represents a CreateSend API error. Contains specific data about the error.
class CreateSendError < StandardError
attr_reader :data
def initialize(data)
@data = data
# @data should contain Code, Message and optionally ResultData
extra = @data.ResultData ? "\nExtra result data: #{@data.ResultData}" : ""
super "The CreateSend API responded with the following error"\
" - #{@data.Code}: #{@data.Message}#{extra}"
end
end
# Raised for HTTP response codes of 400...500
class ClientError < StandardError; end
# Raised for HTTP response codes of 500...600
class ServerError < StandardError; end
# Raised for HTTP response code of 400
class BadRequest < CreateSendError; end
# Raised for HTTP response code of 401
class Unauthorized < CreateSendError; end
# Raised for HTTP response code of 404
class NotFound < ClientError; end
# Raised for HTTP response code of 401, specifically when an OAuth token
# in invalid (Code: 120, Message: 'Invalid OAuth Token')
class InvalidOAuthToken < Unauthorized; end
# Raised for HTTP response code of 401, specifically when an OAuth token
# has expired (Code: 121, Message: 'Expired OAuth Token')
class ExpiredOAuthToken < Unauthorized; end
# Raised for HTTP response code of 401, specifically when an OAuth token
# has been revoked (Code: 122, Message: 'Revoked OAuth Token')
class RevokedOAuthToken < Unauthorized; end
# Provides high level CreateSend functionality/data you'll probably need.
class CreateSend
include HTTParty
attr_reader :auth_details
# Specify cert authority file for cert validation
ssl_ca_file File.expand_path(File.join(File.dirname(__FILE__), 'cacert.pem'))
# Set a custom user agent string to be used when instances of
# CreateSend::CreateSend make API calls.
#
# user_agent - The user agent string to use in the User-Agent header when
# instances of this class make API calls. If set to nil, the
# default value of CreateSend::USER_AGENT_STRING will be used.
def self.user_agent(user_agent)
headers({'User-Agent' => user_agent || USER_AGENT_STRING})
end
# Get the authorization URL for your application, given the application's
# client_id, redirect_uri, scope, and optional state data.
def self.authorize_url(client_id, redirect_uri, scope, state=nil)
qs = "client_id=#{CGI.escape(client_id.to_s)}"
qs << "&redirect_uri=#{CGI.escape(redirect_uri.to_s)}"
qs << "&scope=#{CGI.escape(scope.to_s)}"
qs << "&state=#{CGI.escape(state.to_s)}" if state
"#{@@oauth_base_uri}?#{qs}"
end
# Exchange a provided OAuth code for an OAuth access token, 'expires in'
# value, and refresh token.
def self.exchange_token(client_id, client_secret, redirect_uri, code)
body = "grant_type=authorization_code"
body << "&client_id=#{CGI.escape(client_id.to_s)}"
body << "&client_secret=#{CGI.escape(client_secret.to_s)}"
body << "&redirect_uri=#{CGI.escape(redirect_uri.to_s)}"
body << "&code=#{CGI.escape(code.to_s)}"
options = {:body => body}
response = HTTParty.post(@@oauth_token_uri, options)
if response.has_key? 'error' and response.has_key? 'error_description'
err = "Error exchanging code for access token: "
err << "#{response['error']} - #{response['error_description']}"
raise err
end
r = Hashie::Mash.new(response)
[r.access_token, r.expires_in, r.refresh_token]
end
# Refresh an OAuth access token, given an OAuth refresh token.
# Returns a new access token, 'expires in' value, and refresh token.
def self.refresh_access_token(refresh_token)
options = {
:body => "grant_type=refresh_token&refresh_token=#{CGI.escape(refresh_token)}" }
response = HTTParty.post(@@oauth_token_uri, options)
if response.has_key? 'error' and response.has_key? 'error_description'
err = "Error refreshing access token: "
err << "#{response['error']} - #{response['error_description']}"
raise err
end
r = Hashie::Mash.new(response)
[r.access_token, r.expires_in, r.refresh_token]
end
def initialize(*args)
if args.size > 0
auth args.first # Expect auth details as first argument
end
end
@@base_uri = "https://api.createsend.com/api/v3.1"
@@oauth_base_uri = "https://api.createsend.com/oauth"
@@oauth_token_uri = "#{@@oauth_base_uri}/token"
headers({
'User-Agent' => USER_AGENT_STRING,
'Content-Type' => 'application/json; charset=utf-8',
'Accept-Encoding' => 'gzip, deflate' })
base_uri @@base_uri
# Authenticate using either OAuth or an API key.
def auth(auth_details)
@auth_details = auth_details
end
# Refresh the current OAuth token using the current refresh token.
def refresh_token
if not @auth_details or
not @auth_details.has_key? :refresh_token or
not @auth_details[:refresh_token]
raise '@auth_details[:refresh_token] does not contain a refresh token.'
end
access_token, expires_in, refresh_token =
self.class.refresh_access_token @auth_details[:refresh_token]
auth({
:access_token => access_token,
:refresh_token => refresh_token})
[access_token, expires_in, refresh_token]
end
# Gets your clients.
def clients
response = get('/clients.json')
response.map{|item| Hashie::Mash.new(item)}
end
# Get your billing details.
def billing_details
response = get('/billingdetails.json')
Hashie::Mash.new(response)
end
# Gets valid countries.
def countries
response = get('/countries.json')
response.parsed_response
end
# Gets the current date in your account's timezone.
def systemdate
response = get('/systemdate.json')
Hashie::Mash.new(response)
end
# Gets valid timezones.
def timezones
response = get('/timezones.json')
response.parsed_response
end
# Gets the administrators for the account.
def administrators
response = get('/admins.json')
response.map{|item| Hashie::Mash.new(item)}
end
# Gets the primary contact for the account.
def get_primary_contact
response = get('/primarycontact.json')
Hashie::Mash.new(response)
end
# Set the primary contect for the account.
def set_primary_contact(email)
options = { :query => { :email => email } }
response = put("/primarycontact.json", options)
Hashie::Mash.new(response)
end
# Get a URL which initiates a new external session for the user with the
# given email.
# Full details: http://www.campaignmonitor.com/api/account/#single_sign_on
#
# email - The email address of the Campaign Monitor user for whom
# the login session should be created.
# chrome - Which 'chrome' to display - Must be either "all",
# "tabs", or "none".
# url - The URL to display once logged in. e.g. "/subscribers/"
# integrator_id - The integrator ID. You need to contact Campaign Monitor
# support to get an integrator ID.
# client_id - The Client ID of the client which should be active once
# logged in to the Campaign Monitor account.
#
# Returns An object containing a single field SessionUrl which represents
# the URL to initiate the external Campaign Monitor session.
def external_session_url(email, chrome, url, integrator_id, client_id)
options = { :body => {
:Email => email,
:Chrome => chrome,
:Url => url,
:IntegratorID => integrator_id,
:ClientID => client_id }.to_json }
response = put("/externalsession.json", options)
Hashie::Mash.new(response)
end
def get(*args)
args = add_auth_details_to_options(args)
handle_response CreateSend.get(*args)
end
alias_method :cs_get, :get
def post(*args)
args = add_auth_details_to_options(args)
handle_response CreateSend.post(*args)
end
alias_method :cs_post, :post
def put(*args)
args = add_auth_details_to_options(args)
handle_response CreateSend.put(*args)
end
alias_method :cs_put, :put
def delete(*args)
args = add_auth_details_to_options(args)
handle_response CreateSend.delete(*args)
end
alias_method :cs_delete, :delete
def add_auth_details_to_options(args)
if @auth_details
options = {}
if args.size > 1
options = args[1]
end
if @auth_details.has_key? :access_token
options[:headers] = {
"Authorization" => "Bearer #{@auth_details[:access_token]}" }
elsif @auth_details.has_key? :api_key
if not options.has_key? :basic_auth
options[:basic_auth] = {
:username => @auth_details[:api_key], :password => 'x' }
end
end
args[1] = options
end
args
end
def handle_response(response) # :nodoc:
case response.code
when 400
raise BadRequest.new(Hashie::Mash.new response)
when 401
data = Hashie::Mash.new(response)
case data.Code
when 120
raise InvalidOAuthToken.new data
when 121
raise ExpiredOAuthToken.new data
when 122
raise RevokedOAuthToken.new data
else
raise Unauthorized.new data
end
when 404
raise NotFound.new
when 400...500
raise ClientError.new
when 500...600
raise ServerError.new
else
response
end
end
end
end