mailgun-ruby 1.1.9 → 1.2.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +0 -0
  3. data/.rubocop_todo.yml +0 -0
  4. data/.ruby-env.yml.example +0 -0
  5. data/.travis.yml +6 -5
  6. data/CHANGELOG.md +16 -0
  7. data/Gemfile +1 -1
  8. data/README.md +27 -3
  9. data/docs/Domains.md +3 -0
  10. data/docs/OptInHandler.md +1 -1
  11. data/docs/Snippets.md +54 -61
  12. data/docs/Webhooks.md +0 -0
  13. data/docs/railgun/Overview.md +11 -0
  14. data/docs/railgun/Parameters.md +83 -0
  15. data/docs/railgun/Templates.md +92 -0
  16. data/lib/mailgun/address.rb +3 -28
  17. data/lib/mailgun/chains.rb +0 -0
  18. data/lib/mailgun/client.rb +44 -9
  19. data/lib/mailgun/domains/domains.rb +20 -2
  20. data/lib/mailgun/events/events.rb +2 -2
  21. data/lib/mailgun/exceptions/exceptions.rb +28 -1
  22. data/lib/mailgun/messages/batch_message.rb +1 -0
  23. data/lib/mailgun/messages/message_builder.rb +56 -8
  24. data/lib/mailgun/response.rb +7 -0
  25. data/lib/mailgun/suppressions.rb +15 -8
  26. data/lib/mailgun/templates/templates.rb +187 -0
  27. data/lib/mailgun/version.rb +1 -1
  28. data/lib/mailgun/webhooks/webhooks.rb +2 -2
  29. data/lib/mailgun-ruby.rb +1 -1
  30. data/lib/mailgun.rb +4 -1
  31. data/lib/railgun/mailer.rb +83 -12
  32. data/lib/railgun/message.rb +2 -1
  33. data/lib/railgun/railtie.rb +3 -2
  34. data/mailgun.gemspec +15 -12
  35. data/spec/integration/bounces_spec.rb +3 -3
  36. data/spec/integration/campaign_spec.rb +0 -0
  37. data/spec/integration/complaints_spec.rb +0 -0
  38. data/spec/integration/domains_spec.rb +8 -0
  39. data/spec/integration/email_validation_spec.rb +10 -2
  40. data/spec/integration/events_spec.rb +1 -1
  41. data/spec/integration/list_members_spec.rb +0 -0
  42. data/spec/integration/list_spec.rb +0 -0
  43. data/spec/integration/mailer_spec.rb +67 -0
  44. data/spec/integration/mailgun_spec.rb +92 -1
  45. data/spec/integration/routes_spec.rb +0 -0
  46. data/spec/integration/stats_spec.rb +0 -0
  47. data/spec/integration/suppressions_spec.rb +18 -2
  48. data/spec/integration/templates_spec.rb +135 -0
  49. data/spec/integration/unsubscribes_spec.rb +0 -0
  50. data/spec/integration/webhook_spec.rb +0 -0
  51. data/spec/spec_helper.rb +3 -1
  52. data/spec/unit/connection/test_client.rb +18 -1
  53. data/spec/unit/events/events_spec.rb +19 -0
  54. data/spec/unit/mailgun_spec.rb +43 -2
  55. data/spec/unit/messages/batch_message_spec.rb +56 -40
  56. data/spec/unit/messages/message_builder_spec.rb +149 -16
  57. data/spec/unit/messages/sample_data/unknown.type +0 -0
  58. data/spec/unit/railgun/mailer_spec.rb +388 -0
  59. data/vcr_cassettes/bounces.yml +12 -12
  60. data/vcr_cassettes/complaints.yml +0 -0
  61. data/vcr_cassettes/domains.todo.yml +0 -0
  62. data/vcr_cassettes/domains.yml +51 -1
  63. data/vcr_cassettes/email_validation.yml +5 -5
  64. data/vcr_cassettes/events.yml +0 -0
  65. data/vcr_cassettes/exceptions-invalid-api-key.yml +52 -0
  66. data/vcr_cassettes/exceptions-invalid-data.yml +52 -0
  67. data/vcr_cassettes/exceptions-not-allowed.yml +54 -0
  68. data/vcr_cassettes/list_members.yml +0 -0
  69. data/vcr_cassettes/mailer_invalid_domain.yml +109 -0
  70. data/vcr_cassettes/mailing_list.todo.yml +0 -0
  71. data/vcr_cassettes/mailing_list.yml +0 -0
  72. data/vcr_cassettes/message_deliver.yml +149 -0
  73. data/vcr_cassettes/routes.yml +0 -0
  74. data/vcr_cassettes/send_message.yml +0 -0
  75. data/vcr_cassettes/stats.yml +0 -0
  76. data/vcr_cassettes/suppressions.yml +66 -15
  77. data/vcr_cassettes/templates.yml +1065 -0
  78. data/vcr_cassettes/unsubscribes.yml +0 -0
  79. data/vcr_cassettes/webhooks.yml +0 -0
  80. metadata +49 -29
  81. data/.ruby-version +0 -1
  82. /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
