mailgunner 1.0.0

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 ADDED
@@ -0,0 +1,45 @@
1
+ A Ruby wrapper for the [Mailgun API](http://documentation.mailgun.net/api_reference.html)
2
+ =========================================================================================
3
+
4
+
5
+ Quick Start
6
+ -----------
7
+
8
+ ```ruby
9
+ require 'mailgunner'
10
+
11
+ mailgun = Mailgunner::Client.new(domain: 'samples.mailgun.org', api_key: 'key-3ax6xnjp29jd6fds4gc373sgvjxteol0')
12
+
13
+ response = mailgun.get_stats(limit: 5)
14
+
15
+ if response.ok?
16
+ # do something with response.object
17
+ else
18
+ # handle client/server error
19
+ end
20
+ ```
21
+
22
+
23
+ Alternative JSON Implementations
24
+ --------------------------------
25
+
26
+ Mailgunner::Client defaults to using the "json" library which is available
27
+ in the Ruby 1.9 standard library, and as a gem for Ruby 1.8. You can specify
28
+ an alternative implementation using the json option when constructing a client
29
+ object. For example, to use [multi_json](https://rubygems.org/gems/multi_json):
30
+
31
+ ```ruby
32
+ mailgun = Mailgunner::Client.new(:json => MultiJson)
33
+ ```
34
+
35
+
36
+ Environment Variables
37
+ ---------------------
38
+
39
+ Best practice for storing credentials for external services is to use environment
40
+ variables, as described by [12factor.net/config](http://www.12factor.net/config).
41
+
42
+ Mailgunner::Client defaults to extracting the domain and api_key values it needs
43
+ from the MAILGUN_API_KEY and MAILGUN_SMTP_LOGIN environment variables. These will
44
+ exist if you are using Mailgun on Heroku, or you can set them manually.
45
+
data/lib/mailgunner.rb ADDED
@@ -0,0 +1,300 @@
1
+ require 'net/http'
2
+ require 'json'
3
+ require 'cgi'
4
+ require 'uri'
5
+
6
+ module Mailgunner
7
+ class Client
8
+ attr_accessor :domain, :api_key, :json, :http
9
+
10
+ def initialize(options = {})
11
+ @domain = options.fetch(:domain) { ENV.fetch('MAILGUN_SMTP_LOGIN').split('@').last }
12
+
13
+ @api_key = options.fetch(:api_key) { ENV.fetch('MAILGUN_API_KEY') }
14
+
15
+ @json = options.fetch(:json) { JSON }
16
+
17
+ @http = Net::HTTP.new('api.mailgun.net', Net::HTTP.https_default_port)
18
+
19
+ @http.use_ssl = true
20
+ end
21
+
22
+ def send_message(attributes = {})
23
+ post("/v2/#{escape @domain}/messages", attributes)
24
+ end
25
+
26
+ def get_domains(params = {})
27
+ get('/v2/domains', params)
28
+ end
29
+
30
+ def get_domain(name)
31
+ get("/v2/domains/#{escape name}")
32
+ end
33
+
34
+ def add_domain(attributes = {})
35
+ post('/v2/domains', attributes)
36
+ end
37
+
38
+ def get_unsubscribes(params = {})
39
+ get("/v2/#{escape @domain}/unsubscribes", params)
40
+ end
41
+
42
+ def get_unsubscribe(address)
43
+ get("/v2/#{escape @domain}/unsubscribes/#{escape address}")
44
+ end
45
+
46
+ def delete_unsubscribe(address_or_id)
47
+ delete("/v2/#{escape @domain}/unsubscribes/#{escape address_or_id}")
48
+ end
49
+
50
+ def add_unsubscribe(attributes = {})
51
+ post("/v2/#{escape @domain}/unsubscribes", attributes)
52
+ end
53
+
54
+ def get_complaints(params = {})
55
+ get("/v2/#{escape @domain}/complaints", params)
56
+ end
57
+
58
+ def get_complaint(address)
59
+ get("/v2/#{escape @domain}/complaints/#{escape address}")
60
+ end
61
+
62
+ def add_complaint(attributes = {})
63
+ post("/v2/#{escape @domain}/complaints", attributes)
64
+ end
65
+
66
+ def delete_complaint(address)
67
+ delete("/v2/#{escape @domain}/complaints/#{escape address}")
68
+ end
69
+
70
+ def get_bounces(params = {})
71
+ get("/v2/#{escape @domain}/bounces", params)
72
+ end
73
+
74
+ def get_bounce(address)
75
+ get("/v2/#{escape @domain}/bounces/#{escape address}")
76
+ end
77
+
78
+ def add_bounce(attributes = {})
79
+ post("/v2/#{escape @domain}/bounces", attributes)
80
+ end
81
+
82
+ def delete_bounce(address)
83
+ delete("/v2/#{escape @domain}/bounces/#{escape address}")
84
+ end
85
+
86
+ def get_stats(params = {})
87
+ get("/v2/#{escape @domain}/stats", params)
88
+ end
89
+
90
+ def get_log(params = {})
91
+ get("/v2/#{escape @domain}/log", params)
92
+ end
93
+
94
+ def get_routes(params = {})
95
+ get('/v2/routes', params)
96
+ end
97
+
98
+ def get_route(id)
99
+ get("/v2/routes/#{escape id}")
100
+ end
101
+
102
+ def add_route(attributes = {})
103
+ post('/v2/routes', attributes)
104
+ end
105
+
106
+ def update_route(id, attributes = {})
107
+ put("/v2/routes/#{escape id}", attributes)
108
+ end
109
+
110
+ def delete_route(id)
111
+ delete("/v2/routes/#{escape id}")
112
+ end
113
+
114
+ def get_mailboxes(params = {})
115
+ get("/v2/#{escape @domain}/mailboxes", params)
116
+ end
117
+
118
+ def add_mailbox(attributes = {})
119
+ post("/v2/#{escape @domain}/mailboxes", attributes)
120
+ end
121
+
122
+ def update_mailbox(name, attributes = {})
123
+ put("/v2/#{escape @domain}/mailboxes/#{escape name}", attributes)
124
+ end
125
+
126
+ def delete_mailbox(name)
127
+ delete("/v2/#{escape @domain}/mailboxes/#{escape name}")
128
+ end
129
+
130
+ def get_campaigns(params = {})
131
+ get("/v2/#{escape @domain}/campaigns", params)
132
+ end
133
+
134
+ def get_campaign(id)
135
+ get("/v2/#{escape @domain}/campaigns/#{escape id}")
136
+ end
137
+
138
+ def add_campaign(attributes = {})
139
+ post("/v2/#{escape @domain}/campaigns", attributes)
140
+ end
141
+
142
+ def update_campaign(id, attributes = {})
143
+ put("/v2/#{escape @domain}/campaigns/#{escape id}", attributes)
144
+ end
145
+
146
+ def delete_campaign(id)
147
+ delete("/v2/#{escape @domain}/campaigns/#{escape id}")
148
+ end
149
+
150
+ def get_campaign_events(campaign_id, params = {})
151
+ get("/v2/#{escape @domain}/campaigns/#{escape campaign_id}/events", params)
152
+ end
153
+
154
+ def get_campaign_stats(campaign_id, params = {})
155
+ get("/v2/#{escape @domain}/campaigns/#{escape campaign_id}/stats", params)
156
+ end
157
+
158
+ def get_campaign_clicks(campaign_id, params = {})
159
+ get("/v2/#{escape @domain}/campaigns/#{escape campaign_id}/clicks", params)
160
+ end
161
+
162
+ def get_campaign_opens(campaign_id, params = {})
163
+ get("/v2/#{escape @domain}/campaigns/#{escape campaign_id}/opens", params)
164
+ end
165
+
166
+ def get_campaign_unsubscribes(campaign_id, params = {})
167
+ get("/v2/#{escape @domain}/campaigns/#{escape campaign_id}/unsubscribes", params)
168
+ end
169
+
170
+ def get_campaign_complaints(campaign_id, params = {})
171
+ get("/v2/#{escape @domain}/campaigns/#{escape campaign_id}/complaints", params)
172
+ end
173
+
174
+ def get_lists(params = {})
175
+ get('/v2/lists', params)
176
+ end
177
+
178
+ def get_list(address)
179
+ get("/v2/lists/#{escape address}")
180
+ end
181
+
182
+ def add_list(attributes = {})
183
+ post('/v2/lists', attributes)
184
+ end
185
+
186
+ def update_list(address, attributes = {})
187
+ put("/v2/lists/#{escape address}", attributes)
188
+ end
189
+
190
+ def delete_list(address)
191
+ delete("/v2/lists/#{escape address}")
192
+ end
193
+
194
+ def get_list_members(list_address, params = {})
195
+ get("/v2/lists/#{escape list_address}/members", params)
196
+ end
197
+
198
+ def get_list_member(list_address, member_address)
199
+ get("/v2/lists/#{escape list_address}/members/#{escape member_address}")
200
+ end
201
+
202
+ def add_list_member(list_address, member_attributes)
203
+ post("/v2/lists/#{escape list_address}/members", member_attributes)
204
+ end
205
+
206
+ def update_list_member(list_address, member_address, member_attributes)
207
+ put("/v2/lists/#{escape list_address}/members/#{escape member_address}", member_attributes)
208
+ end
209
+
210
+ def delete_list_member(list_address, member_address)
211
+ delete("/v2/lists/#{escape list_address}/members/#{escape member_address}")
212
+ end
213
+
214
+ def get_list_stats(list_address)
215
+ get("/v2/lists/#{escape list_address}/stats")
216
+ end
217
+
218
+ private
219
+
220
+ def get(path, params = {})
221
+ transmit(Net::HTTP::Get, request_uri(path, params))
222
+ end
223
+
224
+ def post(path, attributes = {})
225
+ transmit(Net::HTTP::Post, path, attributes)
226
+ end
227
+
228
+ def put(path, attributes = {})
229
+ transmit(Net::HTTP::Put, path, attributes)
230
+ end
231
+
232
+ def delete(path)
233
+ transmit(Net::HTTP::Delete, path)
234
+ end
235
+
236
+ def transmit(subclass, path, attributes = nil)
237
+ message = subclass.new(path)
238
+ message.basic_auth('api', @api_key)
239
+ message.body = URI.encode_www_form(attributes) if attributes
240
+
241
+ Response.new(@http.request(message), :json => @json)
242
+ end
243
+
244
+ def request_uri(path, params_hash)
245
+ if params_hash.empty?
246
+ path
247
+ else
248
+ tmp = []
249
+
250
+ params_hash.each do |key, values|
251
+ Array(values).each do |value|
252
+ tmp << "#{escape(key)}=#{escape(value)}"
253
+ end
254
+ end
255
+
256
+ path + '?' + tmp.join('&')
257
+ end
258
+ end
259
+
260
+ def escape(component)
261
+ CGI.escape(component.to_s)
262
+ end
263
+ end
264
+
265
+ class Response
266
+ def initialize(http_response, options = {})
267
+ @http_response = http_response
268
+
269
+ @json = options.fetch(:json) { JSON }
270
+ end
271
+
272
+ def method_missing(name, *args, &block)
273
+ @http_response.send(name, *args, &block)
274
+ end
275
+
276
+ def respond_to_missing?(name, include_private = false)
277
+ @http_response.respond_to?(name)
278
+ end
279
+
280
+ def ok?
281
+ code.to_i == 200
282
+ end
283
+
284
+ def client_error?
285
+ (400 .. 499).include?(code.to_i)
286
+ end
287
+
288
+ def server_error?
289
+ (500 .. 599).include?(code.to_i)
290
+ end
291
+
292
+ def json?
293
+ self['Content-Type'].split(';').first == 'application/json'
294
+ end
295
+
296
+ def object
297
+ @object ||= @json.load(body)
298
+ end
299
+ end
300
+ end
@@ -0,0 +1,14 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'mailgunner'
3
+ s.version = '1.0.0'
4
+ s.platform = Gem::Platform::RUBY
5
+ s.authors = ['Tim Craft']
6
+ s.email = ['mail@timcraft.com']
7
+ s.homepage = 'http://github.com/timcraft/mailgunner'
8
+ s.description = 'A Ruby wrapper for the Mailgun API'
9
+ s.summary = 'See description'
10
+ s.files = Dir.glob('{lib,spec}/**/*') + %w(README.md mailgunner.gemspec)
11
+ s.add_development_dependency('rake', '>= 0.9.3')
12
+ s.add_development_dependency('mocha', '~> 0.10.3')
13
+ s.require_path = 'lib'
14
+ end
@@ -0,0 +1,763 @@
1
+ require 'minitest/autorun'
2
+ require 'mailgunner'
3
+ require 'mocha'
4
+
5
+ class Net::HTTPGenericRequest
6
+ def inspect
7
+ if request_body_permitted?
8
+ "<#{self.class.name} #{path} #{body}>"
9
+ else
10
+ "<#{self.class.name} #{path}>"
11
+ end
12
+ end
13
+ end
14
+
15
+ describe 'Mailgunner::Client' do
16
+ before do
17
+ @domain = 'samples.mailgun.org'
18
+
19
+ @api_key = 'xxx'
20
+
21
+ @client = Mailgunner::Client.new(domain: @domain, api_key: @api_key)
22
+
23
+ @address = 'user@example.com'
24
+
25
+ @encoded_address = 'user%40example.com'
26
+ end
27
+
28
+ def expect(request_class, arg)
29
+ matcher = String === arg ? responds_with(:path, arg) : arg
30
+
31
+ @client.http.expects(:request).with(all_of(instance_of(request_class), matcher)).returns(stub)
32
+ end
33
+
34
+ describe 'http method' do
35
+ it 'returns a net http object that uses ssl' do
36
+ @client.http.must_be_instance_of(Net::HTTP)
37
+
38
+ @client.http.use_ssl?.must_equal(true)
39
+ end
40
+ end
41
+
42
+ describe 'domain method' do
43
+ it 'returns the value passed to the constructor' do
44
+ @client.domain.must_equal(@domain)
45
+ end
46
+
47
+ it 'defaults to the domain in the MAILGUN_SMTP_LOGIN environment variable' do
48
+ ENV['MAILGUN_SMTP_LOGIN'] = 'postmaster@samples.mailgun.org'
49
+
50
+ Mailgunner::Client.new(api_key: @api_key).domain.must_equal(@domain)
51
+ end
52
+ end
53
+
54
+ describe 'api_key method' do
55
+ it 'returns the value passed to the constructor' do
56
+ @client.api_key.must_equal(@api_key)
57
+ end
58
+
59
+ it 'defaults to the value of MAILGUN_API_KEY environment variable' do
60
+ ENV['MAILGUN_API_KEY'] = @api_key
61
+
62
+ Mailgunner::Client.new(domain: @domain).api_key.must_equal(@api_key)
63
+ end
64
+ end
65
+
66
+ describe 'json method' do
67
+ it 'returns the value passed to the constructor' do
68
+ json = stub
69
+
70
+ Mailgunner::Client.new(json: json).json.must_equal(json)
71
+ end
72
+
73
+ it 'defaults to the standard library json implementation' do
74
+ @client.json.must_equal(JSON)
75
+ end
76
+ end
77
+
78
+ describe 'send_message method' do
79
+ it 'posts to the domain messages resource and returns a response object' do
80
+ expect(Net::HTTP::Post, "/v2/#@domain/messages")
81
+
82
+ @client.send_message({}).must_be_instance_of(Mailgunner::Response)
83
+ end
84
+
85
+ it 'encodes the message attributes' do
86
+ expect(Net::HTTP::Post, responds_with(:body, "to=#@encoded_address"))
87
+
88
+ @client.add_domain({to: @address})
89
+ end
90
+
91
+ it 'encodes the message attributes as multipart form data when sending attachments' do
92
+ # TODO
93
+ end
94
+ end
95
+
96
+ describe 'get_domains method' do
97
+ it 'fetches the domains resource and returns a response object' do
98
+ expect(Net::HTTP::Get, '/v2/domains')
99
+
100
+ @client.get_domains.must_be_instance_of(Mailgunner::Response)
101
+ end
102
+ end
103
+
104
+ describe 'get_domain method' do
105
+ it 'fetches the domain resource and returns a response object' do
106
+ expect(Net::HTTP::Get, "/v2/domains/#@domain")
107
+
108
+ @client.get_domain(@domain).must_be_instance_of(Mailgunner::Response)
109
+ end
110
+ end
111
+
112
+ describe 'add_domain method' do
113
+ it 'posts to the domains resource and returns a response object' do
114
+ expect(Net::HTTP::Post, '/v2/domains')
115
+
116
+ @client.add_domain({}).must_be_instance_of(Mailgunner::Response)
117
+ end
118
+
119
+ it 'encodes the domain attributes' do
120
+ expect(Net::HTTP::Post, responds_with(:body, "name=#@domain"))
121
+
122
+ @client.add_domain({name: @domain})
123
+ end
124
+ end
125
+
126
+ describe 'get_unsubscribes method' do
127
+ it 'fetches the domain unsubscribes resource and returns a response object' do
128
+ expect(Net::HTTP::Get, "/v2/#@domain/unsubscribes")
129
+
130
+ @client.get_unsubscribes.must_be_instance_of(Mailgunner::Response)
131
+ end
132
+
133
+ it 'encodes skip and limit parameters' do
134
+ expect(Net::HTTP::Get, "/v2/#@domain/unsubscribes?skip=1&limit=2")
135
+
136
+ @client.get_unsubscribes(skip: 1, limit: 2)
137
+ end
138
+ end
139
+
140
+ describe 'get_unsubscribe method' do
141
+ it 'fetches the unsubscribe resource with the given address and returns a response object' do
142
+ expect(Net::HTTP::Get, "/v2/#@domain/unsubscribes/#@encoded_address")
143
+
144
+ @client.get_unsubscribe(@address).must_be_instance_of(Mailgunner::Response)
145
+ end
146
+ end
147
+
148
+ describe 'delete_unsubscribe method' do
149
+ it 'deletes the domain unsubscribe resource with the given address and returns a response object' do
150
+ expect(Net::HTTP::Delete, "/v2/#@domain/unsubscribes/#@encoded_address")
151
+
152
+ @client.delete_unsubscribe(@address).must_be_instance_of(Mailgunner::Response)
153
+ end
154
+ end
155
+
156
+ describe 'add_unsubscribe method' do
157
+ it 'posts to the domain unsubscribes resource and returns a response object' do
158
+ expect(Net::HTTP::Post, "/v2/#@domain/unsubscribes")
159
+
160
+ @client.add_unsubscribe({}).must_be_instance_of(Mailgunner::Response)
161
+ end
162
+
163
+ it 'encodes the unsubscribe attributes' do
164
+ expect(Net::HTTP::Post, responds_with(:body, "address=#@encoded_address"))
165
+
166
+ @client.add_unsubscribe({address: @address})
167
+ end
168
+ end
169
+
170
+ describe 'get_complaints method' do
171
+ it 'fetches the domain complaints resource and returns a response object' do
172
+ expect(Net::HTTP::Get, "/v2/#@domain/complaints")
173
+
174
+ @client.get_complaints.must_be_instance_of(Mailgunner::Response)
175
+ end
176
+
177
+ it 'encodes skip and limit parameters' do
178
+ expect(Net::HTTP::Get, "/v2/#@domain/complaints?skip=1&limit=2")
179
+
180
+ @client.get_complaints(skip: 1, limit: 2)
181
+ end
182
+ end
183
+
184
+ describe 'get_complaint method' do
185
+ it 'fetches the complaint resource with the given address and returns a response object' do
186
+ expect(Net::HTTP::Get, "/v2/#@domain/complaints/#@encoded_address")
187
+
188
+ @client.get_complaint(@address).must_be_instance_of(Mailgunner::Response)
189
+ end
190
+ end
191
+
192
+ describe 'add_complaint method' do
193
+ it 'posts to the domain complaints resource and returns a response object' do
194
+ expect(Net::HTTP::Post, "/v2/#@domain/complaints")
195
+
196
+ @client.add_complaint({}).must_be_instance_of(Mailgunner::Response)
197
+ end
198
+
199
+ it 'encodes the complaint attributes' do
200
+ expect(Net::HTTP::Post, responds_with(:body, "address=#@encoded_address"))
201
+
202
+ @client.add_complaint({address: @address})
203
+ end
204
+ end
205
+
206
+ describe 'delete_complaint method' do
207
+ it 'deletes the domain complaint resource with the given address and returns a response object' do
208
+ expect(Net::HTTP::Delete, "/v2/#@domain/complaints/#@encoded_address")
209
+
210
+ @client.delete_complaint(@address).must_be_instance_of(Mailgunner::Response)
211
+ end
212
+ end
213
+
214
+ describe 'get_bounces method' do
215
+ it 'fetches the domain bounces resource and returns a response object' do
216
+ expect(Net::HTTP::Get, "/v2/#@domain/bounces")
217
+
218
+ @client.get_bounces.must_be_instance_of(Mailgunner::Response)
219
+ end
220
+
221
+ it 'encodes skip and limit parameters' do
222
+ expect(Net::HTTP::Get, "/v2/#@domain/bounces?skip=1&limit=2")
223
+
224
+ @client.get_bounces(skip: 1, limit: 2)
225
+ end
226
+ end
227
+
228
+ describe 'get_bounce method' do
229
+ it 'fetches the bounce resource with the given address and returns a response object' do
230
+ expect(Net::HTTP::Get, "/v2/#@domain/bounces/#@encoded_address")
231
+
232
+ @client.get_bounce(@address).must_be_instance_of(Mailgunner::Response)
233
+ end
234
+ end
235
+
236
+ describe 'add_bounce method' do
237
+ it 'posts to the domain bounces resource and returns a response object' do
238
+ expect(Net::HTTP::Post, "/v2/#@domain/bounces")
239
+
240
+ @client.add_bounce({}).must_be_instance_of(Mailgunner::Response)
241
+ end
242
+
243
+ it 'encodes the bounce attributes' do
244
+ expect(Net::HTTP::Post, responds_with(:body, "address=#@encoded_address"))
245
+
246
+ @client.add_bounce({address: @address})
247
+ end
248
+ end
249
+
250
+ describe 'delete_bounce method' do
251
+ it 'deletes the domain bounce resource with the given address and returns a response object' do
252
+ expect(Net::HTTP::Delete, "/v2/#@domain/bounces/#@encoded_address")
253
+
254
+ @client.delete_bounce(@address).must_be_instance_of(Mailgunner::Response)
255
+ end
256
+ end
257
+
258
+ describe 'get_stats method' do
259
+ it 'fetches the domain stats resource and returns a response object' do
260
+ expect(Net::HTTP::Get, "/v2/#@domain/stats")
261
+
262
+ @client.get_stats.must_be_instance_of(Mailgunner::Response)
263
+ end
264
+
265
+ it 'encodes skip and limit parameters' do
266
+ expect(Net::HTTP::Get, "/v2/#@domain/stats?skip=1&limit=2")
267
+
268
+ @client.get_stats(skip: 1, limit: 2)
269
+ end
270
+
271
+ it 'encodes an event parameter with multiple values' do
272
+ expect(Net::HTTP::Get, "/v2/#@domain/stats?event=sent&event=opened")
273
+
274
+ @client.get_stats(event: %w(sent opened))
275
+ end
276
+ end
277
+
278
+ describe 'get_log method' do
279
+ it 'fetches the domain stats resource and returns a response object' do
280
+ expect(Net::HTTP::Get, "/v2/#@domain/log")
281
+
282
+ @client.get_log.must_be_instance_of(Mailgunner::Response)
283
+ end
284
+
285
+ it 'encodes skip and limit parameters' do
286
+ expect(Net::HTTP::Get, "/v2/#@domain/log?skip=1&limit=2")
287
+
288
+ @client.get_log(skip: 1, limit: 2)
289
+ end
290
+ end
291
+
292
+ describe 'get_routes method' do
293
+ it 'fetches the global routes resource and returns a response object' do
294
+ expect(Net::HTTP::Get, '/v2/routes')
295
+
296
+ @client.get_routes.must_be_instance_of(Mailgunner::Response)
297
+ end
298
+
299
+ it 'encodes skip and limit parameters' do
300
+ expect(Net::HTTP::Get, '/v2/routes?skip=1&limit=2')
301
+
302
+ @client.get_routes(skip: 1, limit: 2)
303
+ end
304
+ end
305
+
306
+ describe 'get_route method' do
307
+ it 'fetches the route resource with the given id and returns a response object' do
308
+ expect(Net::HTTP::Get, '/v2/routes/4f3bad2335335426750048c6')
309
+
310
+ @client.get_route('4f3bad2335335426750048c6').must_be_instance_of(Mailgunner::Response)
311
+ end
312
+ end
313
+
314
+ describe 'add_route method' do
315
+ it 'posts to the routes resource and returns a response object' do
316
+ expect(Net::HTTP::Post, '/v2/routes')
317
+
318
+ @client.add_route({}).must_be_instance_of(Mailgunner::Response)
319
+ end
320
+
321
+ it 'encodes the route attributes' do
322
+ expect(Net::HTTP::Post, responds_with(:body, 'description=Example+route&priority=1'))
323
+
324
+ @client.add_route({description: 'Example route', priority: 1})
325
+ end
326
+ end
327
+
328
+ describe 'update_route method' do
329
+ it 'updates the route resource with the given id and returns a response object' do
330
+ expect(Net::HTTP::Put, '/v2/routes/4f3bad2335335426750048c6')
331
+
332
+ @client.update_route('4f3bad2335335426750048c6', {}).must_be_instance_of(Mailgunner::Response)
333
+ end
334
+
335
+ it 'encodes the route attributes' do
336
+ expect(Net::HTTP::Put, responds_with(:body, 'priority=10'))
337
+
338
+ @client.update_route('4f3bad2335335426750048c6', {priority: 10})
339
+ end
340
+ end
341
+
342
+ describe 'delete_route method' do
343
+ it 'deletes the route resource with the given id and returns a response object' do
344
+ expect(Net::HTTP::Delete, '/v2/routes/4f3bad2335335426750048c6')
345
+
346
+ @client.delete_route('4f3bad2335335426750048c6').must_be_instance_of(Mailgunner::Response)
347
+ end
348
+ end
349
+
350
+ describe 'get_mailboxes method' do
351
+ it 'fetches the domain mailboxes resource and returns a response object' do
352
+ expect(Net::HTTP::Get, "/v2/#@domain/mailboxes")
353
+
354
+ @client.get_mailboxes.must_be_instance_of(Mailgunner::Response)
355
+ end
356
+
357
+ it 'encodes skip and limit parameters' do
358
+ expect(Net::HTTP::Get, "/v2/#@domain/mailboxes?skip=1&limit=2")
359
+
360
+ @client.get_mailboxes(skip: 1, limit: 2)
361
+ end
362
+ end
363
+
364
+ describe 'add_mailbox method' do
365
+ it 'posts to the domain mailboxes resource and returns a response object' do
366
+ expect(Net::HTTP::Post, "/v2/#@domain/mailboxes")
367
+
368
+ @client.add_mailbox({}).must_be_instance_of(Mailgunner::Response)
369
+ end
370
+
371
+ it 'encodes the mailbox attributes' do
372
+ expect(Net::HTTP::Post, responds_with(:body, 'mailbox=user'))
373
+
374
+ @client.add_mailbox({mailbox: 'user'})
375
+ end
376
+ end
377
+
378
+ describe 'update_mailbox method' do
379
+ it 'updates the mailbox resource and returns a response object' do
380
+ expect(Net::HTTP::Put, "/v2/#@domain/mailboxes/user")
381
+
382
+ @client.update_mailbox('user', {}).must_be_instance_of(Mailgunner::Response)
383
+ end
384
+
385
+ it 'encodes the mailbox attributes' do
386
+ expect(Net::HTTP::Put, responds_with(:body, 'password=secret'))
387
+
388
+ @client.update_mailbox('user', {password: 'secret'})
389
+ end
390
+ end
391
+
392
+ describe 'delete_mailbox method' do
393
+ it 'deletes the domain mailbox resource with the given address and returns a response object' do
394
+ expect(Net::HTTP::Delete, "/v2/#@domain/mailboxes/user")
395
+
396
+ @client.delete_mailbox('user').must_be_instance_of(Mailgunner::Response)
397
+ end
398
+ end
399
+
400
+ describe 'get_campaigns method' do
401
+ it 'fetches the domain campaigns resource and returns a response object' do
402
+ expect(Net::HTTP::Get, "/v2/#@domain/campaigns")
403
+
404
+ @client.get_campaigns.must_be_instance_of(Mailgunner::Response)
405
+ end
406
+
407
+ it 'encodes skip and limit parameters' do
408
+ expect(Net::HTTP::Get, "/v2/#@domain/campaigns?skip=1&limit=2")
409
+
410
+ @client.get_campaigns(skip: 1, limit: 2)
411
+ end
412
+ end
413
+
414
+ describe 'get_campaign method' do
415
+ it 'fetches the campaign resource with the given id and returns a response object' do
416
+ expect(Net::HTTP::Get, "/v2/#@domain/campaigns/id")
417
+
418
+ @client.get_campaign('id').must_be_instance_of(Mailgunner::Response)
419
+ end
420
+ end
421
+
422
+ describe 'add_campaign method' do
423
+ it 'posts to the domain campaigns resource and returns a response object' do
424
+ expect(Net::HTTP::Post, "/v2/#@domain/campaigns")
425
+
426
+ @client.add_campaign({}).must_be_instance_of(Mailgunner::Response)
427
+ end
428
+
429
+ it 'encodes the campaign attributes' do
430
+ expect(Net::HTTP::Post, responds_with(:body, 'id=id'))
431
+
432
+ @client.add_campaign({id: 'id'})
433
+ end
434
+ end
435
+
436
+ describe 'update_campaign method' do
437
+ it 'updates the campaign resource and returns a response object' do
438
+ expect(Net::HTTP::Put, "/v2/#@domain/campaigns/id")
439
+
440
+ @client.update_campaign('id', {}).must_be_instance_of(Mailgunner::Response)
441
+ end
442
+
443
+ it 'encodes the campaign attributes' do
444
+ expect(Net::HTTP::Put, responds_with(:body, 'name=Example+Campaign'))
445
+
446
+ @client.update_campaign('id', {name: 'Example Campaign'})
447
+ end
448
+ end
449
+
450
+ describe 'delete_campaign method' do
451
+ it 'deletes the domain campaign resource with the given id and returns a response object' do
452
+ expect(Net::HTTP::Delete, "/v2/#@domain/campaigns/id")
453
+
454
+ @client.delete_campaign('id').must_be_instance_of(Mailgunner::Response)
455
+ end
456
+ end
457
+
458
+ describe 'get_campaign_events method' do
459
+ it 'fetches the domain campaign events resource and returns a response object' do
460
+ expect(Net::HTTP::Get, "/v2/#@domain/campaigns/id/events")
461
+
462
+ @client.get_campaign_events('id').must_be_instance_of(Mailgunner::Response)
463
+ end
464
+
465
+ it 'encodes the optional parameters' do
466
+ expect(Net::HTTP::Get, "/v2/#@domain/campaigns/id/events?country=US&limit=100")
467
+
468
+ @client.get_campaign_events('id', country: 'US', limit: 100)
469
+ end
470
+ end
471
+
472
+ describe 'get_campaign_stats method' do
473
+ it 'fetches the domain campaign stats resource and returns a response object' do
474
+ expect(Net::HTTP::Get, "/v2/#@domain/campaigns/id/stats")
475
+
476
+ @client.get_campaign_stats('id').must_be_instance_of(Mailgunner::Response)
477
+ end
478
+
479
+ it 'encodes the optional parameters' do
480
+ expect(Net::HTTP::Get, "/v2/#@domain/campaigns/id/stats?groupby=dailyhour")
481
+
482
+ @client.get_campaign_stats('id', groupby: 'dailyhour')
483
+ end
484
+ end
485
+
486
+ describe 'get_campaign_clicks method' do
487
+ it 'fetches the domain campaign clicks resource and returns a response object' do
488
+ expect(Net::HTTP::Get, "/v2/#@domain/campaigns/id/clicks")
489
+
490
+ @client.get_campaign_clicks('id').must_be_instance_of(Mailgunner::Response)
491
+ end
492
+
493
+ it 'encodes the optional parameters' do
494
+ expect(Net::HTTP::Get, "/v2/#@domain/campaigns/id/clicks?groupby=month&limit=100")
495
+
496
+ @client.get_campaign_clicks('id', groupby: 'month', limit: 100)
497
+ end
498
+ end
499
+
500
+ describe 'get_campaign_opens method' do
501
+ it 'fetches the domain campaign opens resource and returns a response object' do
502
+ expect(Net::HTTP::Get, "/v2/#@domain/campaigns/id/opens")
503
+
504
+ @client.get_campaign_opens('id').must_be_instance_of(Mailgunner::Response)
505
+ end
506
+
507
+ it 'encodes the optional parameters' do
508
+ expect(Net::HTTP::Get, "/v2/#@domain/campaigns/id/opens?groupby=month&limit=100")
509
+
510
+ @client.get_campaign_opens('id', groupby: 'month', limit: 100)
511
+ end
512
+ end
513
+
514
+ describe 'get_campaign_unsubscribes method' do
515
+ it 'fetches the domain campaign unsubscribes resource and returns a response object' do
516
+ expect(Net::HTTP::Get, "/v2/#@domain/campaigns/id/unsubscribes")
517
+
518
+ @client.get_campaign_unsubscribes('id').must_be_instance_of(Mailgunner::Response)
519
+ end
520
+
521
+ it 'encodes the optional parameters' do
522
+ expect(Net::HTTP::Get, "/v2/#@domain/campaigns/id/unsubscribes?groupby=month&limit=100")
523
+
524
+ @client.get_campaign_unsubscribes('id', groupby: 'month', limit: 100)
525
+ end
526
+ end
527
+
528
+ describe 'get_campaign_complaints method' do
529
+ it 'fetches the domain campaign complaints resource and returns a response object' do
530
+ expect(Net::HTTP::Get, "/v2/#@domain/campaigns/id/complaints")
531
+
532
+ @client.get_campaign_complaints('id').must_be_instance_of(Mailgunner::Response)
533
+ end
534
+
535
+ it 'encodes the optional parameters' do
536
+ expect(Net::HTTP::Get, "/v2/#@domain/campaigns/id/complaints?groupby=month&limit=100")
537
+
538
+ @client.get_campaign_complaints('id', groupby: 'month', limit: 100)
539
+ end
540
+ end
541
+
542
+ describe 'get_lists method' do
543
+ it 'fetches the domain lists resource and returns a response object' do
544
+ expect(Net::HTTP::Get, '/v2/lists')
545
+
546
+ @client.get_lists.must_be_instance_of(Mailgunner::Response)
547
+ end
548
+
549
+ it 'encodes skip and limit parameters' do
550
+ expect(Net::HTTP::Get, '/v2/lists?skip=1&limit=2')
551
+
552
+ @client.get_lists(skip: 1, limit: 2)
553
+ end
554
+ end
555
+
556
+ describe 'get_list method' do
557
+ it 'fetches the list resource with the given address and returns a response object' do
558
+ expect(Net::HTTP::Get, '/v2/lists/developers%40mailgun.net')
559
+
560
+ @client.get_list('developers@mailgun.net').must_be_instance_of(Mailgunner::Response)
561
+ end
562
+ end
563
+
564
+ describe 'add_list method' do
565
+ it 'posts to the domain lists resource and returns a response object' do
566
+ expect(Net::HTTP::Post, '/v2/lists')
567
+
568
+ @client.add_list({}).must_be_instance_of(Mailgunner::Response)
569
+ end
570
+
571
+ it 'encodes the list attributes' do
572
+ expect(Net::HTTP::Post, responds_with(:body, 'address=developers%40mailgun.net'))
573
+
574
+ @client.add_list({address: 'developers@mailgun.net'})
575
+ end
576
+ end
577
+
578
+ describe 'update_list method' do
579
+ it 'updates the list resource and returns a response object' do
580
+ expect(Net::HTTP::Put, '/v2/lists/developers%40mailgun.net')
581
+
582
+ @client.update_list('developers@mailgun.net', {}).must_be_instance_of(Mailgunner::Response)
583
+ end
584
+
585
+ it 'encodes the list attributes' do
586
+ expect(Net::HTTP::Put, responds_with(:body, 'name=Example+list'))
587
+
588
+ @client.update_list('developers@mailgun.net', {name: 'Example list'})
589
+ end
590
+ end
591
+
592
+ describe 'delete_list method' do
593
+ it 'deletes the domain list resource with the given address and returns a response object' do
594
+ expect(Net::HTTP::Delete, '/v2/lists/developers%40mailgun.net')
595
+
596
+ @client.delete_list('developers@mailgun.net').must_be_instance_of(Mailgunner::Response)
597
+ end
598
+ end
599
+
600
+ describe 'get_list_members method' do
601
+ it 'fetches the list members resource and returns a response object' do
602
+ expect(Net::HTTP::Get, '/v2/lists/developers%40mailgun.net/members')
603
+
604
+ @client.get_list_members('developers@mailgun.net').must_be_instance_of(Mailgunner::Response)
605
+ end
606
+
607
+ it 'encodes skip and limit parameters' do
608
+ expect(Net::HTTP::Get, '/v2/lists/developers%40mailgun.net/members?skip=1&limit=2')
609
+
610
+ @client.get_list_members('developers@mailgun.net', skip: 1, limit: 2)
611
+ end
612
+ end
613
+
614
+ describe 'get_list_member method' do
615
+ it 'fetches the list member resource with the given address and returns a response object' do
616
+ expect(Net::HTTP::Get, "/v2/lists/developers%40mailgun.net/members/#@encoded_address")
617
+
618
+ @client.get_list_member('developers@mailgun.net', @address).must_be_instance_of(Mailgunner::Response)
619
+ end
620
+ end
621
+
622
+ describe 'add_list_member method' do
623
+ it 'posts to the list members resource and returns a response object' do
624
+ expect(Net::HTTP::Post, '/v2/lists/developers%40mailgun.net/members')
625
+
626
+ @client.add_list_member('developers@mailgun.net', {}).must_be_instance_of(Mailgunner::Response)
627
+ end
628
+
629
+ it 'encodes the list attributes' do
630
+ expect(Net::HTTP::Post, responds_with(:body, "address=#@encoded_address"))
631
+
632
+ @client.add_list_member('developers@mailgun.net', {address: @address})
633
+ end
634
+ end
635
+
636
+ describe 'update_list_member method' do
637
+ it 'updates the list member resource with the given address and returns a response object' do
638
+ expect(Net::HTTP::Put, "/v2/lists/developers%40mailgun.net/members/#@encoded_address")
639
+
640
+ @client.update_list_member('developers@mailgun.net', @address, {}).must_be_instance_of(Mailgunner::Response)
641
+ end
642
+
643
+ it 'encodes the list member attributes' do
644
+ expect(Net::HTTP::Put, responds_with(:body, 'subscribed=no'))
645
+
646
+ @client.update_list_member('developers@mailgun.net', @address, {subscribed: 'no'})
647
+ end
648
+ end
649
+
650
+ describe 'delete_list_member method' do
651
+ it 'deletes the list member resource with the given address and returns a response object' do
652
+ expect(Net::HTTP::Delete, "/v2/lists/developers%40mailgun.net/members/#@encoded_address")
653
+
654
+ @client.delete_list_member('developers@mailgun.net', @address).must_be_instance_of(Mailgunner::Response)
655
+ end
656
+ end
657
+
658
+ describe 'get_list_stats method' do
659
+ it 'fetches the list stats resource and returns a response object' do
660
+ expect(Net::HTTP::Get, '/v2/lists/developers%40mailgun.net/stats')
661
+
662
+ @client.get_list_stats('developers@mailgun.net').must_be_instance_of(Mailgunner::Response)
663
+ end
664
+ end
665
+ end
666
+
667
+ describe 'Mailgunner::Response' do
668
+ before do
669
+ @http_response = mock()
670
+
671
+ @response = Mailgunner::Response.new(@http_response)
672
+ end
673
+
674
+ it 'delegates missing methods to the http response object' do
675
+ @http_response.stubs(:code).returns('200')
676
+
677
+ @response.code.must_equal('200')
678
+ end
679
+
680
+ describe 'ok query method' do
681
+ it 'returns true if the status code is 200' do
682
+ @http_response.expects(:code).returns('200')
683
+
684
+ @response.ok?.must_equal(true)
685
+ end
686
+
687
+ it 'returns false otherwise' do
688
+ @http_response.expects(:code).returns('400')
689
+
690
+ @response.ok?.must_equal(false)
691
+ end
692
+ end
693
+
694
+ describe 'client_error query method' do
695
+ it 'returns true if the status code is 4xx' do
696
+ @http_response.stubs(:code).returns(%w(400 401 402 404).sample)
697
+
698
+ @response.client_error?.must_equal(true)
699
+ end
700
+
701
+ it 'returns false otherwise' do
702
+ @http_response.stubs(:code).returns('200')
703
+
704
+ @response.client_error?.must_equal(false)
705
+ end
706
+ end
707
+
708
+ describe 'server_error query method' do
709
+ it 'returns true if the status code is 5xx' do
710
+ @http_response.stubs(:code).returns(%w(500 502 503 504).sample)
711
+
712
+ @response.server_error?.must_equal(true)
713
+ end
714
+
715
+ it 'returns false otherwise' do
716
+ @http_response.stubs(:code).returns('200')
717
+
718
+ @response.server_error?.must_equal(false)
719
+ end
720
+ end
721
+
722
+ describe 'json query method' do
723
+ it 'returns true if the response has a json content type' do
724
+ @http_response.expects(:[]).with('Content-Type').returns('application/json;charset=utf-8')
725
+
726
+ @response.json?.must_equal(true)
727
+ end
728
+
729
+ it 'returns false otherwise' do
730
+ @http_response.expects(:[]).with('Content-Type').returns('text/html')
731
+
732
+ @response.json?.must_equal(false)
733
+ end
734
+ end
735
+
736
+ describe 'object method' do
737
+ it 'decodes the response body as json and returns a hash' do
738
+ @http_response.expects(:body).returns('{"foo":"bar"}')
739
+
740
+ @response.object.must_equal({'foo' => 'bar'})
741
+ end
742
+ end
743
+ end
744
+
745
+ describe 'Mailgunner::Response initialized with an alternative json implementation' do
746
+ before do
747
+ @json = mock()
748
+
749
+ @http_response = mock()
750
+
751
+ @response = Mailgunner::Response.new(@http_response, :json => @json)
752
+ end
753
+
754
+ describe 'object method' do
755
+ it 'uses the alternative json implementation to parse the response body' do
756
+ @http_response.stubs(:body).returns(response_body = '{"foo":"bar"}')
757
+
758
+ @json.expects(:load).with(response_body)
759
+
760
+ @response.object
761
+ end
762
+ end
763
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mailgunner
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Tim Craft
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.9.3
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 0.9.3
30
+ - !ruby/object:Gem::Dependency
31
+ name: mocha
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 0.10.3
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 0.10.3
46
+ description: A Ruby wrapper for the Mailgun API
47
+ email:
48
+ - mail@timcraft.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - lib/mailgunner.rb
54
+ - spec/mailgunner_spec.rb
55
+ - README.md
56
+ - mailgunner.gemspec
57
+ homepage: http://github.com/timcraft/mailgunner
58
+ licenses: []
59
+ post_install_message:
60
+ rdoc_options: []
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ! '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ requirements: []
76
+ rubyforge_project:
77
+ rubygems_version: 1.8.24
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: See description
81
+ test_files: []
82
+ has_rdoc: