mailgun-ruby 1.1.2 → 1.1.6

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.
@@ -0,0 +1,158 @@
1
+ require 'action_mailer'
2
+ require 'mailgun'
3
+ require 'rails'
4
+ require 'railgun/errors'
5
+
6
+ module Railgun
7
+
8
+ # Railgun::Mailer is an ActionMailer provider for sending mail through
9
+ # Mailgun.
10
+ class Mailer
11
+
12
+ # [Hash] config ->
13
+ # Requires *at least* `api_key` and `domain` keys.
14
+ attr_accessor :config, :domain
15
+
16
+ # Initialize the Railgun mailer.
17
+ #
18
+ # @param [Hash] config Hash of config values, typically from `app_config.action_mailer.mailgun_config`
19
+ def initialize(config)
20
+ @config = config
21
+
22
+ [:api_key, :domain].each do |k|
23
+ raise Railgun::ConfigurationError.new("Config requires `#{k}` key", @config) unless @config.has_key?(k)
24
+ end
25
+
26
+ @mg_client = Mailgun::Client.new(config[:api_key])
27
+ @domain = @config[:domain]
28
+
29
+ if (@config[:fake_message_send] || false)
30
+ Rails.logger.info "NOTE: fake message sending has been enabled for mailgun-ruby!"
31
+ @mg_client.enable_test_mode!
32
+ end
33
+ end
34
+
35
+ def deliver!(mail)
36
+ mg_message = Railgun.transform_for_mailgun(mail)
37
+ response = @mg_client.send_message(@domain, mg_message)
38
+
39
+ if response.code == 200 then
40
+ mg_id = response.body['id']
41
+ mail.message_id = mg_id
42
+ end
43
+ response
44
+ end
45
+
46
+ def mailgun_client
47
+ @mg_obj
48
+ end
49
+
50
+ end
51
+
52
+ module_function
53
+
54
+ # Performs a series of transformations on the `mailgun*` attributes.
55
+ # After prefixing them with the proper option type, they are added to
56
+ # the message hash where they will then be sent to the API as JSON.
57
+ #
58
+ # @param [Mail::Message] mail message to transform
59
+ #
60
+ # @return [Hash] transformed message hash
61
+ def transform_for_mailgun(mail)
62
+ message = build_message_object(mail)
63
+
64
+ # v:* attributes (variables)
65
+ mail.mailgun_variables.try(:each) do |k, v|
66
+ message["v:#{k}"] = v
67
+ end
68
+
69
+ # o:* attributes (options)
70
+ mail.mailgun_options.try(:each) do |k, v|
71
+ message["o:#{k}"] = v
72
+ end
73
+
74
+ # h:* attributes (headers)
75
+ mail.mailgun_headers.try(:each) do |k, v|
76
+ message["h:#{k}"] = v
77
+ end
78
+
79
+ # recipient variables
80
+ message['recipient-variables'] = mail.mailgun_recipient_variables.to_json if mail.mailgun_recipient_variables
81
+
82
+ # reject blank values
83
+ message.delete_if do |k, v|
84
+ v.nil? or (v.respond_to?(:empty) and v.empty?)
85
+ end
86
+
87
+ return message
88
+ end
89
+
90
+ # Acts on a Rails/ActionMailer message object and uses Mailgun::MessageBuilder
91
+ # to construct a new message.
92
+ #
93
+ # @param [Mail::Message] mail message to transform
94
+ #
95
+ # @returns [Hash] Message hash from Mailgun::MessageBuilder
96
+ def build_message_object(mail)
97
+ mb = Mailgun::MessageBuilder.new
98
+
99
+ mb.from mail[:from]
100
+ mb.reply_to(mail[:reply_to].to_s) if mail[:reply_to].present?
101
+ mb.subject mail.subject
102
+ mb.body_html extract_body_html(mail)
103
+ mb.body_text extract_body_text(mail)
104
+
105
+ [:to, :cc, :bcc].each do |rcpt_type|
106
+ addrs = mail[rcpt_type] || nil
107
+ case addrs
108
+ when String
109
+ # Likely a single recipient
110
+ mb.add_recipient rcpt_type.to_s, addrs
111
+ when Array
112
+ addrs.each do |addr|
113
+ mb.add_recipient rcpt_type.to_s, addr
114
+ end
115
+ when Mail::Field
116
+ mb.add_recipient rcpt_type.to_s, addrs.to_s
117
+ end
118
+ end
119
+
120
+ return mb.message if mail.attachments.empty?
121
+
122
+ mail.attachments.each do |attach|
123
+ attach = Attachment.new(attach, encoding: 'ascii-8bit', inline: attach.inline?)
124
+ attach.attach_to_message! mb
125
+ end
126
+
127
+ return mb.message
128
+ end
129
+
130
+ # Returns the decoded HTML body from the Mail::Message object if available,
131
+ # otherwise nil.
132
+ #
133
+ # @param [Mail::Message] mail message to transform
134
+ #
135
+ # @return [String]
136
+ def extract_body_html(mail)
137
+ begin
138
+ (mail.html_part || mail).body.decoded || nil
139
+ rescue
140
+ nil
141
+ end
142
+ end
143
+
144
+ # Returns the decoded text body from the Mail::Message object if it is available,
145
+ # otherwise nil.
146
+ #
147
+ # @param [Mail::Message] mail message to transform
148
+ #
149
+ # @return [String]
150
+ def extract_body_text(mail)
151
+ begin
152
+ (mail.text_part || mail).body.decoded || nil
153
+ rescue
154
+ nil
155
+ end
156
+ end
157
+
158
+ end
@@ -0,0 +1,17 @@
1
+ require 'mail'
2
+ require 'mailgun/messages/message_builder'
3
+ require 'railgun/attachment'
4
+ require 'railgun/errors'
5
+
6
+ module Mail
7
+
8
+ class Message
9
+
10
+ # Attributes to hold Mailgun-specific information
11
+ attr_accessor :mailgun_variables,
12
+ :mailgun_options,
13
+ :mailgun_recipient_variables,
14
+ :mailgun_headers
15
+
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ require 'railgun/mailer'
2
+
3
+ module Railgun
4
+ class Railtie < ::Rails::Railtie
5
+ config.before_configuration do
6
+ ActionMailer::Base.add_delivery_method :mailgun, Railgun::Mailer
7
+ end
8
+ end
9
+ end
data/lib/railgun.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'railgun/railtie'
2
+ require 'railgun/attachment'
3
+ require 'railgun/errors'
4
+ require 'railgun/mailer'
5
+ require 'railgun/message'
6
+
7
+ module Railgun
8
+ end
data/mailgun.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.version = Mailgun::VERSION
10
10
  spec.homepage = 'http://www.mailgun.com'
11
11
  spec.platform = Gem::Platform::RUBY
12
- spec.license = 'Apache'
12
+ spec.license = 'Apache-2.0'
13
13
 
14
14
  spec.summary = "Mailgun's Official Ruby SDK"
15
15
  spec.description = "Mailgun's Official Ruby SDK for interacting with the Mailgun API."
@@ -1,5 +1,6 @@
1
1
  require 'spec_helper'
2
2
  require 'mailgun'
3
+ require 'mailgun/events/events'
3
4
 
4
5
  vcr_opts = { :cassette_name => "events" }
5
6
 
@@ -7,9 +8,10 @@ describe 'For the Events endpoint', vcr: vcr_opts do
7
8
  before(:all) do
8
9
  @mg_obj = Mailgun::Client.new(APIKEY, APIHOST, APIVERSION, SSL)
9
10
  @domain = TESTDOMAIN
11
+ @events = Mailgun::Events.new(@mg_obj, @domain)
10
12
  end
11
13
 
12
- it 'get an event.' do
14
+ it 'can get an event.' do
13
15
  result = @mg_obj.get("#{@domain}/events", {:limit => 1})
14
16
 
15
17
  result.to_h!
@@ -17,4 +19,10 @@ describe 'For the Events endpoint', vcr: vcr_opts do
17
19
  expect(result.body["paging"]).to include("next")
18
20
  expect(result.body["paging"]).to include("previous")
19
21
  end
22
+
23
+ it 'can iterate over all events with `each`' do
24
+ @events.each do |e|
25
+ expect(e.id).to eq("JAx9z641TuGGUyaJlD9sCQ")
26
+ end
27
+ end
20
28
  end
File without changes
@@ -0,0 +1,126 @@
1
+ require 'spec_helper'
2
+
3
+ require 'mailgun'
4
+ require 'mailgun/suppressions'
5
+
6
+ vcr_opts = { :cassette_name => 'suppressions' }
7
+
8
+ describe 'For the suppressions handling class', order: :defined, vcr: vcr_opts do
9
+
10
+ before(:all) do
11
+ @mg_obj = Mailgun::Client.new(APIKEY)
12
+ @suppress = Mailgun::Suppressions.new(@mg_obj, TESTDOMAIN)
13
+
14
+ @addresses = ['test1@example.com', 'test2@example.org', 'test3@example.net', 'test4@example.info']
15
+ end
16
+
17
+ it 'can batch-add bounces' do
18
+ bounces = []
19
+ @addresses.each do |addr|
20
+ bounces.push({
21
+ :address => addr,
22
+ :code => 500,
23
+ :error => 'integration testing',
24
+ })
25
+ end
26
+
27
+ response, nested = @suppress.create_bounces bounces
28
+ response.to_h!
29
+
30
+ expect(response.code).to eq(200)
31
+ expect(response.body['message']).to eq('4 addresses have been added to the bounces table')
32
+ expect(nested.length).to eq(0)
33
+ end
34
+
35
+ it 'raises ParameterError if no bounce[:address] is present' do
36
+ bounces = []
37
+ bounces.push({
38
+ :code => 500,
39
+ :error => 'integration testing',
40
+ })
41
+
42
+ expect { @suppress.create_bounces bounces }.to raise_error(Mailgun::ParameterError)
43
+ end
44
+
45
+ it 'removes a single bounce address' do
46
+ @addresses.each do |addr|
47
+ response = @suppress.delete_bounce addr
48
+ response.to_h!
49
+
50
+ expect(response.code).to eq(200)
51
+ expect(response.body['message']).to eq('Bounced address has been removed')
52
+ end
53
+ end
54
+
55
+ it 'can batch-add unsubscribes' do
56
+ unsubscribes = []
57
+ @addresses.each do |addr|
58
+ unsubscribes.push({
59
+ :address => addr,
60
+ :tag => 'integration',
61
+ })
62
+ end
63
+
64
+ response, nested = @suppress.create_unsubscribes unsubscribes
65
+ response.to_h!
66
+
67
+ expect(response.code).to eq(200)
68
+ expect(response.body['message']).to eq('4 addresses have been added to the unsubscribes table')
69
+ expect(nested.length).to eq(0)
70
+ end
71
+
72
+ it 'raises ParameterError if no unsubscribe[:address] is present' do
73
+ unsubscribes = []
74
+ unsubscribes.push({
75
+ :tag => 'integration',
76
+ })
77
+
78
+ expect { @suppress.create_unsubscribes unsubscribes }.to raise_error(Mailgun::ParameterError)
79
+ end
80
+
81
+ it 'removes a single unsubscribe address' do
82
+ @addresses.each do |addr|
83
+ response = @suppress.delete_unsubscribe addr
84
+ response.to_h!
85
+
86
+ expect(response.code).to eq(200)
87
+ expect(response.body['message']).to eq('Unsubscribe event has been removed')
88
+ end
89
+ end
90
+
91
+ it 'can batch-add complaints' do
92
+ complaints = []
93
+ @addresses.each do |addr|
94
+ complaints.push :address => addr
95
+ end
96
+
97
+ response, nested = @suppress.create_complaints complaints
98
+ response.to_h!
99
+
100
+ expect(response.code).to eq(200)
101
+ expect(response.body['message']).to eq('4 complaint addresses have been added to the complaints table')
102
+ expect(nested.length).to eq(0)
103
+ end
104
+
105
+ it 'raises ParameterError if no complaint[:address] is present' do
106
+ complaints = []
107
+ complaints.push({
108
+ :tag => 'integration',
109
+ })
110
+
111
+ expect { @suppress.create_complaints complaints }.to raise_error(Mailgun::ParameterError)
112
+ end
113
+
114
+ it 'removes a single complaint address' do
115
+ @addresses.each do |addr|
116
+ response = @suppress.delete_complaint addr
117
+ response.to_h!
118
+
119
+ expect(response.code).to eq(200)
120
+ expect(response.body['message']).to eq('Spam complaint has been removed')
121
+ end
122
+ end
123
+
124
+ # TODO: Add tests for pagination support.
125
+ end
126
+
@@ -5,7 +5,7 @@ describe 'The method get' do
5
5
  @mg_obj = Mailgun::UnitClient.new('events')
