mailgun-ruby 1.1.10 → 1.2.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2e88262b939edf330c91ac551b2f5945e4f4e5825f734bef28a7455cc2ec2dcc
4
- data.tar.gz: 4bfffa2ae623704dcef2754a040fccc3a5efbbe8ee6e761e473215fbfcade5b8
3
+ metadata.gz: eeb68d33e0686c3074e829fa7c33c1a9300c31bd9db3b7fde74abfdbdd681a31
4
+ data.tar.gz: dc0cd7912abb804476731ba487186a362595f7806d88f50bbbe2296da8eb0b5b
5
5
  SHA512:
6
- metadata.gz: c8b5a20d3cf98827f101e5e06f563e605a80a626f2d4e5f4147f908d955e13926e3f2995db6bfbeb26b8ff343618104f2d83f298c808416362f4c0cae7a2d9ec
7
- data.tar.gz: 7bb9a9085b738d2cb0bd8bc57f176928d7eb54bf09a190bfd6fc0d21ae526a0f72b70970c2bd19a9aa899401cc3bce1d591089a14aa7ec324b94758b2ed3caa0
6
+ metadata.gz: 9469121fbdd8341af7cc6ee395d35d8f487f556b67d8b6a0d2b6cd874f4bb1828384f9aff7c6751377fa56182b7c890947e40444f09d583d97aca8f90c41c642
7
+ data.tar.gz: 2f2b70e53e187c28ea3fcf3d3abc3f208886baca01732af0bb233c154bd171565790279620fe392259f3fa74151276d4e281389606d35ce60047b6f9a922a7cf
data/.travis.yml CHANGED
@@ -1,10 +1,11 @@
1
1
  language: ruby
2
2
  sudo: false
3
3
  rvm:
4
- - 2.0.0
5
- - 2.1
6
- - 2.2
7
- - 2.3.1
4
+ - 2.2.2
5
+ - 2.2.10
6
+ - 2.3.7
7
+ - 2.4.4
8
+ - 2.5.1
8
9
  script:
9
10
  - bundle install
10
11
  - bundle exec rake spec
@@ -17,7 +18,7 @@ deploy:
17
18
  gemspec: mailgun.gemspec
18
19
  on:
19
20
  tags: true
20
- condition: "$TRAVIS_RUBY_VERSION == 2.3.1"
21
+ condition: "$TRAVIS_RUBY_VERSION == 2.5.1"
21
22
  notifications:
22
23
  slack:
23
24
  rooms:
data/Gemfile CHANGED
@@ -3,4 +3,4 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in mailgun.gemspec
4
4
  gemspec
5
5
 
6
- gem 'json', '~> 1.8', platform: :mri_19
6
+ gem 'json', '~> 2.1', platform: :mri_19
data/README.md CHANGED
@@ -19,7 +19,7 @@ gem install mailgun-ruby
19
19
  Gemfile:
20
20
 
21
21
  ```ruby
22
- gem 'mailgun-ruby', '~>1.1.6'
22
+ gem 'mailgun-ruby', '~>1.2.5'
23
23
  ```
24
24
 
25
25
  Usage
@@ -56,6 +56,12 @@ domain = 'example.com'
56
56
  result = mg_client.get("#{domain}/events", {:event => 'delivered'})
57
57
  ```
58
58
 
59
+ If you're using the EU domains, make sure you specify it when creating the client:
60
+
61
+ ```
62
+ mg_client = Mailgun::Client.new 'your-api-key', 'api.eu.mailgun.net'
63
+ ```
64
+
59
65
  Rails
60
66
  -----
61
67
 
@@ -74,9 +80,25 @@ and replace `api-myapikey` and `mydomain.com` with your secret API key and domai
74
80
  config.action_mailer.mailgun_settings = {
75
81
  api_key: 'api-myapikey',
76
82
  domain: 'mydomain.com',
83
+ # api_host: 'api.eu.mailgun.net' # Uncomment this line for EU region domains
77
84
  }
78
85
  ```
79
86
 
