mailgun-ruby 1.1.8 → 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 +5 -5
- data/.travis.yml +6 -5
- data/Gemfile +1 -1
- data/README.md +24 -2
- data/docs/Domains.md +0 -0
- data/docs/Webhooks.md +1 -1
- data/docs/railgun/Overview.md +11 -0
- data/docs/railgun/Parameters.md +83 -0
- data/lib/mailgun/client.rb +27 -6
- data/lib/mailgun/events/events.rb +1 -1
- data/lib/mailgun/exceptions/exceptions.rb +2 -0
- data/lib/mailgun/messages/batch_message.rb +1 -0
- data/lib/mailgun/messages/message_builder.rb +61 -6
- data/lib/mailgun/suppressions.rb +4 -1
- data/lib/mailgun/version.rb +1 -1
- data/lib/mailgun/webhooks/webhooks.rb +1 -1
- data/lib/railgun/mailer.rb +105 -13
- data/lib/railgun/message.rb +2 -1
- data/lib/railgun/railtie.rb +3 -2
- data/mailgun.gemspec +11 -11
- data/spec/integration/email_validation_spec.rb +8 -0
- data/spec/integration/events_spec.rb +1 -1
- data/spec/integration/mailer_spec.rb +67 -0
- data/spec/integration/mailgun_spec.rb +4 -1
- data/spec/integration/suppressions_spec.rb +18 -2
- data/spec/spec_helper.rb +3 -1
- data/spec/unit/connection/test_client.rb +16 -0
- data/spec/unit/events/events_spec.rb +19 -0
- data/spec/unit/mailgun_spec.rb +24 -2
- data/spec/unit/messages/batch_message_spec.rb +56 -40
- data/spec/unit/messages/message_builder_spec.rb +165 -17
- data/spec/unit/messages/sample_data/unknown.type +0 -0
- data/spec/unit/railgun/content_type_spec.rb +71 -0
- data/spec/unit/railgun/mailer_spec.rb +388 -0
- data/vcr_cassettes/mailer_invalid_domain.yml +109 -0
- data/vcr_cassettes/message_deliver.yml +149 -0
- data/vcr_cassettes/suppressions.yml +66 -15
- metadata +50 -26
- data/.ruby-version +0 -1
data/mailgun.gemspec
CHANGED
@@ -22,16 +22,16 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
23
23
|
spec.require_paths = ["lib"]
|
24
24
|
|
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
|
-
|
35
|
-
spec.add_dependency 'rest-client', '
|
25
|
+
spec.required_ruby_version = '>= 2.2.2'
|
26
|
+
|
27
|
+
spec.add_development_dependency 'bundler', '>= 1.16.2'
|
28
|
+
spec.add_development_dependency 'rspec', '~> 3.8.0'
|
29
|
+
spec.add_development_dependency 'rake', '~> 12.3.2'
|
30
|
+
spec.add_development_dependency 'webmock', '~> 3.4.2'
|
31
|
+
spec.add_development_dependency 'pry', '~> 0.11.3'
|
32
|
+
spec.add_development_dependency 'vcr', '~> 3.0.3'
|
33
|
+
spec.add_development_dependency 'simplecov', '~> 0.16.1'
|
34
|
+
spec.add_development_dependency 'rails'
|
35
|
+
spec.add_dependency 'rest-client', '>= 2.0.2'
|
36
36
|
|
37
37
|
end
|
@@ -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,
|
@@ -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
|
@@ -63,6 +63,9 @@ describe 'The method send_message()', vcr: vcr_opts do
|
|
63
63
|
:to => "bob@#{@domain}",
|
64
64
|
:subject => "Test",
|
65
65
|
:text => "Test Data" }
|
66
|
+
uuid = 'uuid'
|
67
|
+
|
68
|
+
allow(SecureRandom).to receive(:uuid).and_return(uuid)
|
66
69
|
|
67
70
|
result = @mg_obj.send_message(@domain, data)
|
68
71
|
|
@@ -72,7 +75,7 @@ describe 'The method send_message()', vcr: vcr_opts do
|
|
72
75
|
expect(result.body).to include("id")
|
73
76
|
|
74
77
|
expect(result.code).to eq(200)
|
75
|
-
expect(result.body['id']).to eq("test-mode-mail@localhost")
|
78
|
+
expect(result.body['id']).to eq("test-mode-mail-#{uuid}@localhost")
|
76
79
|
expect(result.body['message']).to eq("Queued. Thank you.")
|
77
80
|
end
|
78
81
|
|
@@ -52,7 +52,7 @@ describe 'For the suppressions handling class', order: :defined, vcr: vcr_opts d
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
-
it 'can batch-add unsubscribes' do
|
55
|
+
it 'can batch-add unsubscribes with tags as string' do
|
56
56
|
unsubscribes = []
|
57
57
|
@addresses.each do |addr|
|
58
58
|
unsubscribes.push({
|
@@ -69,6 +69,23 @@ describe 'For the suppressions handling class', order: :defined, vcr: vcr_opts d
|
|
69
69
|
expect(nested.length).to eq(0)
|
70
70
|
end
|
71
71
|
|
72
|
+
it 'can batch-add unsubscribes with tags as array' do
|
73
|
+
unsubscribes = []
|
74
|
+
@addresses.each do |addr|
|
75
|
+
unsubscribes.push({
|
76
|
+
:address => addr,
|
77
|
+
:tags => ['integration'],
|
78
|
+
})
|
79
|
+
end
|
80
|
+
|
81
|
+
response, nested = @suppress.create_unsubscribes unsubscribes
|
82
|
+
response.to_h!
|
83
|
+
|
84
|
+
expect(response.code).to eq(200)
|
85
|
+
expect(response.body['message']).to eq('4 addresses have been added to the unsubscribes table')
|
86
|
+
expect(nested.length).to eq(0)
|
87
|
+
end
|
88
|
+
|
72
89
|
it 'raises ParameterError if no unsubscribe[:address] is present' do
|
73
90
|
unsubscribes = []
|
74
91
|
unsubscribes.push({
|
@@ -123,4 +140,3 @@ describe 'For the suppressions handling class', order: :defined, vcr: vcr_opts d
|
|
123
140
|
|
124
141
|
# TODO: Add tests for pagination support.
|
125
142
|
end
|
126
|
-
|
data/spec/spec_helper.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'rubygems'
|
2
|
+
require 'base64'
|
2
3
|
require 'bundler'
|
3
4
|
require 'bundler/setup'
|
4
5
|
Bundler.setup(:development)
|
@@ -37,9 +38,10 @@ TESTDOMAIN = envs['MAILGUN_TESTDOMAIN']
|
|
37
38
|
VCR.configure do |c|
|
38
39
|
c.cassette_library_dir = 'vcr_cassettes'
|
39
40
|
c.hook_into :webmock
|
40
|
-
c.configure_rspec_metadata!
|
41
41
|
c.default_cassette_options = { record: :new_episodes }
|
42
42
|
c.filter_sensitive_data('<APIKEY>') { APIKEY }
|
43
43
|
c.filter_sensitive_data('DOMAIN.TEST') { TESTDOMAIN }
|
44
44
|
c.filter_sensitive_data('<PUBKEY>') { PUB_APIKEY }
|
45
|
+
|
46
|
+
c.configure_rspec_metadata!
|
45
47
|
end
|
@@ -23,6 +23,7 @@ module Mailgun
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def send_message(working_domain, data)
|
26
|
+
perform_data_validation(working_domain, data)
|
26
27
|
case data
|
27
28
|
when Hash
|
28
29
|
if data.has_key?(:message)
|
@@ -75,6 +76,21 @@ module Mailgun
|
|
75
76
|
|
76
77
|
private
|
77
78
|
|
79
|
+
def perform_data_validation(working_domain, data)
|
80
|
+
fail ParameterError.new('Missing working domain', working_domain) unless working_domain
|
81
|
+
return true unless data.is_a?(Hash) && data.present?
|
82
|
+
message = data.respond_to?(:message) ? data.message : data
|
83
|
+
|
84
|
+
fail ParameterError.new(
|
85
|
+
'Missing `to` recipient, message should containg at least 1 recipient',
|
86
|
+
working_domain
|
87
|
+
) if message.fetch('to', []).empty? && message.fetch(:to, []).empty?
|
88
|
+
fail ParameterError.new(
|
89
|
+
'Missing a `from` sender, message should containg at least 1 `from` sender',
|
90
|
+
working_domain
|
91
|
+
) if message.fetch('from', []).empty? && message.fetch(:from, []).empty?
|
92
|
+
end
|
93
|
+
|
78
94
|
def response_generator(resource_endpoint)
|
79
95
|
if resource_endpoint == "messages"
|
80
96
|
t = Time.now
|
@@ -11,6 +11,25 @@ describe 'The method get' do
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
+
describe 'Pagination' do
|
15
|
+
it 'should return a proper hash of log data.' do
|
16
|
+
@mg_obj = Mailgun::UnitClient.new('events')
|
17
|
+
events = Mailgun::Events.new(@mg_obj, "samples.mailgun.org")
|
18
|
+
result = events.get()
|
19
|
+
|
20
|
+
json = JSON.parse(result.body)
|
21
|
+
expect(json).to include("paging")
|
22
|
+
expect(json["paging"]).to include("next")
|
23
|
+
expect(json["paging"]).to include{"previous"}
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should calculate proper next-page url' do
|
27
|
+
events = Mailgun::Events.new(@mg_obj, "samples.mailgun.org")
|
28
|
+
output = events.send(:extract_endpoint_from, '/v3/samples.mailgun.org/events/W3siYiI6ICIyMDE3LTA1LTEwVDIwOjA2OjU0LjU3NiswMDowMCIsICJlIjogIjIwMTctMDUtMDhUMjA6MDY6NTQuNTc3KzAwOjAwIn0sIHsiYiI6ICIyMDE3LTA1LTEwVDIwOjA2OjU0LjU3NiswMDowMCIsICJlIjogIjIwMTctMDUtMDhUMjA6MDY6NTQuNTc3KzAwOjAwIn0sIFsiZiJdLCBudWxsLCBbWyJhY2NvdW50LmlkIiwgIjU4MDUyMTg2NzhmYTE2MTNjNzkwYjUwZiJdLCBbImRvbWFpbi5uYW1lIiwgInNhbmRib3gyOTcwMTUyYWYzZDM0NTU5YmZjN2U3MTcwM2E2Y2YyNC5tYWlsZ3VuLm9yZyJdXSwgMTAwLCBudWxsXQ==')
|
29
|
+
|
30
|
+
expect(output).to eq 'W3siYiI6ICIyMDE3LTA1LTEwVDIwOjA2OjU0LjU3NiswMDowMCIsICJlIjogIjIwMTctMDUtMDhUMjA6MDY6NTQuNTc3KzAwOjAwIn0sIHsiYiI6ICIyMDE3LTA1LTEwVDIwOjA2OjU0LjU3NiswMDowMCIsICJlIjogIjIwMTctMDUtMDhUMjA6MDY6NTQuNTc3KzAwOjAwIn0sIFsiZiJdLCBudWxsLCBbWyJhY2NvdW50LmlkIiwgIjU4MDUyMTg2NzhmYTE2MTNjNzkwYjUwZiJdLCBbImRvbWFpbi5uYW1lIiwgInNhbmRib3gyOTcwMTUyYWYzZDM0NTU5YmZjN2U3MTcwM2E2Y2YyNC5tYWlsZ3VuLm9yZyJdXSwgMTAwLCBudWxsXQ=='
|
31
|
+
end
|
32
|
+
end
|
14
33
|
|
15
34
|
describe 'The method next' do
|
16
35
|
it 'should return the next series of data.' do
|
data/spec/unit/mailgun_spec.rb
CHANGED
@@ -37,8 +37,11 @@ describe 'The method send_message()' do
|
|
37
37
|
end
|
38
38
|
|
39
39
|
it 'opens the message MIME and sends the MIME message.' do
|
40
|
-
data = {
|
41
|
-
|
40
|
+
data = {
|
41
|
+
'to' => 'joe@test.com',
|
42
|
+
'message' => 'Sample Data/mime.txt',
|
43
|
+
'from' => 'joe@test.com'
|
44
|
+
}
|
42
45
|
result = @mg_obj.send_message("testdomain.com", data)
|
43
46
|
|
44
47
|
result.to_h!
|
@@ -46,6 +49,25 @@ describe 'The method send_message()' do
|
|
46
49
|
expect(result.body).to include("message")
|
47
50
|
expect(result.body).to include("id")
|
48
51
|
end
|
52
|
+
|
53
|
+
context 'when domain is missing' do
|
54
|
+
it 'shows failure message' do
|
55
|
+
expect(@mg_obj).to receive(:fail)
|
56
|
+
@mg_obj.send_message(nil, {})
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'when to is missing' do
|
61
|
+
it 'shows failure message' do
|
62
|
+
data = {
|
63
|
+
'to' => '',
|
64
|
+
'message' => 'Sample Data/mime.txt',
|
65
|
+
'from' => 'joe@test.com'
|
66
|
+
}
|
67
|
+
expect(@mg_obj).to receive(:fail)
|
68
|
+
@mg_obj.send_message("testdomain.com", data)
|
69
|
+
end
|
70
|
+
end
|
49
71
|
end
|
50
72
|
|
51
73
|
describe 'The method post()' do
|
@@ -72,60 +72,76 @@ describe 'The method add_recipient' do
|
|
72
72
|
@address_3 = 'sam@example.com'
|
73
73
|
@variables_3 = {'first' => 'Sam', 'last' => 'Doe', 'tracking' => 'GHI123'}
|
74
74
|
end
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
1000.times do
|
79
|
-
@mb_obj.add_recipient(recipient_type, @address_1, @variables_1)
|
75
|
+
context 'when from is present' do
|
76
|
+
before(:each) do
|
77
|
+
@mb_obj.from('example@email.com')
|
80
78
|
end
|
81
79
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
end
|
80
|
+
it 'adds 1,000 recipients to the message body and validates counter is incremented then reset' do
|
81
|
+
recipient_type = :to
|
82
|
+
1000.times do
|
83
|
+
@mb_obj.add_recipient(recipient_type, @address_1, @variables_1)
|
84
|
+
end
|
88
85
|
|
89
|
-
|
90
|
-
|
91
|
-
1000.times do
|
86
|
+
expect(@mb_obj.counters[:recipients][recipient_type]).to eq(1000)
|
87
|
+
|
92
88
|
@mb_obj.add_recipient(recipient_type, @address_1, @variables_1)
|
89
|
+
|
90
|
+
expect(@mb_obj.counters[:recipients][recipient_type]).to eq(1)
|
93
91
|
end
|
94
92
|
|
95
|
-
|
96
|
-
|
93
|
+
it 'adds recipients to the message, calls finalize, and cleans up' do
|
94
|
+
recipient_type = :to
|
95
|
+
1000.times do
|
96
|
+
@mb_obj.add_recipient(recipient_type, @address_1, @variables_1)
|
97
|
+
end
|
97
98
|
|
98
|
-
|
99
|
-
|
100
|
-
expect(@mb_obj.counters[:recipients][recipient_type]).to eq(0)
|
101
|
-
end
|
99
|
+
expect(@mb_obj.counters[:recipients][recipient_type]).to eq(1000)
|
100
|
+
@mb_obj.finalize
|
102
101
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
@mb_obj.
|
102
|
+
expect(@mb_obj.recipient_variables).to eq({})
|
103
|
+
expect(@mb_obj.message['recipient-variables'].length).to eq(0)
|
104
|
+
expect(@mb_obj.message[:to].length).to eq(0)
|
105
|
+
expect(@mb_obj.counters[:recipients][recipient_type]).to eq(0)
|
107
106
|
end
|
108
|
-
@mb_obj.finalize
|
109
107
|
|
110
|
-
|
111
|
-
|
108
|
+
it 'adds 5,005 recipients to the message body and validates we receive message_ids back' do
|
109
|
+
recipient_type = :to
|
110
|
+
5005.times do
|
111
|
+
@mb_obj.add_recipient(recipient_type, @address_1, @variables_1)
|
112
|
+
end
|
113
|
+
@mb_obj.finalize
|
112
114
|
|
113
|
-
|
114
|
-
|
115
|
-
@mb_obj.add_recipient(recipient_type, @address_1, @variables_1)
|
115
|
+
expect(@mb_obj.message_ids.length).to eq(6)
|
116
|
+
end
|
116
117
|
|
117
|
-
|
118
|
+
it 'sets recipient-variables, for batch expansion' do
|
119
|
+
recipient_type = :to
|
120
|
+
@mb_obj.add_recipient(recipient_type, @address_1, @variables_1)
|
121
|
+
|
122
|
+
expect(@mb_obj.recipient_variables[@address_1]).to eq(@variables_1)
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'sets multiple recipient-variables, for batch expansion' do
|
126
|
+
recipient_type = :to
|
127
|
+
@mb_obj.add_recipient(recipient_type, @address_1, @variables_1)
|
128
|
+
@mb_obj.add_recipient(recipient_type, @address_2, @variables_2)
|
129
|
+
@mb_obj.add_recipient(recipient_type, @address_3, @variables_3)
|
130
|
+
|
131
|
+
expect(@mb_obj.recipient_variables[@address_1]).to eq(@variables_1)
|
132
|
+
expect(@mb_obj.recipient_variables[@address_2]).to eq(@variables_2)
|
133
|
+
expect(@mb_obj.recipient_variables[@address_3]).to eq(@variables_3)
|
134
|
+
end
|
118
135
|
end
|
119
136
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
expect(@mb_obj.recipient_variables[@address_3]).to eq(@variables_3)
|
137
|
+
context 'when from is empty' do
|
138
|
+
it 'shows error message' do
|
139
|
+
recipient_type = :to
|
140
|
+
@mb_obj.add_recipient(recipient_type, @address_1, @variables_1)
|
141
|
+
@mb_obj.add_recipient(recipient_type, @address_2, @variables_2)
|
142
|
+
expect(@mb_client).to receive(:fail)
|
143
|
+
@mb_obj.finalize
|
144
|
+
end
|
129
145
|
end
|
130
146
|
|
131
147
|
end
|