6
6
  events = Mailgun::Events.new(@mg_obj, "samples.mailgun.org")
7
7
  result = events.get()
8
-
8
+
9
9
  expect(result.body).to include("items")
10
10
  expect(result.body).to include("paging")
11
11
  end
@@ -17,7 +17,7 @@ describe 'The method next' do
17
17
  @mg_obj = Mailgun::UnitClient.new('events')
18
18
  events = Mailgun::Events.new(@mg_obj, "samples.mailgun.org")
19
19
  result = events.next()
20
-
20
+
21
21
  expect(result.body).to include("items")
22
22
  expect(result.body).to include("paging")
23
23
  end
@@ -33,3 +33,18 @@ describe 'The method previous' do
33
33
  expect(result.body).to include("paging")
34
34
  end
35
35
  end
36
+
37
+ describe 'The method each' do
38
+ it 'should iterate over all event items.' do
39
+ @mg_obj = Mailgun::UnitClient.new('events')
40
+ events = Mailgun::Events.new(@mg_obj, "samples.mailgun.org")
41
+ # Events from the UnitClient are actually empty.
42
+ count = 0
43
+ events.each do |e|
44
+ count = count + 1
45
+ end
46
+
47
+ # Better than nothing..
48
+ expect(count).to eq(0)
49
+ end
50
+ end
@@ -70,7 +70,7 @@ describe 'The method add_recipient' do
70
70
  recipient_type = 'h:reply-to'
71
71
  @mb_obj.add_recipient(recipient_type, @address, @variables)
72
72
 
73
- expect(@mb_obj.message[recipient_type][0]).to eq("'#{@variables['first']} #{@variables['last']}' <#{@address}>")
73
+ expect(@mb_obj.message[recipient_type]).to eq("'#{@variables['first']} #{@variables['last']}' <#{@address}>")
74
74
  @mb_obj.counters[:recipients].each_value{|value| expect(value).to eq(0)}
75
75
  end
76
76
 
@@ -249,6 +249,31 @@ describe 'The method add_inline_image' do
249
249
  end
250
250
  end
251
251
 