@@ -1,4 +1,4 @@
1
1
  # It's the version. Yeay!
2
2
  module Mailgun
3
- VERSION = '1.1.9'
3
+ VERSION = '1.2.12'
4
4
  end
@@ -58,7 +58,7 @@ module Mailgun
58
58
  #
59
59
  # Returns true or false
60
60
  def create_all(domain, url = '')
61
- %w(bounce click deliver drop open spam unsubscribe).each do |action|
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(bounce click deliver drop open spam unsubscribe).each do |action|
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 :api_key,
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
 
@@ -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,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(@domain, mg_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
- @mg_obj
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
- mail.mailgun_headers.try(:each) do |k, v|
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
- v.nil? or (v.respond_to?(:empty) and v.empty?)
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
@@ -11,7 +11,8 @@ module Mail
11
11
  attr_accessor :mailgun_variables,
12
12
  :mailgun_options,
13
13
  :mailgun_recipient_variables,
14
- :mailgun_headers
14
+ :mailgun_headers,
15
+ :mailgun_template_variables
15
16
 
16
17
  end
17
18
  end
@@ -2,8 +2,9 @@ require 'railgun/mailer'
2
2
 
3
3
  module Railgun
4
4
  class Railtie < ::Rails::Railtie
5
- config.before_configuration do
6
- ActionMailer::Base.add_delivery_method :mailgun, Railgun::Mailer
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 = 'http://www.mailgun.com'
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.0.0'
26
-
27
- spec.add_development_dependency 'bundler', '~> 1.5'
28
- spec.add_development_dependency 'rspec', '~> 3.0'
29
- spec.add_development_dependency 'rake', '~> 10.0'
30
- spec.add_development_dependency 'webmock', '~> 1.22'
31
- spec.add_development_dependency 'pry', '~> 0.9'
32
- spec.add_development_dependency 'vcr', '~> 3.0'
33
- spec.add_development_dependency 'simplecov', '~> 0.11'
34
- spec.add_development_dependency "rails"
35
- spec.add_dependency 'rest-client', '~> 2.0'
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-email@#{TESTDOMAIN}"
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(PUB_APIKEY)
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["unparseable"]).to eq(@invalid)
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,
@@ -22,7 +22,7 @@ describe 'For the Events endpoint', vcr: vcr_opts do
22
22
 
23
23
  it 'can iterate over all events with `each`' do
24
24
  @events.each do |e|
25
- expect(e.id).to eq("JAx9z641TuGGUyaJlD9sCQ")
25
+ expect(e["id"]).to eq("JAx9z641TuGGUyaJlD9sCQ")
26
26
  end
27
27
  end
28
28
  end
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