87
+ To specify Mailgun options such as campaign or tags:
88
+ ```ruby
89
+ class UserMailer < ApplicationMailer
90
+ def welcome_email
91
+ mail(to: params[:to], subject: "Welcome!").tap do |message|
92
+ message.mailgun_options = {
93
+ "tag" => ["abtest-option-a", "beta-user"],
94
+ "tracking-opens" => true,
95
+ "tracking-clicks" => "htmlonly"
96
+ }
97
+ end
98
+ end
99
+ end
100
+ ```
101
+
80
102
  To get the Mailgun `message_id` after ActionMailer has successfully delivered the email:
81
103
 
82
104
  ```ruby
data/docs/Domains.md CHANGED
File without changes
data/docs/Webhooks.md CHANGED
File without changes
@@ -0,0 +1,11 @@
1
+ Overview
2
+ ========
3
+
4
+ Railgun is a Rails add-on that allows ActionMailer to send via the Mailgun API.
5
+
6
+ See [railgun-sample](https://github.com/pirogoeth/railgun-sample/) for examples of integrating Railgun with your Rails app.
7
+
8
+
9
+ ## Table of Contents
10
+
11
+ - [Parameters](/docs/railgun/Parameters.md)
@@ -0,0 +1,83 @@
1
+ Parameters
2
+ ==========
3
+
4
+ When sending messages via Railgun, it is often useful to set options, headers, and variables
5
+ that should be added to the `POST` request against the messages endpoint.
6
+
7
+
8
+ ## Options
9
+
10
+ See [Mailgun Docs | Sending](https://documentation.mailgun.com/en/latest/api-sending.html#sending) for available options.
11
+
12
+ ---
13
+
14
+ To set options on a message:
15
+
16
+ ```ruby
17
+ # app/controllers/some_controller.rb
18
+
19
+ message = YourMailer.your_message(@args)
20
+
21
+ message.mailgun_options ||= {
22
+ "tracking-opens" => "true",
23
+ "tracking-clicks" => "htmlonly",
24
+ "tag" => "some,tags",
25
+ }
26
+ ```
27
+
28
+
29
+ ## Variables
30
+
31
+ See [Mailgun Docs | Attaching Data to Messages](https://documentation.mailgun.com/en/latest/user_manual.html#attaching-data-to-messages) for more information.
32
+
33
+ ---
34
+
35
+ To set variables on a message:
36
+
37
+ ```ruby
38
+ # app/controllers/some_controller.rb
39
+
40
+ message = YourMailer.your_message(@args)
41
+
42
+ message.mailgun_variables ||= {
43
+ "user_info" => {"id" => "1", "name" => "tstark"},
44
+ }
45
+ ```
46
+
47
+
48
+ ## Headers
49
+
50
+ See [Mailgun Docs | Sending](https://documentation.mailgun.com/en/latest/api-sending.html#sending) for more information.
51
+
52
+ ---
53
+
54
+ To set headers on a message *from a controller*:
55
+
56
+ ```ruby
57
+ # app/controllers/some_controller.rb
58
+
59
+ message = YourMailer.your_message(@args)
60
+
61
+ message.mailgun_headers ||= {
62
+ "X-Sent-From-Rails" => "true",
63
+ }
64
+ ```
65
+
66
+ To set headers on a message *from a mailer*:
67
+
68
+ ```ruby
69
+ # app/mailers/your_mailer.rb
70
+
71
+ class YourMailer < ApplicationMailer
72
+ # ...
73
+
74
+ def your_message(args)
75
+ headers({
76
+ "X-Sent-From-Rails" => "true",
77
+ })
78
+
79
+ mail to: "some-address@example.org", ...
80
+ end
81
+
82
+ end
83
+ ```
@@ -14,13 +14,20 @@ module Mailgun
14
14
  api_host = 'api.mailgun.net',
15
15
  api_version = 'v3',
16
16
  ssl = true,
17
- test_mode = false)
17
+ test_mode = false,
18
+ timeout = nil,
19
+ proxy_url = nil)
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
18
27
 
19
28
  endpoint = endpoint_generator(api_host, api_version, ssl)
20
- @http_client = RestClient::Resource.new(endpoint,
21
- user: 'api',
22
- password: api_key,
23
- user_agent: "mailgun-sdk-ruby/#{Mailgun::VERSION}")
29
+ RestClient.proxy = proxy_url
30
+ @http_client = RestClient::Resource.new(endpoint, rest_client_params)
24
31
  @test_mode = test_mode
25
32
  end
26
33
 
@@ -59,11 +66,13 @@ module Mailgun
59
66
  # containing required parameters for the requested resource.
60
67
  # @return [Mailgun::Response] A Mailgun::Response object.
61
68
  def send_message(working_domain, data)
69
+ perform_data_validation(working_domain, data)
70
+
62
71
  if test_mode? then
63
72
  Mailgun::Client.deliveries << data
64
73
  return Response.from_hash(
65
74
  {
66
- :body => '{"id": "test-mode-mail@localhost", "message": "Queued. Thank you."}',
75
+ :body => "{\"id\": \"test-mode-mail-#{SecureRandom.uuid}@localhost\", \"message\": \"Queued. Thank you.\"}",
67
76
  :code => 200,
68
77
  }
69
78
  )
@@ -195,5 +204,17 @@ module Mailgun
195
204
  CommunicationError.new(e.message)
196
205
  end
197
206
 
207
+ def perform_data_validation(working_domain, data)
208
+ message = data.respond_to?(:message) ? data.message : data
209
+ fail ParameterError.new('Missing working domain', working_domain) unless working_domain
210
+ fail ParameterError.new(
211
+ 'Missing `to` recipient, message should containg at least 1 recipient',
212
+ working_domain
213
+ ) if message.fetch('to', []).empty? && message.fetch(:to, []).empty?
214
+ fail ParameterError.new(
215
+ 'Missing a `from` sender, message should containg at least 1 `from` sender',
216
+ working_domain
217
+ ) if message.fetch('from', []).empty? && message.fetch(:from, []).empty?
218
+ end
198
219
  end
199
220
  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
  #
@@ -51,6 +52,7 @@ module Mailgun
51
52
  rescue NoMethodError
52
53
  api_message = "Unknown API error"
53
54
  end
55
+ api_message = api_message + ' - Invalid Domain or API key' if api_message == FORBIDDEN
54
56
 
55
57
  message = message || ''
56
58
  message = message + ': ' + api_message
@@ -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
- set_multi_simple('o:tracking-opens', bool_lookup(mode))
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
- set_multi_simple('o:tracking-clicks', bool_lookup(mode))
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
- jsondata = make_json data
273
- set_single("v:#{name}", jsondata)
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
 
@@ -389,7 +431,7 @@ module Mailgun
389
431
  full_name = "#{vars['first']} #{vars['last']}".strip
390
432
  end
391
433
 
392
- return "'#{full_name}' <#{address}>" if defined?(full_name)
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'
@@ -157,7 +157,10 @@ module Mailgun
157
157
 
158
158
  unsubscribe.each do |k, v|
159
159
  # Hash values MUST be strings.
160
- if not v.is_a? String then
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
@@ -1,4 +1,4 @@
1
1
  # It's the version. Yeay!
2
2
  module Mailgun
3
- VERSION = '1.1.10'
3
+ VERSION = '1.2.5'
4
4
  end
@@ -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(config[:api_key], config[:api_host] || 'api.mailgun.net', config[:api_version] || 'v3', config[:api_ssl].nil? ? true : config[:api_ssl])
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,11 @@ module Railgun
36
47
  end
37
48
 
38
49
  def deliver!(mail)
50
+ @mg_domain = set_mg_domain(mail)
51
+ mail[:domain] = nil if mail[:domain].present?
52
+
39
53
  mg_message = Railgun.transform_for_mailgun(mail)
40
- response = @mg_client.send_message(@domain, mg_message)
54
+ response = @mg_client.send_message(@mg_domain, mg_message)
41
55
 
42
56
  if response.code == 200 then
43
57
  mg_id = response.to_h['id']
@@ -47,7 +61,15 @@ module Railgun
47
61
  end
48
62
 
49
63
  def mailgun_client
50
- @mg_obj
64
+ @mg_client
65
+ end
66
+
67
+ private
68
+
69
+ # Set @mg_domain from mail[:domain] header if present, then remove it to prevent being sent.
70
+ def set_mg_domain(mail)
71
+ return mail[:domain].value if mail[:domain]
72
+ domain
51
73
  end
52
74
 
53
75
  end
@@ -58,24 +80,60 @@ module Railgun
58
80
  # After prefixing them with the proper option type, they are added to
59
81
  # the message hash where they will then be sent to the API as JSON.
60
82
  #
83
+ # It is important to note that headers set in `mailgun_headers` on the message
84
+ # WILL overwrite headers set via `mail.headers()`.
85
+ #
61
86
  # @param [Mail::Message] mail message to transform
62
87
  #
63
88
  # @return [Hash] transformed message hash
64
89
  def transform_for_mailgun(mail)
65
90
  message = build_message_object(mail)
66
91
 
67
- # v:* attributes (variables)
68
- mail.mailgun_variables.try(:each) do |k, v|
69
- message["v:#{k}"] = v
70
- end
71
-
72
92
  # o:* attributes (options)
73
93
  mail.mailgun_options.try(:each) do |k, v|
74
- message["o:#{k}"] = v
94
+ message["o:#{k}"] = v.dup
95
+ end
96
+
97
+ # t:* attributes (options)
98
+ mail.mailgun_template_variables.try(:each) do |k, v|
99
+ message["t:#{k}"] = v.dup
75
100
  end
76
101
 
102
+ # support for using ActionMailer's `headers()` inside of the mailer
103
+ # note: this will filter out parameters such as `from`, `to`, and so forth
104
+ # as they are accepted as POST parameters on the message endpoint.
105
+
106
+ msg_headers = Hash.new
107
+
77
108
  # h:* attributes (headers)
78
- mail.mailgun_headers.try(:each) do |k, v|
109
+
110
+ # Let's set all of these headers on the [Mail::Message] so that
111
+ # the are created inside of a [Mail::Header] instance and processed there.
112
+ mail.headers(mail.mailgun_headers || {})
113
+ mail.header_fields.each do |field|
114
+ header = field.name.downcase
115
+ if msg_headers.include? header
116
+ msg_headers[header] = [msg_headers[header], field.value].flatten
117
+ else
118
+ msg_headers[header] = field.value
119
+ end
120
+ end
121
+
122
+ msg_headers.each do |k, v|
123
+ if Railgun::Mailer::IGNORED_HEADERS.include? k.downcase
124
+ Rails.logger.debug("[railgun] ignoring header (using envelope instead): #{k}")
125
+ next
126
+ end
127
+
128
+ # Cover cases like `cc`, `bcc` where parameters are valid
129
+ # headers BUT they are submitted as separate POST params
130
+ # and already exist on the message because of the call to
131
+ # `build_message_object`.
132
+ if message.include? k.downcase
133
+ Rails.logger.debug("[railgun] ignoring header (already set): #{k}")
134
+ next
135
+ end
136
+
79
137
  message["h:#{k}"] = v
80
138
  end
81
139
 
@@ -84,7 +142,12 @@ module Railgun
84
142
 
85
143
  # reject blank values
86
144
  message.delete_if do |k, v|
87
- v.nil? or (v.respond_to?(:empty) and v.empty?)
145
+ return true if v.nil?
146
+
147
+ # if it's an array remove empty elements
148
+ v.delete_if { |i| i.respond_to?(:empty?) && i.empty? } if v.is_a?(Array)
149
+
150
+ v.respond_to?(:empty?) && v.empty?
88
151
  end
89
152
 
90
153
  return message
@@ -101,6 +164,7 @@ module Railgun
101
164
 
102
165
  mb.from mail[:from]
103
166
  mb.reply_to(mail[:reply_to].to_s) if mail[:reply_to].present?
167
+ mb.template(mail[:template].to_s) if mail[:template].present?
104
168
  mb.subject mail.subject
105
169
  mb.body_html extract_body_html(mail)
106
170
  mb.body_text extract_body_text(mail)
@@ -120,6 +184,11 @@ module Railgun
120
184
  end
121
185
  end
122
186
 
187
+ # v:* attributes (variables)
188
+ mail.mailgun_variables.try(:each) do |name, value|
189
+ mb.variable(name, value)
190
+ end
191
+
123
192
  return mb.message if mail.attachments.empty?
124
193
 
125
194
  mail.attachments.each do |attach|
@@ -181,5 +250,4 @@ module Railgun
181
250
  return mail.html_part if mail.multipart?
182
251
  (mail.mime_type =~ /^text\/html$/i) && mail
183
252
  end
184
-
185
253
  end