mailgun-ruby 1.2.0 → 1.2.10
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.
- checksums.yaml +4 -4
- data/README.md +9 -1
- data/docs/Domains.md +3 -0
- data/docs/OptInHandler.md +1 -1
- data/docs/Snippets.md +54 -61
- data/docs/railgun/Templates.md +92 -0
- data/lib/mailgun/address.rb +3 -28
- data/lib/mailgun/client.rb +43 -10
- data/lib/mailgun/domains/domains.rb +19 -1
- data/lib/mailgun/exceptions/exceptions.rb +28 -1
- data/lib/mailgun/messages/batch_message.rb +1 -0
- data/lib/mailgun/messages/message_builder.rb +55 -7
- data/lib/mailgun/suppressions.rb +15 -8
- data/lib/mailgun/version.rb +1 -1
- data/lib/mailgun-ruby.rb +1 -1
- data/lib/mailgun.rb +3 -1
- data/lib/railgun/mailer.rb +30 -9
- data/lib/railgun/message.rb +2 -1
- data/lib/railgun/railtie.rb +3 -2
- data/mailgun.gemspec +1 -1
- data/spec/integration/bounces_spec.rb +3 -3
- data/spec/integration/domains_spec.rb +8 -0
- data/spec/integration/email_validation_spec.rb +1 -8
- data/spec/integration/mailer_spec.rb +67 -0
- data/spec/integration/mailgun_spec.rb +76 -1
- data/spec/integration/suppressions_spec.rb +18 -2
- data/spec/unit/connection/test_client.rb +18 -1
- data/spec/unit/mailgun_spec.rb +43 -2
- data/spec/unit/messages/batch_message_spec.rb +56 -40
- data/spec/unit/messages/message_builder_spec.rb +149 -16
- data/spec/unit/messages/sample_data/unknown.type +0 -0
- data/spec/unit/railgun/mailer_spec.rb +171 -0
- data/vcr_cassettes/bounces.yml +12 -12
- data/vcr_cassettes/domains.yml +51 -1
- data/vcr_cassettes/exceptions-invalid-api-key.yml +52 -0
- data/vcr_cassettes/exceptions-invalid-data.yml +52 -0
- data/vcr_cassettes/exceptions-not-allowed.yml +54 -0
- data/vcr_cassettes/mailer_invalid_domain.yml +109 -0
- data/vcr_cassettes/message_deliver.yml +149 -0
- data/vcr_cassettes/suppressions.yml +66 -15
- metadata +15 -6
data/lib/mailgun/client.rb
CHANGED
@@ -11,18 +11,23 @@ module Mailgun
|
|
11
11
|
class Client
|
12
12
|
|
13
13
|
def initialize(api_key = Mailgun.api_key,
|
14
|
-
api_host = 'api.mailgun.net',
|
15
|
-
api_version = 'v3',
|
14
|
+
api_host = Mailgun.api_host || 'api.mailgun.net',
|
15
|
+
api_version = Mailgun.api_version || 'v3',
|
16
16
|
ssl = true,
|
17
17
|
test_mode = false,
|
18
|
-
timeout = nil
|
18
|
+
timeout = nil,
|
19
|
+
proxy_url = Mailgun.proxy_url)
|
20
|
+
|
21
|
+
rest_client_params = {
|
22
|
+
user: 'api',
|
23
|
+
password: api_key,
|
24
|
+
user_agent: "mailgun-sdk-ruby/#{Mailgun::VERSION}"
|
25
|
+
}
|
26
|
+
rest_client_params[:timeout] = timeout if timeout
|
19
27
|
|
20
28
|
endpoint = endpoint_generator(api_host, api_version, ssl)
|
21
|
-
|
22
|
-
|
23
|
-
password: api_key,
|
24
|
-
user_agent: "mailgun-sdk-ruby/#{Mailgun::VERSION}",
|
25
|
-
timeout: timeout)
|
29
|
+
RestClient.proxy = proxy_url
|
30
|
+
@http_client = RestClient::Resource.new(endpoint, rest_client_params)
|
26
31
|
@test_mode = test_mode
|
27
32
|
end
|
28
33
|
|
@@ -40,6 +45,11 @@ module Mailgun
|
|
40
45
|
@test_mode = false
|
41
46
|
end
|
42
47
|
|
48
|
+
# Change API key
|
49
|
+
def set_api_key(api_key)
|
50
|
+
@http_client.options[:password] = api_key
|
51
|
+
end
|
52
|
+
|
43
53
|
# Client is in test mode?
|
44
54
|
#
|
45
55
|
# @return [Boolean] Is the client set in test mode?
|
@@ -61,11 +71,13 @@ module Mailgun
|
|
61
71
|
# containing required parameters for the requested resource.
|
62
72
|
# @return [Mailgun::Response] A Mailgun::Response object.
|
63
73
|
def send_message(working_domain, data)
|
74
|
+
perform_data_validation(working_domain, data)
|
75
|
+
|
64
76
|
if test_mode? then
|
65
77
|
Mailgun::Client.deliveries << data
|
66
78
|
return Response.from_hash(
|
67
79
|
{
|
68
|
-
:body =>
|
80
|
+
:body => "{\"id\": \"test-mode-mail-#{SecureRandom.uuid}@localhost\", \"message\": \"Queued. Thank you.\"}",
|
69
81
|
:code => 200,
|
70
82
|
}
|
71
83
|
)
|
@@ -193,9 +205,30 @@ module Mailgun
|
|
193
205
|
#
|
194
206
|
# @param [StandardException] e upstream exception object
|
195
207
|
def communication_error(e)
|
196
|
-
|
208
|
+
if e.respond_to?(:response) && e.response
|
209
|
+
return case e.response.code
|
210
|
+
when Unauthorized::CODE
|
211
|
+
Unauthorized.new(e.message, e.response)
|
212
|
+
when BadRequest::CODE
|
213
|
+
BadRequest.new(e.message, e.response)
|
214
|
+
else
|
215
|
+
CommunicationError.new(e.message, e.response)
|
216
|
+
end
|
217
|
+
end
|
197
218
|
CommunicationError.new(e.message)
|
198
219
|
end
|
199
220
|
|
221
|
+
def perform_data_validation(working_domain, data)
|
222
|
+
message = data.respond_to?(:message) ? data.message : data
|
223
|
+
fail ParameterError.new('Missing working domain', working_domain) unless working_domain
|
224
|
+
fail ParameterError.new(
|
225
|
+
'Missing `to` recipient, message should containg at least 1 recipient',
|
226
|
+
working_domain
|
227
|
+
) if message.fetch('to', []).empty? && message.fetch(:to, []).empty?
|
228
|
+
fail ParameterError.new(
|
229
|
+
'Missing a `from` sender, message should containg at least 1 `from` sender',
|
230
|
+
working_domain
|
231
|
+
) if message.fetch('from', []).empty? && message.fetch(:from, []).empty?
|
232
|
+
end
|
200
233
|
end
|
201
234
|
end
|
@@ -53,8 +53,9 @@ module Mailgun
|
|
53
53
|
# domain - [String] Name of the domain (ex. domain.com)
|
54
54
|
# options - [Hash] of
|
55
55
|
# smtp_password - [String] Password for SMTP authentication
|
56
|
-
# spam_action - [String] disabled or tag
|
56
|
+
# spam_action - [String] disabled, blocked or tag
|
57
57
|
# Disable, no spam filtering will occur for inbound messages.
|
58
|
+
# Block, inbound spam messages will not be delivered.
|
58
59
|
# Tag, messages will be tagged wtih a spam header. See Spam Filter.
|
59
60
|
# wildcard - [Boolean] true or false Determines whether the domain will accept email for sub-domains.
|
60
61
|
#
|
@@ -80,5 +81,22 @@ module Mailgun
|
|
80
81
|
alias_method :delete, :remove
|
81
82
|
alias_method :delete_domain, :remove
|
82
83
|
|
84
|
+
# Public: Update domain
|
85
|
+
#
|
86
|
+
# domain - [String] Name of the domain (ex. domain.com)
|
87
|
+
# options - [Hash] of
|
88
|
+
# spam_action - [String] disabled, blocked or tag
|
89
|
+
# Disable, no spam filtering will occur for inbound messages.
|
90
|
+
# Block, inbound spam messages will not be delivered.
|
91
|
+
# Tag, messages will be tagged wtih a spam header. See Spam Filter.
|
92
|
+
# web_scheme - [String] http or https
|
93
|
+
# Set your open, click and unsubscribe URLs to use http or https
|
94
|
+
# wildcard - [Boolean] true or false Determines whether the domain will accept email for sub-domains.
|
95
|
+
#
|
96
|
+
# Returns [Hash] of updated domain
|
97
|
+
def update(domain, options = {})
|
98
|
+
fail(ParameterError, 'No domain given to add on Mailgun', caller) unless domain
|
99
|
+
@client.put("domains/#{domain}", options).to_h
|
100
|
+
end
|
83
101
|
end
|
84
102
|
end
|
@@ -34,6 +34,7 @@ module Mailgun
|
|
34
34
|
|
35
35
|
# Public: fallback if there is no response code on the object
|
36
36
|
NOCODE = 000
|
37
|
+
FORBIDDEN = 'Forbidden'
|
37
38
|
|
38
39
|
# Public: initialization of new error given a message and/or object
|
39
40
|
#
|
@@ -42,7 +43,11 @@ module Mailgun
|
|
42
43
|
#
|
43
44
|
def initialize(message = nil, response = nil)
|
44
45
|
@response = response
|
45
|
-
@code = response.
|
46
|
+
@code = if response.nil?
|
47
|
+
NOCODE
|
48
|
+
else
|
49
|
+
response.code
|
50
|
+
end
|
46
51
|
|
47
52
|
begin
|
48
53
|
api_message = JSON.parse(response.body)['message']
|
@@ -50,6 +55,8 @@ module Mailgun
|
|
50
55
|
api_message = response.body
|
51
56
|
rescue NoMethodError
|
52
57
|
api_message = "Unknown API error"
|
58
|
+
rescue
|
59
|
+
api_message = 'Unknown API error'
|
53
60
|
end
|
54
61
|
|
55
62
|
message = message || ''
|
@@ -60,6 +67,26 @@ module Mailgun
|
|
60
67
|
@code = NOCODE
|
61
68
|
super(message, response)
|
62
69
|
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Public: Class for managing unauthorized 401 errors
|
73
|
+
# Inherits from Mailgun::CommunicationError
|
74
|
+
class Unauthorized < CommunicationError
|
75
|
+
CODE = 401
|
63
76
|
|
77
|
+
def initialize(error_message, response)
|
78
|
+
error_message = error_message + ' - Invalid Domain or API key'
|
79
|
+
super(error_message, response)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Public: Class for managing bad request 400 errors
|
84
|
+
# Inherits from Mailgun::CommunicationError
|
85
|
+
class BadRequest < CommunicationError
|
86
|
+
CODE = 400
|
87
|
+
|
88
|
+
def initialize(error_message, response)
|
89
|
+
super(error_message, response)
|
90
|
+
end
|
64
91
|
end
|
65
92
|
end
|
@@ -111,6 +111,7 @@ module Mailgun
|
|
111
111
|
# This method resets the message object to prepare for the next batch
|
112
112
|
# of recipients.
|
113
113
|
def reset_message
|
114
|
+
@recipient_variables = {}
|
114
115
|
@message.delete('recipient-variables')
|
115
116
|
@message.delete(:to)
|
116
117
|
@message.delete(:cc)
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'mime/types'
|
1
2
|
require 'time'
|
2
3
|
|
3
4
|
module Mailgun
|
@@ -197,7 +198,9 @@ module Mailgun
|
|
197
198
|
# @param [Boolean] tracking Boolean true or false.
|
198
199
|
# @return [void]
|
199
200
|
def track_opens(mode)
|
200
|
-
|
201
|
+
value = bool_lookup(mode)
|
202
|
+
set_single('o:tracking-opens', value)
|
203
|
+
set_multi_simple('o:tracking', value)
|
201
204
|
end
|
202
205
|
|
203
206
|
# Deprecated: 'set_open_tracking' is deprecated. Please use 'track_opens' instead.
|
@@ -211,7 +214,9 @@ module Mailgun
|
|
211
214
|
# @param [String] mode True, False, or HTML (for HTML only tracking)
|
212
215
|
# @return [void]
|
213
216
|
def track_clicks(mode)
|
214
|
-
|
217
|
+
value = bool_lookup(mode)
|
218
|
+
set_single('o:tracking-clicks', value)
|
219
|
+
set_multi_simple('o:tracking', value)
|
215
220
|
end
|
216
221
|
|
217
222
|
# Depreciated: 'set_click_tracking. is deprecated. Please use 'track_clicks' instead.
|
@@ -269,8 +274,12 @@ module Mailgun
|
|
269
274
|
# @return [void]
|
270
275
|
def variable(name, data)
|
271
276
|
fail(Mailgun::ParameterError, 'Variable name must be specified') if name.to_s.empty?
|
272
|
-
|
273
|
-
|
277
|
+
begin
|
278
|
+
jsondata = make_json data
|
279
|
+
set_single("v:#{name}", jsondata)
|
280
|
+
rescue Mailgun::ParameterError
|
281
|
+
set_single("v:#{name}", data)
|
282
|
+
end
|
274
283
|
end
|
275
284
|
|
276
285
|
# Add custom parameter to the message. A custom parameter is any parameter that
|
@@ -303,6 +312,38 @@ module Mailgun
|
|
303
312
|
message_id data
|
304
313
|
end
|
305
314
|
|
315
|
+
# Set name of a template stored via template API. See Templates for more information
|
316
|
+
# https://documentation.mailgun.com/en/latest/api-templates.html
|
317
|
+
#
|
318
|
+
# @param [String] tag A defined template name to use. Passing nil or
|
319
|
+
# empty string will delete template key and value from @message hash.
|
320
|
+
# @return [void]
|
321
|
+
def template(template_name = nil)
|
322
|
+
key = 'template'
|
323
|
+
return @message.delete(key) if template_name.to_s.empty?
|
324
|
+
set_single(key, template_name)
|
325
|
+
end
|
326
|
+
|
327
|
+
# Set specific template version.
|
328
|
+
#
|
329
|
+
# @param [String] tag A defined template name to use. Passing nil or
|
330
|
+
# empty string will delete template key and value from @message hash.
|
331
|
+
# @return [void]
|
332
|
+
def template_version(version = nil)
|
333
|
+
key = 't:version'
|
334
|
+
return @message.delete(key) if version.to_s.empty?
|
335
|
+
set_single(key, version)
|
336
|
+
end
|
337
|
+
|
338
|
+
# Turn off or on template rendering in the text part
|
339
|
+
# of the message in case of template sending.
|
340
|
+
#
|
341
|
+
# @param [Boolean] tracking Boolean true or false.
|
342
|
+
# @return [void]
|
343
|
+
def template_text(mode)
|
344
|
+
set_single('t:text', bool_lookup(mode))
|
345
|
+
end
|
346
|
+
|
306
347
|
private
|
307
348
|
|
308
349
|
# Sets a single value in the message hash where "multidict" features are not needed.
|
@@ -342,6 +383,7 @@ module Mailgun
|
|
342
383
|
def bool_lookup(value)
|
343
384
|
return 'yes' if %w(true yes yep).include? value.to_s.downcase
|
344
385
|
return 'no' if %w(false no nope).include? value.to_s.downcase
|
386
|
+
warn 'WARN: for bool type actions next values are prefered: true yes yep | false no nope | htmlonly'
|
345
387
|
value
|
346
388
|
end
|
347
389
|
|
@@ -364,7 +406,7 @@ module Mailgun
|
|
364
406
|
def make_json(obj)
|
365
407
|
return JSON.parse(obj).to_json if obj.is_a?(String)
|
366
408
|
return obj.to_json if obj.is_a?(Hash)
|
367
|
-
|
409
|
+
JSON.generate(obj).to_json
|
368
410
|
rescue
|
369
411
|
raise Mailgun::ParameterError, 'Provided data could not be made into JSON. Try a JSON string or Hash.', obj
|
370
412
|
end
|
@@ -387,9 +429,9 @@ module Mailgun
|
|
387
429
|
full_name = vars['full_name']
|
388
430
|
elsif vars['first'] || vars['last']
|
389
431
|
full_name = "#{vars['first']} #{vars['last']}".strip
|
390
|
-
end
|
432
|
+
end
|
391
433
|
|
392
|
-
return "'#{full_name}' <#{address}>" if
|
434
|
+
return "'#{full_name}' <#{address}>" if full_name
|
393
435
|
address
|
394
436
|
end
|
395
437
|
|
@@ -409,6 +451,12 @@ module Mailgun
|
|
409
451
|
'Unable to access attachment file object.'
|
410
452
|
) unless attachment.respond_to?(:read)
|
411
453
|
|
454
|
+
if attachment.respond_to?(:path) && !attachment.respond_to?(:content_type)
|
455
|
+
mime_types = MIME::Types.type_for(attachment.path)
|
456
|
+
content_type = mime_types.empty? ? 'application/octet-stream' : mime_types[0].content_type
|
457
|
+
attachment.instance_eval "def content_type; '#{content_type}'; end"
|
458
|
+
end
|
459
|
+
|
412
460
|
unless filename.nil?
|
413
461
|
attachment.instance_variable_set :@original_filename, filename
|
414
462
|
attachment.instance_eval 'def original_filename; @original_filename; end'
|
data/lib/mailgun/suppressions.rb
CHANGED
@@ -45,11 +45,11 @@ module Mailgun
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def get_bounce(address)
|
48
|
-
@client.get("#{@domain}/bounces/#{address}", nil)
|
48
|
+
@client.get("#{@domain}/bounces/#{escape_address(address)}", nil)
|
49
49
|
end
|
50
50
|
|
51
51
|
def create_bounce(params = {})
|
52
|
-
@client.post("#{@domain/bounces
|
52
|
+
@client.post("#{@domain}/bounces", params)
|
53
53
|
end
|
54
54
|
|
55
55
|
# Creates multiple bounces on the Mailgun API.
|
@@ -100,7 +100,7 @@ module Mailgun
|
|
100
100
|
end
|
101
101
|
|
102
102
|
def delete_bounce(address)
|
103
|
-
@client.delete("#{@domain}/bounces/#{address}")
|
103
|
+
@client.delete("#{@domain}/bounces/#{escape_address(address)}")
|
104
104
|
end
|
105
105
|
|
106
106
|
def delete_all_bounces
|
@@ -118,7 +118,7 @@ module Mailgun
|
|
118
118
|
end
|
119
119
|
|
120
120
|
def get_unsubscribe(address)
|
121
|
-
@client.get("#{@domain}/unsubscribes/#{address}")
|
121
|
+
@client.get("#{@domain}/unsubscribes/#{escape_address(address)}")
|
122
122
|
end
|
123
123
|
|
124
124
|
def create_unsubscribe(params = {})
|
@@ -157,7 +157,10 @@ module Mailgun
|
|
157
157
|
|
158
158
|
unsubscribe.each do |k, v|
|
159
159
|
# Hash values MUST be strings.
|
160
|
-
|
160
|
+
# However, unsubscribes contain an array of tags
|
161
|
+
if v.is_a? Array
|
162
|
+
unsubscribe[k] = v.map(&:to_s)
|
163
|
+
elsif !v.is_a? String
|
161
164
|
unsubscribe[k] = v.to_s
|
162
165
|
end
|
163
166
|
end
|
@@ -170,7 +173,7 @@ module Mailgun
|
|
170
173
|
end
|
171
174
|
|
172
175
|
def delete_unsubscribe(address, params = {})
|
173
|
-
@client.delete("#{@domain}/unsubscribes/#{address}")
|
176
|
+
@client.delete("#{@domain}/unsubscribes/#{escape_address(address)}")
|
174
177
|
end
|
175
178
|
|
176
179
|
####
|
@@ -184,7 +187,7 @@ module Mailgun
|
|
184
187
|
end
|
185
188
|
|
186
189
|
def get_complaint(address)
|
187
|
-
@client.get("#{@domain}/complaints/#{address}", nil)
|
190
|
+
@client.get("#{@domain}/complaints/#{escape_address(address)}", nil)
|
188
191
|
end
|
189
192
|
|
190
193
|
def create_complaint(params = {})
|
@@ -236,11 +239,15 @@ module Mailgun
|
|
236
239
|
end
|
237
240
|
|
238
241
|
def delete_complaint(address)
|
239
|
-
@client.delete("#{@domain}/complaints/#{address}")
|
242
|
+
@client.delete("#{@domain}/complaints/#{escape_address(address)}")
|
240
243
|
end
|
241
244
|
|
242
245
|
private
|
243
246
|
|
247
|
+
def escape_address(address)
|
248
|
+
CGI.escape address
|
249
|
+
end
|
250
|
+
|
244
251
|
def get_from_paging(uri, params = {})
|
245
252
|
@client.get(uri, params)
|
246
253
|
end
|
data/lib/mailgun/version.rb
CHANGED
data/lib/mailgun-ruby.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
require 'mailgun'
|
2
|
-
require 'railgun' if defined?(Rails)
|
2
|
+
require 'railgun' if defined?(Rails) && defined?(ActionMailer)
|
data/lib/mailgun.rb
CHANGED
data/lib/railgun/mailer.rb
CHANGED
@@ -11,7 +11,7 @@ module Railgun
|
|
11
11
|
class Mailer
|
12
12
|
|
13
13
|
# List of the headers that will be ignored when copying headers from `mail.header_fields`
|
14
|
-
IGNORED_HEADERS = %w[ to from subject reply-to ]
|
14
|
+
IGNORED_HEADERS = %w[ to from subject reply-to mime-version template ]
|
15
15
|
|
16
16
|
# [Hash] config ->
|
17
17
|
# Requires *at least* `api_key` and `domain` keys.
|
@@ -32,6 +32,8 @@ module Railgun
|
|
32
32
|
config[:api_host] || 'api.mailgun.net',
|
33
33
|
config[:api_version] || 'v3',
|
34
34
|
config[:api_ssl].nil? ? true : config[:api_ssl],
|
35
|
+
false,
|
36
|
+
config[:timeout]
|
35
37
|
)
|
36
38
|
@domain = @config[:domain]
|
37
39
|
|
@@ -45,8 +47,14 @@ module Railgun
|
|
45
47
|
end
|
46
48
|
|
47
49
|
def deliver!(mail)
|
50
|
+
@mg_domain = set_mg_domain(mail)
|
51
|
+
@mg_client.set_api_key(mail[:api_key].value) if mail[:api_key].present?
|
52
|
+
|
53
|
+
mail[:domain] = nil if mail[:domain].present?
|
54
|
+
mail[:api_key] = nil if mail[:api_key].present?
|
55
|
+
|
48
56
|
mg_message = Railgun.transform_for_mailgun(mail)
|
49
|
-
response = @mg_client.send_message(@
|
57
|
+
response = @mg_client.send_message(@mg_domain, mg_message)
|
50
58
|
|
51
59
|
if response.code == 200 then
|
52
60
|
mg_id = response.to_h['id']
|
@@ -59,6 +67,14 @@ module Railgun
|
|
59
67
|
@mg_client
|
60
68
|
end
|
61
69
|
|
70
|
+
private
|
71
|
+
|
72
|
+
# Set @mg_domain from mail[:domain] header if present, then remove it to prevent being sent.
|
73
|
+
def set_mg_domain(mail)
|
74
|
+
return mail[:domain].value if mail[:domain]
|
75
|
+
domain
|
76
|
+
end
|
77
|
+
|
62
78
|
end
|
63
79
|
|
64
80
|
module_function
|
@@ -76,14 +92,14 @@ module Railgun
|
|
76
92
|
def transform_for_mailgun(mail)
|
77
93
|
message = build_message_object(mail)
|
78
94
|
|
79
|
-
# v:* attributes (variables)
|
80
|
-
mail.mailgun_variables.try(:each) do |k, v|
|
81
|
-
message["v:#{k}"] = JSON.dump(v)
|
82
|
-
end
|
83
|
-
|
84
95
|
# o:* attributes (options)
|
85
96
|
mail.mailgun_options.try(:each) do |k, v|
|
86
|
-
message["o:#{k}"] = v
|
97
|
+
message["o:#{k}"] = v.dup
|
98
|
+
end
|
99
|
+
|
100
|
+
# t:* attributes (options)
|
101
|
+
mail.mailgun_template_variables.try(:each) do |k, v|
|
102
|
+
message["t:#{k}"] = v.dup
|
87
103
|
end
|
88
104
|
|
89
105
|
# support for using ActionMailer's `headers()` inside of the mailer
|
@@ -151,6 +167,7 @@ module Railgun
|
|
151
167
|
|
152
168
|
mb.from mail[:from]
|
153
169
|
mb.reply_to(mail[:reply_to].to_s) if mail[:reply_to].present?
|
170
|
+
mb.template(mail[:template].to_s) if mail[:template].present?
|
154
171
|
mb.subject mail.subject
|
155
172
|
mb.body_html extract_body_html(mail)
|
156
173
|
mb.body_text extract_body_text(mail)
|
@@ -170,6 +187,11 @@ module Railgun
|
|
170
187
|
end
|
171
188
|
end
|
172
189
|
|
190
|
+
# v:* attributes (variables)
|
191
|
+
mail.mailgun_variables.try(:each) do |name, value|
|
192
|
+
mb.variable(name, value)
|
193
|
+
end
|
194
|
+
|
173
195
|
return mb.message if mail.attachments.empty?
|
174
196
|
|
175
197
|
mail.attachments.each do |attach|
|
@@ -231,5 +253,4 @@ module Railgun
|
|
231
253
|
return mail.html_part if mail.multipart?
|
232
254
|
(mail.mime_type =~ /^text\/html$/i) && mail
|
233
255
|
end
|
234
|
-
|
235
256
|
end
|
data/lib/railgun/message.rb
CHANGED
data/lib/railgun/railtie.rb
CHANGED
@@ -2,8 +2,9 @@ require 'railgun/mailer'
|
|
2
2
|
|
3
3
|
module Railgun
|
4
4
|
class Railtie < ::Rails::Railtie
|
5
|
-
|
6
|
-
|
5
|
+
ActiveSupport.on_load(:action_mailer) do
|
6
|
+
add_delivery_method :mailgun, Railgun::Mailer
|
7
|
+
ActiveSupport.run_load_hooks(:mailgun_mailer, Railgun::Mailer)
|
7
8
|
end
|
8
9
|
end
|
9
10
|
end
|
data/mailgun.gemspec
CHANGED
@@ -32,6 +32,6 @@ Gem::Specification.new do |spec|
|
|
32
32
|
spec.add_development_dependency 'vcr', '~> 3.0.3'
|
33
33
|
spec.add_development_dependency 'simplecov', '~> 0.16.1'
|
34
34
|
spec.add_development_dependency 'rails'
|
35
|
-
spec.add_dependency 'rest-client', '
|
35
|
+
spec.add_dependency 'rest-client', '>= 2.0.2'
|
36
36
|
|
37
37
|
end
|
@@ -7,7 +7,7 @@ describe 'For the Bounces endpoint', order: :defined, vcr: vcr_opts do
|
|
7
7
|
before(:all) do
|
8
8
|
@mg_obj = Mailgun::Client.new(APIKEY, APIHOST, APIVERSION, SSL)
|
9
9
|
@domain = TESTDOMAIN
|
10
|
-
@email = "integration-test
|
10
|
+
@email = "integration-test+email@#{TESTDOMAIN}"
|
11
11
|
end
|
12
12
|
|
13
13
|
it 'creates a bounce' do
|
@@ -22,7 +22,7 @@ describe 'For the Bounces endpoint', order: :defined, vcr: vcr_opts do
|
|
22
22
|
end
|
23
23
|
|
24
24
|
it 'get a bounce.' do
|
25
|
-
result = @mg_obj.get("#{@domain}/bounces/#{@email}")
|
25
|
+
result = @mg_obj.get("#{@domain}/bounces/#{CGI.escape(@email)}")
|
26
26
|
|
27
27
|
result.to_h!
|
28
28
|
expect(result.body["code"]).to eq("550")
|
@@ -38,7 +38,7 @@ describe 'For the Bounces endpoint', order: :defined, vcr: vcr_opts do
|
|
38
38
|
end
|
39
39
|
|
40
40
|
it 'deletes a bounce' do
|
41
|
-
@mg_obj.delete("#{@domain}/bounces/#{@email}")
|
41
|
+
@mg_obj.delete("#{@domain}/bounces/#{CGI.escape(@email)}")
|
42
42
|
end
|
43
43
|
|
44
44
|
end
|
@@ -36,4 +36,12 @@ describe 'For the domains endpoint', vcr: vcr_opts do
|
|
36
36
|
|
37
37
|
expect(result).to be_truthy
|
38
38
|
end
|
39
|
+
|
40
|
+
it 'updates the domain' do
|
41
|
+
result = @mg_obj.update(@domain, { spam_action: 'block', web_scheme: 'https', wildcard: true })
|
42
|
+
|
43
|
+
expect(result['domain']["spam_action"]).to eq('block')
|
44
|
+
expect(result['domain']["web_scheme"]).to eq('https')
|
45
|
+
expect(result['domain']["wildcard"]).to eq(true)
|
46
|
+
end
|
39
47
|
end
|
@@ -7,7 +7,7 @@ vcr_opts = { :cassette_name => "email_validation" }
|
|
7
7
|
|
8
8
|
describe 'For the email validation endpoint', order: :defined, vcr: vcr_opts do
|
9
9
|
before(:all) do
|
10
|
-
@mg_obj = Mailgun::Address.new
|
10
|
+
@mg_obj = Mailgun::Address.new
|
11
11
|
|
12
12
|
@valid = ["Alice <alice@example.com>", "bob@example.com"]
|
13
13
|
@invalid = ["example.org"]
|
@@ -15,13 +15,6 @@ describe 'For the email validation endpoint', order: :defined, vcr: vcr_opts do
|
|
15
15
|
@all_addrs = @valid + @invalid
|
16
16
|
end
|
17
17
|
|
18
|
-
it 'returns parsed and unparsable lists' do
|
19
|
-
res = @mg_obj.parse(@all_addrs)
|
20
|
-
|
21
|
-
expect(res["parsed"]).to eq(@valid)
|
22
|
-
expect(res["unparseable"]).to eq(@invalid)
|
23
|
-
end
|
24
|
-
|
25
18
|
it 'validates alice@mailgun.net with info' do
|
26
19
|
res = @mg_obj.validate("alice@mailgun.net")
|
27
20
|
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'json'
|
3
|
+
require 'logger'
|
4
|
+
require 'railgun'
|
5
|
+
require 'mailgun'
|
6
|
+
require 'mailgun/exceptions/exceptions'
|
7
|
+
|
8
|
+
ActionMailer::Base.raise_delivery_errors = true
|
9
|
+
Rails.logger = Logger.new('/dev/null')
|
10
|
+
Rails.logger.level = Logger::DEBUG
|
11
|
+
|
12
|
+
class UnitTestMailer < ActionMailer::Base
|
13
|
+
default from: 'unittest@example.org'
|
14
|
+
|
15
|
+
def plain_message(address, from, subject, headers)
|
16
|
+
headers(headers)
|
17
|
+
mail(to: address, from: from, subject: subject) do |format|
|
18
|
+
format.text { render plain: 'Test!' }
|
19
|
+
format.html { render html: '<p>Test!</p>'.html_safe }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
vcr_opts = { :cassette_name => 'message_deliver' }
|
25
|
+
|
26
|
+
describe 'Message deliver', vcr: vcr_opts do
|
27
|
+
let(:domain) { TESTDOMAIN }
|
28
|
+
let(:config) do
|
29
|
+
{
|
30
|
+
api_key: APIKEY,
|
31
|
+
domain: domain
|
32
|
+
}
|
33
|
+
end
|
34
|
+
let(:mail) { UnitTestMailer.plain_message("bob@#{domain}", "bob@#{domain}", 'subject', {}) }
|
35
|
+
|
36
|
+
it 'successfully delivers message' do
|
37
|
+
result = Railgun::Mailer.new(config).deliver!(mail)
|
38
|
+
result.to_h!
|
39
|
+
|
40
|
+
expect(result.body['message']).to eq('Queued. Thank you.')
|
41
|
+
expect(result.body).to include('id')
|
42
|
+
expect(result.code).to eq(200)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
vcr_opts = { :cassette_name => 'mailer_invalid_domain' }
|
47
|
+
|
48
|
+
describe 'Invalid domain', vcr: vcr_opts do
|
49
|
+
let(:domain) { 'not-our-doma.in' }
|
50
|
+
let(:config) do
|
51
|
+
{
|
52
|
+
api_key: APIKEY,
|
53
|
+
domain: domain
|
54
|
+
}
|
55
|
+
end
|
56
|
+
let(:mail) { UnitTestMailer.plain_message("bob@#{domain}", 'sally@not-our-doma.in' 'subject', {}) }
|
57
|
+
|
58
|
+
it 'raises expected error' do
|
59
|
+
|
60
|
+
Railgun::Mailer.new(config).deliver!(mail)
|
61
|
+
rescue Mailgun::CommunicationError => err
|
62
|
+
expect(err.message).to eq('401 Unauthorized: Forbidden - Invalid Domain or API key')
|
63
|
+
else
|
64
|
+
fail
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|