252
+ describe 'The method list_unsubscribe' do
253
+ before(:each) do
254
+ @mb_obj = Mailgun::MessageBuilder.new
255
+ end
256
+
257
+ it 'sets the message list_unsubscribe to blank if called and no parameters are provided' do
258
+ @mb_obj.list_unsubscribe
259
+ expect(@mb_obj.message['h:List-Unsubscribe']).to eq('')
260
+ end
261
+
262
+ it 'sets the message list_unsubscribe if called with one list_unsubscribe parameter' do
263
+ unsubscribe_to = 'http://example.com/stop-hassle'
264
+ @mb_obj.list_unsubscribe(unsubscribe_to)
265
+ expect(@mb_obj.message['h:List-Unsubscribe']).to eq("<#{unsubscribe_to}>")
266
+ end
267
+
268
+ it 'sets the message list_unsubscribe if called with many list_unsubscribe parameters' do
269
+ unsubscribe_to = %w(http://example.com/stop-hassle mailto:stop-hassle@example.com)
270
+ @mb_obj.list_unsubscribe(*unsubscribe_to)
271
+ expect(@mb_obj.message['h:List-Unsubscribe']).to eq(
272
+ unsubscribe_to.map { |var| "<#{var}>" }.join(',')
273
+ )
274
+ end
275
+ end
276
+
252
277
  describe 'The method set_test_mode' do
253
278
  it 'warns of set_test_mode deprecation' do
254
279
  @mb_obj = Mailgun::MessageBuilder.new
@@ -485,19 +510,31 @@ describe 'The method header' do
485
510
  it 'accepts valid JSON and appends as data to the message.' do
486
511
  @mb_obj.header('my-data', '{"key":"value"}')
487
512
 
488
- expect(@mb_obj.message["v:my-data"][0]).to be_kind_of(String)
489
- expect(@mb_obj.message["v:my-data"][0].to_s).to eq('{"key"=>"value"}')
513
+ expect(@mb_obj.message["h:my-data"]).to be_kind_of(String)
514
+ expect(@mb_obj.message["h:my-data"].to_s).to eq('{"key":"value"}')
515
+ end
516
+ end
517
+
518
+ describe 'The method variable' do
519
+ before(:each) do
520
+ @mb_obj = Mailgun::MessageBuilder.new
521
+ end
522
+ it 'accepts valid JSON and stores it as message[param].' do
523
+ @mb_obj.variable('my-data', '{"key":"value"}')
524
+
525
+ expect(@mb_obj.message["v:my-data"]).to be_kind_of(String)
526
+ expect(@mb_obj.message["v:my-data"].to_s).to eq('{"key":"value"}')
490
527
  end
491
528
  it 'accepts a hash and appends as data to the message.' do
492
529
  data = {'key' => 'value'}
493
- @mb_obj.header('my-data', data)
530
+ @mb_obj.variable('my-data', data)
494
531
 
495
- expect(@mb_obj.message["v:my-data"][0]).to be_kind_of(String)
496
- expect(@mb_obj.message["v:my-data"][0].to_s).to eq('{"key"=>"value"}')
532
+ expect(@mb_obj.message["v:my-data"]).to be_kind_of(String)
533
+ expect(@mb_obj.message["v:my-data"].to_s).to eq('{"key":"value"}')
497
534
  end
498
535
  it 'throws an exception on broken JSON.' do
499
536
  data = 'This is some crappy JSON.'
500
- expect {@mb_obj.header('my-data', data)}.to raise_error(Mailgun::ParameterError)
537
+ expect {@mb_obj.variable('my-data', data)}.to raise_error(Mailgun::ParameterError)
501
538
  end
502
539
  end
503
540
 
@@ -58,4 +58,51 @@ http_interactions:
58
58
  \ }\n}"
59
59
  http_version:
60
60
  recorded_at: Thu, 07 Jan 2016 22:08:06 GMT
