mailgun-ruby 1.1.9 → 1.2.12
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/.rubocop.yml +0 -0
- data/.rubocop_todo.yml +0 -0
- data/.ruby-env.yml.example +0 -0
- data/.travis.yml +6 -5
- data/CHANGELOG.md +16 -0
- data/Gemfile +1 -1
- data/README.md +27 -3
- data/docs/Domains.md +3 -0
- data/docs/OptInHandler.md +1 -1
- data/docs/Snippets.md +54 -61
- data/docs/Webhooks.md +0 -0
- data/docs/railgun/Overview.md +11 -0
- data/docs/railgun/Parameters.md +83 -0
- data/docs/railgun/Templates.md +92 -0
- data/lib/mailgun/address.rb +3 -28
- data/lib/mailgun/chains.rb +0 -0
- data/lib/mailgun/client.rb +44 -9
- data/lib/mailgun/domains/domains.rb +20 -2
- data/lib/mailgun/events/events.rb +2 -2
- data/lib/mailgun/exceptions/exceptions.rb +28 -1
- data/lib/mailgun/messages/batch_message.rb +1 -0
- data/lib/mailgun/messages/message_builder.rb +56 -8
- data/lib/mailgun/response.rb +7 -0
- data/lib/mailgun/suppressions.rb +15 -8
- data/lib/mailgun/templates/templates.rb +187 -0
- data/lib/mailgun/version.rb +1 -1
- data/lib/mailgun/webhooks/webhooks.rb +2 -2
- data/lib/mailgun-ruby.rb +1 -1
- data/lib/mailgun.rb +4 -1
- data/lib/railgun/mailer.rb +83 -12
- data/lib/railgun/message.rb +2 -1
- data/lib/railgun/railtie.rb +3 -2
- data/mailgun.gemspec +15 -12
- data/spec/integration/bounces_spec.rb +3 -3
- data/spec/integration/campaign_spec.rb +0 -0
- data/spec/integration/complaints_spec.rb +0 -0
- data/spec/integration/domains_spec.rb +8 -0
- data/spec/integration/email_validation_spec.rb +10 -2
- data/spec/integration/events_spec.rb +1 -1
- data/spec/integration/list_members_spec.rb +0 -0
- data/spec/integration/list_spec.rb +0 -0
- data/spec/integration/mailer_spec.rb +67 -0
- data/spec/integration/mailgun_spec.rb +92 -1
- data/spec/integration/routes_spec.rb +0 -0
- data/spec/integration/stats_spec.rb +0 -0
- data/spec/integration/suppressions_spec.rb +18 -2
- data/spec/integration/templates_spec.rb +135 -0
- data/spec/integration/unsubscribes_spec.rb +0 -0
- data/spec/integration/webhook_spec.rb +0 -0
- data/spec/spec_helper.rb +3 -1
- data/spec/unit/connection/test_client.rb +18 -1
- data/spec/unit/events/events_spec.rb +19 -0
- 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 +388 -0
- data/vcr_cassettes/bounces.yml +12 -12
- data/vcr_cassettes/complaints.yml +0 -0
- data/vcr_cassettes/domains.todo.yml +0 -0
- data/vcr_cassettes/domains.yml +51 -1
- data/vcr_cassettes/email_validation.yml +5 -5
- data/vcr_cassettes/events.yml +0 -0
- 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/list_members.yml +0 -0
- data/vcr_cassettes/mailer_invalid_domain.yml +109 -0
- data/vcr_cassettes/mailing_list.todo.yml +0 -0
- data/vcr_cassettes/mailing_list.yml +0 -0
- data/vcr_cassettes/message_deliver.yml +149 -0
- data/vcr_cassettes/routes.yml +0 -0
- data/vcr_cassettes/send_message.yml +0 -0
- data/vcr_cassettes/stats.yml +0 -0
- data/vcr_cassettes/suppressions.yml +66 -15
- data/vcr_cassettes/templates.yml +1065 -0
- data/vcr_cassettes/unsubscribes.yml +0 -0
- data/vcr_cassettes/webhooks.yml +0 -0
- metadata +49 -29
- data/.ruby-version +0 -1
- /data/spec/unit/{railgun_spec.rb → railgun/content_type_spec.rb} +0 -0
@@ -0,0 +1,187 @@
|
|
1
|
+
require 'mailgun/exceptions/exceptions'
|
2
|
+
|
3
|
+
module Mailgun
|
4
|
+
|
5
|
+
# A Mailgun::Templates object is a simple CRUD interface to Mailgun Templates.
|
6
|
+
# Uses Mailgun
|
7
|
+
class Templates
|
8
|
+
|
9
|
+
# Public: creates a new Mailgun::Templates instance.
|
10
|
+
# Defaults to Mailgun::Client
|
11
|
+
def initialize(client = Mailgun::Client.new)
|
12
|
+
@client = client
|
13
|
+
end
|
14
|
+
|
15
|
+
# Public: Add template
|
16
|
+
#
|
17
|
+
# domain - [String] Name of the domain for new template(ex. domain.com)
|
18
|
+
# options - [Hash] of
|
19
|
+
# name - [String] Name of the template being stored.
|
20
|
+
# description - [String] Description of the template being stored
|
21
|
+
# template - [String] (Optional) Content of the template
|
22
|
+
# tag - [String] (Optional) Initial tag of the created version.
|
23
|
+
# comment - [String] (Optional) Version comment.
|
24
|
+
# headers - [String] (Optional) Key Value json dictionary of headers to be stored with the template.
|
25
|
+
# ex.('{"Subject": "{{subject}}"}')
|
26
|
+
#
|
27
|
+
# Returns [Hash] of created template
|
28
|
+
def create(domain, options = {})
|
29
|
+
fail(ParameterError, 'No domain given to store template on', caller) unless domain
|
30
|
+
@client.post("#{domain}/templates", options).to_h
|
31
|
+
end
|
32
|
+
|
33
|
+
# Public: Get template information
|
34
|
+
#
|
35
|
+
# domain - [String] Domain name where template is stored
|
36
|
+
# template_name - [String] Template name to lookup for
|
37
|
+
# options - [Hash] of
|
38
|
+
# active - [Boolean] (Optional) If this flag is set to yes the active version
|
39
|
+
# of the template is included in the response.
|
40
|
+
#
|
41
|
+
# Returns [Hash] Information on the requested template.
|
42
|
+
def info(domain, template_name, options = {})
|
43
|
+
fail(ParameterError, 'No domain given to find on Mailgun', caller) unless domain
|
44
|
+
fail(ParameterError, 'No template name given to find on provided domain', caller) unless template_name
|
45
|
+
@client.get("#{domain}/templates/#{template_name}", options).to_h!
|
46
|
+
end
|
47
|
+
|
48
|
+
# Public: Update the metadata information of the template
|
49
|
+
#
|
50
|
+
# domain - [String] Domain name where template is stored
|
51
|
+
# template_name - [String] Template name to lookup for
|
52
|
+
# options - [Hash] of
|
53
|
+
# description - [String] Updated description of the template
|
54
|
+
#
|
55
|
+
# Returns [Hash] of updated domain
|
56
|
+
def update(domain, template_name, options = {})
|
57
|
+
fail(ParameterError, 'No domain given to add on Mailgun', caller) unless domain
|
58
|
+
fail(ParameterError, 'No template name given to find on provided domain', caller) unless template_name
|
59
|
+
@client.put("#{domain}/templates/#{template_name}", options).to_h
|
60
|
+
end
|
61
|
+
|
62
|
+
# Public: Delete Template
|
63
|
+
# NOTE: This method deletes all versions of the specified template.
|
64
|
+
#
|
65
|
+
# domain - [String] Domain name where template is stored
|
66
|
+
# template_name - [String] Template name to lookup for
|
67
|
+
#
|
68
|
+
# Returns [Boolean] if successful or not
|
69
|
+
def remove(domain, template_name)
|
70
|
+
fail(ParameterError, 'No domain given to remove on Mailgun', caller) unless domain
|
71
|
+
fail(ParameterError, 'No template name given to find on provided domain', caller) unless template_name
|
72
|
+
@client.delete("#{domain}/templates/#{template_name}").to_h['message'] == 'template has been deleted'
|
73
|
+
end
|
74
|
+
alias_method :delete, :remove
|
75
|
+
alias_method :delete_template, :remove
|
76
|
+
|
77
|
+
# Public: Get Templates
|
78
|
+
#
|
79
|
+
# domain - [String] Domain name where template is stored
|
80
|
+
# page - [String] Name of a page to retrieve. first, last, next, prev
|
81
|
+
# limit - [Integer] Maximum number of records to return. (100 by default)
|
82
|
+
# p - [Integer] Pivot is used to retrieve records in chronological order
|
83
|
+
#
|
84
|
+
# Returns [Array] A list of templates (hash)
|
85
|
+
def list(domain, options = {})
|
86
|
+
fail(ParameterError, 'No domain given.', caller) unless domain
|
87
|
+
@client.get("#{domain}/templates", options).to_h['items']
|
88
|
+
end
|
89
|
+
alias_method :get_templates, :list
|
90
|
+
|
91
|
+
# Public: Delete Templates
|
92
|
+
# NOTE: This method deletes all stored templates for the domain.
|
93
|
+
#
|
94
|
+
# domain - [String] Domain name where template is stored
|
95
|
+
#
|
96
|
+
# Returns [Boolean] if successful or not
|
97
|
+
def remove_all(domain)
|
98
|
+
fail(ParameterError, 'No domain given to remove on Mailgun', caller) unless domain
|
99
|
+
@client.delete("#{domain}/templates").to_h['message'] == 'templates have been deleted'
|
100
|
+
end
|
101
|
+
alias_method :delete_templates, :remove_all
|
102
|
+
|
103
|
+
# Public: Create a new version of a template
|
104
|
+
#
|
105
|
+
# domain - [String] Name of the domain for new template(ex. domain.com)
|
106
|
+
# template_name - [String] Template name to lookup for
|
107
|
+
# options - [Hash] of
|
108
|
+
# template - [String] Content of the template
|
109
|
+
# tag - [String] Initial tag of the created version.
|
110
|
+
# comment - [String] (Optional) Version comment.
|
111
|
+
# active - [Boolean] (Optional) If this flag is set to yes, this version becomes active
|
112
|
+
# headers - [String] (Optional) Key Value json dictionary of headers to be stored with the template.
|
113
|
+
# ex.('{"Subject": "{{subject}}"}')
|
114
|
+
#
|
115
|
+
# Returns [Hash] of updated template
|
116
|
+
def create_version(domain, template_name, options = {})
|
117
|
+
fail(ParameterError, 'No domain given.', caller) unless domain
|
118
|
+
fail(ParameterError, 'No template name given.', caller) unless template_name
|
119
|
+
@client.post("#{domain}/templates/#{template_name}/versions", options).to_h
|
120
|
+
end
|
121
|
+
|
122
|
+
# Public: Get template version information
|
123
|
+
#
|
124
|
+
# domain - [String] Domain name where template is stored
|
125
|
+
# template_name - [String] Template name to lookup for
|
126
|
+
# tag - [String] Version tag to lookup for
|
127
|
+
#
|
128
|
+
# Returns [Hash] Information on the requested template + version.
|
129
|
+
def info_version(domain, template_name, tag)
|
130
|
+
fail(ParameterError, 'No domain given to find on Mailgun', caller) unless domain
|
131
|
+
fail(ParameterError, 'No template name given to find on provided domain', caller) unless template_name
|
132
|
+
fail(ParameterError, 'No version tag given.', caller) unless tag
|
133
|
+
@client.get("#{domain}/templates/#{template_name}/versions/#{tag}").to_h!
|
134
|
+
end
|
135
|
+
|
136
|
+
# Public: Update the version of the template
|
137
|
+
#
|
138
|
+
# domain - [String] Domain name where template is stored
|
139
|
+
# template_name - [String] Template name to lookup for
|
140
|
+
# tag - [String] Version tag to lookup for
|
141
|
+
# options - [Hash] of
|
142
|
+
# template - [String] Content of the template
|
143
|
+
# comment - [String] (Optional) Version comment.
|
144
|
+
# active - [Boolean] (Optional) If this flag is set to yes, this version becomes active
|
145
|
+
# headers - [String] (Optional) Key Value json dictionary of headers to be stored with the template.
|
146
|
+
# ex.('{"Subject": "{{subject}}"}')
|
147
|
+
#
|
148
|
+
# Returns [Hash] of updated template's version
|
149
|
+
def update_version(domain, template_name, tag, options = {})
|
150
|
+
fail(ParameterError, 'No domain given.', caller) unless domain
|
151
|
+
fail(ParameterError, 'No template name given to find on provided domain.', caller) unless template_name
|
152
|
+
fail(ParameterError, 'No version tag given.', caller) unless tag
|
153
|
+
@client.put("#{domain}/templates/#{template_name}/versions/#{tag}", options).to_h
|
154
|
+
end
|
155
|
+
|
156
|
+
# Public: Delete the version of the template
|
157
|
+
#
|
158
|
+
# domain - [String] Domain name where template is stored
|
159
|
+
# template_name - [String] Template name to lookup for
|
160
|
+
# tag - [String] Version tag to lookup for
|
161
|
+
#
|
162
|
+
# Returns [Boolean] if successful or not
|
163
|
+
def delete_version(domain, template_name, tag)
|
164
|
+
fail(ParameterError, 'No domain given.', caller) unless domain
|
165
|
+
fail(ParameterError, 'No template name given to find on provided domain.', caller) unless template_name
|
166
|
+
fail(ParameterError, 'No version tag given.', caller) unless tag
|
167
|
+
@client.delete("#{domain}/templates/#{template_name}/versions/#{tag}")
|
168
|
+
.to_h['message'] == 'version has been deleted'
|
169
|
+
end
|
170
|
+
|
171
|
+
# Public: Get Template's Versions list
|
172
|
+
#
|
173
|
+
# domain - [String] Domain name where template is stored
|
174
|
+
# template_name - [String] Template name to lookup for
|
175
|
+
# options - [Hash] of
|
176
|
+
# page - [String] Name of a page to retrieve. first, last, next, prev
|
177
|
+
# limit - [Integer] Maximum number of records to return. (100 by default)
|
178
|
+
# p - [Integer] Pivot is used to retrieve records in chronological order
|
179
|
+
#
|
180
|
+
# Returns [Array] A list of template's versions (hash)
|
181
|
+
def template_versions_list(domain, template_name, options = {})
|
182
|
+
fail(ParameterError, 'No domain given.', caller) unless domain
|
183
|
+
fail(ParameterError, 'No template name given to find on provided domain.', caller) unless template_name
|
184
|
+
@client.get("#{domain}/templates/#{template_name}/versions", options).to_h
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
data/lib/mailgun/version.rb
CHANGED
@@ -58,7 +58,7 @@ module Mailgun
|
|
58
58
|
#
|
59
59
|
# Returns true or false
|
60
60
|
def create_all(domain, url = '')
|
61
|
-
%w(
|
61
|
+
%w(accepted clicked complained delivered opened permanent_fail temporary_fail unsubscribed).each do |action|
|
62
62
|
add_webhook domain, action, url
|
63
63
|
end
|
64
64
|
true
|
@@ -90,7 +90,7 @@ module Mailgun
|
|
90
90
|
# Returns a Boolean on the success
|
91
91
|
def remove_all(domain)
|
92
92
|
fail Mailgun::ParameterError('Domain not provided to remove webhooks from') unless domain
|
93
|
-
%w(
|
93
|
+
%w(accepted clicked complained delivered opened permanent_fail temporary_fail unsubscribed).each do |action|
|
94
94
|
delete_webhook domain, action
|
95
95
|
end
|
96
96
|
end
|
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
@@ -15,6 +15,7 @@ require 'mailgun/events/events'
|
|
15
15
|
require 'mailgun/exceptions/exceptions'
|
16
16
|
require 'mailgun/domains/domains'
|
17
17
|
require 'mailgun/webhooks/webhooks'
|
18
|
+
require 'mailgun/templates/templates'
|
18
19
|
|
19
20
|
# Module for interacting with the sweet Mailgun API.
|
20
21
|
#
|
@@ -22,10 +23,12 @@ require 'mailgun/webhooks/webhooks'
|
|
22
23
|
module Mailgun
|
23
24
|
|
24
25
|
class << self
|
25
|
-
attr_accessor :
|
26
|
+
attr_accessor :api_host,
|
27
|
+
:api_key,
|
26
28
|
:api_version,
|
27
29
|
:protocol,
|
28
30
|
:mailgun_host,
|
31
|
+
:proxy_url,
|
29
32
|
:test_mode,
|
30
33
|
:domain
|
31
34
|
|
data/lib/railgun/mailer.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'action_mailer'
|
2
|
+
require 'json'
|
2
3
|
require 'mailgun'
|
3
4
|
require 'rails'
|
4
5
|
require 'railgun/errors'
|
@@ -9,6 +10,9 @@ module Railgun
|
|
9
10
|
# Mailgun.
|
10
11
|
class Mailer
|
11
12
|
|
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 mime-version template ]
|
15
|
+
|
12
16
|
# [Hash] config ->
|
13
17
|
# Requires *at least* `api_key` and `domain` keys.
|
14
18
|
attr_accessor :config, :domain, :settings
|
@@ -23,7 +27,14 @@ module Railgun
|
|
23
27
|
raise Railgun::ConfigurationError.new("Config requires `#{k}` key", @config) unless @config.has_key?(k)
|
24
28
|
end
|
25
29
|
|
26
|
-
@mg_client = Mailgun::Client.new(
|
30
|
+
@mg_client = Mailgun::Client.new(
|
31
|
+
config[:api_key],
|
32
|
+
config[:api_host] || 'api.mailgun.net',
|
33
|
+
config[:api_version] || 'v3',
|
34
|
+
config[:api_ssl].nil? ? true : config[:api_ssl],
|
35
|
+
false,
|
36
|
+
config[:timeout]
|
37
|
+
)
|
27
38
|
@domain = @config[:domain]
|
28
39
|
|
29
40
|
# To avoid exception in mail gem v2.6
|
@@ -36,8 +47,14 @@ module Railgun
|
|
36
47
|
end
|
37
48
|
|
38
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
|
+
|
39
56
|
mg_message = Railgun.transform_for_mailgun(mail)
|
40
|
-
response = @mg_client.send_message(@
|
57
|
+
response = @mg_client.send_message(@mg_domain, mg_message)
|
41
58
|
|
42
59
|
if response.code == 200 then
|
43
60
|
mg_id = response.to_h['id']
|
@@ -47,7 +64,15 @@ module Railgun
|
|
47
64
|
end
|
48
65
|
|
49
66
|
def mailgun_client
|
50
|
-
@
|
67
|
+
@mg_client
|
68
|
+
end
|
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
|
51
76
|
end
|
52
77
|
|
53
78
|
end
|
@@ -58,24 +83,60 @@ module Railgun
|
|
58
83
|
# After prefixing them with the proper option type, they are added to
|
59
84
|
# the message hash where they will then be sent to the API as JSON.
|
60
85
|
#
|
86
|
+
# It is important to note that headers set in `mailgun_headers` on the message
|
87
|
+
# WILL overwrite headers set via `mail.headers()`.
|
88
|
+
#
|
61
89
|
# @param [Mail::Message] mail message to transform
|
62
90
|
#
|
63
91
|
# @return [Hash] transformed message hash
|
64
92
|
def transform_for_mailgun(mail)
|
65
93
|
message = build_message_object(mail)
|
66
94
|
|
67
|
-
# v:* attributes (variables)
|
68
|
-
mail.mailgun_variables.try(:each) do |k, v|
|
69
|
-
message["v:#{k}"] = v
|
70
|
-
end
|
71
|
-
|
72
95
|
# o:* attributes (options)
|
73
96
|
mail.mailgun_options.try(:each) do |k, v|
|
74
|
-
message["o:#{k}"] = v
|
97
|
+
message["o:#{k}"] = v.dup
|
75
98
|
end
|
76
99
|
|
100
|
+
# t:* attributes (options)
|
101
|
+
mail.mailgun_template_variables.try(:each) do |k, v|
|
102
|
+
message["t:#{k}"] = v.dup
|
103
|
+
end
|
104
|
+
|
105
|
+
# support for using ActionMailer's `headers()` inside of the mailer
|
106
|
+
# note: this will filter out parameters such as `from`, `to`, and so forth
|
107
|
+
# as they are accepted as POST parameters on the message endpoint.
|
108
|
+
|
109
|
+
msg_headers = Hash.new
|
110
|
+
|
77
111
|
# h:* attributes (headers)
|
78
|
-
|
112
|
+
|
113
|
+
# Let's set all of these headers on the [Mail::Message] so that
|
114
|
+
# the are created inside of a [Mail::Header] instance and processed there.
|
115
|
+
mail.headers(mail.mailgun_headers || {})
|
116
|
+
mail.header_fields.each do |field|
|
117
|
+
header = field.name.downcase
|
118
|
+
if msg_headers.include? header
|
119
|
+
msg_headers[header] = [msg_headers[header], field.value].flatten
|
120
|
+
else
|
121
|
+
msg_headers[header] = field.value
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
msg_headers.each do |k, v|
|
126
|
+
if Railgun::Mailer::IGNORED_HEADERS.include? k.downcase
|
127
|
+
Rails.logger.debug("[railgun] ignoring header (using envelope instead): #{k}")
|
128
|
+
next
|
129
|
+
end
|
130
|
+
|
131
|
+
# Cover cases like `cc`, `bcc` where parameters are valid
|
132
|
+
# headers BUT they are submitted as separate POST params
|
133
|
+
# and already exist on the message because of the call to
|
134
|
+
# `build_message_object`.
|
135
|
+
if message.include? k.downcase
|
136
|
+
Rails.logger.debug("[railgun] ignoring header (already set): #{k}")
|
137
|
+
next
|
138
|
+
end
|
139
|
+
|
79
140
|
message["h:#{k}"] = v
|
80
141
|
end
|
81
142
|
|
@@ -84,7 +145,12 @@ module Railgun
|
|
84
145
|
|
85
146
|
# reject blank values
|
86
147
|
message.delete_if do |k, v|
|
87
|
-
|
148
|
+
next true if v.nil?
|
149
|
+
|
150
|
+
# if it's an array remove empty elements
|
151
|
+
v.delete_if { |i| i.respond_to?(:empty?) && i.empty? } if v.is_a?(Array)
|
152
|
+
|
153
|
+
v.respond_to?(:empty?) && v.empty?
|
88
154
|
end
|
89
155
|
|
90
156
|
return message
|
@@ -101,6 +167,7 @@ module Railgun
|
|
101
167
|
|
102
168
|
mb.from mail[:from]
|
103
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?
|
104
171
|
mb.subject mail.subject
|
105
172
|
mb.body_html extract_body_html(mail)
|
106
173
|
mb.body_text extract_body_text(mail)
|
@@ -120,6 +187,11 @@ module Railgun
|
|
120
187
|
end
|
121
188
|
end
|
122
189
|
|
190
|
+
# v:* attributes (variables)
|
191
|
+
mail.mailgun_variables.try(:each) do |name, value|
|
192
|
+
mb.variable(name, value)
|
193
|
+
end
|
194
|
+
|
123
195
|
return mb.message if mail.attachments.empty?
|
124
196
|
|
125
197
|
mail.attachments.each do |attach|
|
@@ -181,5 +253,4 @@ module Railgun
|
|
181
253
|
return mail.html_part if mail.multipart?
|
182
254
|
(mail.mime_type =~ /^text\/html$/i) && mail
|
183
255
|
end
|
184
|
-
|
185
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
@@ -7,7 +7,7 @@ Gem::Specification.new do |spec|
|
|
7
7
|
|
8
8
|
spec.name = 'mailgun-ruby'
|
9
9
|
spec.version = Mailgun::VERSION
|
10
|
-
spec.homepage = '
|
10
|
+
spec.homepage = 'https://www.mailgun.com/'
|
11
11
|
spec.platform = Gem::Platform::RUBY
|
12
12
|
spec.license = 'Apache-2.0'
|
13
13
|
|
@@ -17,21 +17,24 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.authors = ['Mailgun', 'Travis Swientek']
|
18
18
|
spec.email = 'support@mailgunhq.com'
|
19
19
|
|
20
|
+
spec.metadata['documentation_uri'] = 'https://documentation.mailgun.com/'
|
21
|
+
spec.metadata['source_code_uri'] = 'https://github.com/mailgun/mailgun-ruby'
|
22
|
+
|
20
23
|
spec.files = `git ls-files -z`.split("\x0")
|
21
24
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
22
25
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
23
26
|
spec.require_paths = ["lib"]
|
24
27
|
|
25
|
-
spec.required_ruby_version = '>= 2.
|
26
|
-
|
27
|
-
spec.add_development_dependency 'bundler', '
|
28
|
-
spec.add_development_dependency 'rspec', '~> 3.0'
|
29
|
-
spec.add_development_dependency 'rake', '~>
|
30
|
-
spec.add_development_dependency 'webmock', '~>
|
31
|
-
spec.add_development_dependency 'pry', '~> 0.
|
32
|
-
spec.add_development_dependency 'vcr', '~> 3.0'
|
33
|
-
spec.add_development_dependency 'simplecov', '~> 0.
|
34
|
-
spec.add_development_dependency
|
35
|
-
spec.add_dependency 'rest-client', '
|
28
|
+
spec.required_ruby_version = '>= 2.2.2'
|
29
|
+
|
30
|
+
spec.add_development_dependency 'bundler', '>= 1.16.2'
|
31
|
+
spec.add_development_dependency 'rspec', '~> 3.8.0'
|
32
|
+
spec.add_development_dependency 'rake', '~> 12.3.2'
|
33
|
+
spec.add_development_dependency 'webmock', '~> 3.4.2'
|
34
|
+
spec.add_development_dependency 'pry', '~> 0.11.3'
|
35
|
+
spec.add_development_dependency 'vcr', '~> 3.0.3'
|
36
|
+
spec.add_development_dependency 'simplecov', '~> 0.16.1'
|
37
|
+
spec.add_development_dependency 'rails'
|
38
|
+
spec.add_dependency 'rest-client', '>= 2.0.2'
|
36
39
|
|
37
40
|
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
|
File without changes
|
File without changes
|
@@ -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"]
|
@@ -19,7 +19,7 @@ describe 'For the email validation endpoint', order: :defined, vcr: vcr_opts do
|
|
19
19
|
res = @mg_obj.parse(@all_addrs)
|
20
20
|
|
21
21
|
expect(res["parsed"]).to eq(@valid)
|
22
|
-
expect(res["
|
22
|
+
expect(res["unparsable"]).to eq(@invalid)
|
23
23
|
end
|
24
24
|
|
25
25
|
it 'validates alice@mailgun.net with info' do
|
@@ -28,7 +28,11 @@ describe 'For the email validation endpoint', order: :defined, vcr: vcr_opts do
|
|
28
28
|
expected = {
|
29
29
|
"address" => "alice@mailgun.net",
|
30
30
|
"did_you_mean" => nil,
|
31
|
+
"is_disposable_address" => false,
|
32
|
+
"is_role_address" => false,
|
31
33
|
"is_valid" => true,
|
34
|
+
"mailbox_verification" => "true",
|
35
|
+
"reason" => nil,
|
32
36
|
"parts" => {
|
33
37
|
"display_name" => nil,
|
34
38
|
"domain" => "mailgun.net",
|
@@ -50,7 +54,11 @@ describe 'For the email validation endpoint', order: :defined, vcr: vcr_opts do
|
|
50
54
|
expected = {
|
51
55
|
"address" => "example.org",
|
52
56
|
"did_you_mean" => nil,
|
57
|
+
"is_disposable_address" => false,
|
58
|
+
"is_role_address" => false,
|
53
59
|
"is_valid" => false,
|
60
|
+
"mailbox_verification" => "unknown",
|
61
|
+
"reason" => "Validation failed for 'example.org', reason: 'malformed address; missing @ sign'",
|
54
62
|
"parts" => {
|
55
63
|
"display_name" => nil,
|
56
64
|
"domain" => nil,
|
File without changes
|
File without changes
|
@@ -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
|