61
- recorded_with: VCR 3.0.1
61
+ - request:
62
+ method: get
63
+ uri: https://api:<APIKEY>@api.mailgun.net/v3/DOMAIN.TEST/events
64
+ body:
65
+ encoding: US-ASCII
66
+ string: ''
67
+ headers:
68
+ Accept:
69
+ - "*/*"
70
+ Accept-Encoding:
71
+ - gzip, deflate
72
+ User-Agent:
73
+ - rest-client/2.0.1 (linux-gnu x86_64) ruby/2.3.3p222
74
+ Host:
75
+ - api.mailgun.net
76
+ response:
77
+ status:
78
+ code: 200
79
+ message: OK
80
+ headers:
81
+ Access-Control-Allow-Headers:
82
+ - Content-Type, x-requested-with
83
+ Access-Control-Allow-Methods:
84
+ - GET, POST, PUT, DELETE, OPTIONS
85
+ Access-Control-Allow-Origin:
86
+ - "*"
87
+ Access-Control-Max-Age:
88
+ - '600'
89
+ Content-Type:
90
+ - application/json
91
+ Date:
92
+ - Wed, 10 May 2017 20:06:54 GMT
93
+ Server:
94
+ - nginx
95
+ Content-Length:
96
+ - '2060'
97
+ Connection:
98
+ - keep-alive
99
+ body:
100
+ encoding: UTF-8
101
+ string: "{\n \"items\": [], \n \"paging\": {\n \"next\": \"https://api.mailgun.net/v3/DOMAIN.TEST/events/W3siYiI6ICIyMDE3LTA1LTEwVDIwOjA2OjU0LjU3NiswMDowMCIsICJlIjogIjIwMTctMDUtMDhUMjA6MDY6NTQuNTc3KzAwOjAwIn0sIHsiYiI6ICIyMDE3LTA1LTEwVDIwOjA2OjU0LjU3NiswMDowMCIsICJlIjogIjIwMTctMDUtMDhUMjA6MDY6NTQuNTc3KzAwOjAwIn0sIFsiZiJdLCBudWxsLCBbWyJhY2NvdW50LmlkIiwgIjU4MDUyMTg2NzhmYTE2MTNjNzkwYjUwZiJdLCBbImRvbWFpbi5uYW1lIiwgInNhbmRib3gyOTcwMTUyYWYzZDM0NTU5YmZjN2U3MTcwM2E2Y2YyNC5tYWlsZ3VuLm9yZyJdXSwgMTAwLCBudWxsXQ==\",
102
+ \n \"last\": \"https://api.mailgun.net/v3/DOMAIN.TEST/events/W3siYiI6ICIyMDE3LTA1LTEwVDIwOjA2OjU0LjU3NiswMDowMCIsICJlIjogIjIwMTctMDUtMDhUMjA6MDY6NTQuNTc3KzAwOjAwIn0sIHsiYiI6ICIyMDE3LTA1LTA4VDIwOjA2OjU0LjU3NyswMDowMCIsICJlIjogIjIwMTctMDUtMTBUMjA6MDY6NTQuNTc2KzAwOjAwIn0sIFsicCIsICJmIl0sIG51bGwsIFtbImFjY291bnQuaWQiLCAiNTgwNTIxODY3OGZhMTYxM2M3OTBiNTBmIl0sIFsiZG9tYWluLm5hbWUiLCAic2FuZGJveDI5NzAxNTJhZjNkMzQ1NTliZmM3ZTcxNzAzYTZjZjI0Lm1haWxndW4ub3JnIl1dLCAxMDAsIG51bGxd\",
103
+ \n \"first\": \"https://api.mailgun.net/v3/DOMAIN.TEST/events/W3siYiI6ICIyMDE3LTA1LTEwVDIwOjA2OjU0LjU3NiswMDowMCIsICJlIjogIjIwMTctMDUtMDhUMjA6MDY6NTQuNTc3KzAwOjAwIn0sIHsiYiI6ICIyMDE3LTA1LTEwVDIwOjA2OjU0LjU3NiswMDowMCIsICJlIjogIjIwMTctMDUtMDhUMjA6MDY6NTQuNTc3KzAwOjAwIn0sIFsiZiJdLCBudWxsLCBbWyJhY2NvdW50LmlkIiwgIjU4MDUyMTg2NzhmYTE2MTNjNzkwYjUwZiJdLCBbImRvbWFpbi5uYW1lIiwgInNhbmRib3gyOTcwMTUyYWYzZDM0NTU5YmZjN2U3MTcwM2E2Y2YyNC5tYWlsZ3VuLm9yZyJdXSwgMTAwLCBudWxsXQ==\",
104
+ \n \"previous\": \"https://api.mailgun.net/v3/DOMAIN.TEST/events/W3siYiI6ICIyMDE3LTA1LTEwVDIwOjA2OjU0LjU3NiswMDowMCIsICJlIjogIjIwMTctMDUtMDhUMjA6MDY6NTQuNTc3KzAwOjAwIn0sIHsiYiI6ICIyMDE3LTA1LTEwVDIwOjA2OjU0LjU3NiswMDowMCIsICJlIjogIjIwMTctMDUtMTBUMjA6MDY6NTQuNTc3KzAwOjAwIn0sIFsicCIsICJmIl0sIG51bGwsIFtbImFjY291bnQuaWQiLCAiNTgwNTIxODY3OGZhMTYxM2M3OTBiNTBmIl0sIFsiZG9tYWluLm5hbWUiLCAic2FuZGJveDI5NzAxNTJhZjNkMzQ1NTliZmM3ZTcxNzAzYTZjZjI0Lm1haWxndW4ub3JnIl1dLCAxMDAsIG51bGxd\"\n
105
+ \ }\n}"
106
+ http_version:
107
+ recorded_at: Wed, 10 May 2017 20:06:54 GMT
108
+ recorded_with: VCR 3.0.3