wj-mailgun-ruby 1.1.7
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 +7 -0
- data/.gitignore +23 -0
- data/.rubocop.yml +8 -0
- data/.rubocop_todo.yml +22 -0
- data/.ruby-env.yml.example +12 -0
- data/.ruby-version +1 -0
- data/.travis.yml +24 -0
- data/Gemfile +6 -0
- data/LICENSE +191 -0
- data/README.md +241 -0
- data/Rakefile +35 -0
- data/docs/Domains.md +54 -0
- data/docs/Events.md +46 -0
- data/docs/MessageBuilder.md +105 -0
- data/docs/Messages.md +107 -0
- data/docs/OptInHandler.md +103 -0
- data/docs/Snippets.md +526 -0
- data/docs/Suppressions.md +82 -0
- data/docs/Webhooks.md +40 -0
- data/lib/mailgun-ruby.rb +2 -0
- data/lib/mailgun.rb +39 -0
- data/lib/mailgun/address.rb +45 -0
- data/lib/mailgun/chains.rb +16 -0
- data/lib/mailgun/client.rb +199 -0
- data/lib/mailgun/domains/domains.rb +84 -0
- data/lib/mailgun/events/events.rb +120 -0
- data/lib/mailgun/exceptions/exceptions.rb +65 -0
- data/lib/mailgun/lists/opt_in_handler.rb +58 -0
- data/lib/mailgun/messages/batch_message.rb +125 -0
- data/lib/mailgun/messages/message_builder.rb +413 -0
- data/lib/mailgun/response.rb +62 -0
- data/lib/mailgun/suppressions.rb +270 -0
- data/lib/mailgun/version.rb +4 -0
- data/lib/mailgun/webhooks/webhooks.rb +101 -0
- data/lib/railgun.rb +8 -0
- data/lib/railgun/attachment.rb +56 -0
- data/lib/railgun/errors.rb +27 -0
- data/lib/railgun/mailer.rb +161 -0
- data/lib/railgun/message.rb +17 -0
- data/lib/railgun/railtie.rb +9 -0
- data/mailgun.gemspec +37 -0
- data/spec/integration/bounces_spec.rb +44 -0
- data/spec/integration/campaign_spec.rb +60 -0
- data/spec/integration/complaints_spec.rb +38 -0
- data/spec/integration/domains_spec.rb +39 -0
- data/spec/integration/email_validation_spec.rb +57 -0
- data/spec/integration/events_spec.rb +28 -0
- data/spec/integration/list_members_spec.rb +63 -0
- data/spec/integration/list_spec.rb +58 -0
- data/spec/integration/mailgun_spec.rb +121 -0
- data/spec/integration/messages/sample_data/mime.txt +38 -0
- data/spec/integration/routes_spec.rb +74 -0
- data/spec/integration/stats_spec.rb +15 -0
- data/spec/integration/suppressions_spec.rb +126 -0
- data/spec/integration/unsubscribes_spec.rb +42 -0
- data/spec/integration/webhook_spec.rb +54 -0
- data/spec/spec_helper.rb +45 -0
- data/spec/unit/connection/test_client.rb +99 -0
- data/spec/unit/events/events_spec.rb +50 -0
- data/spec/unit/lists/opt_in_handler_spec.rb +24 -0
- data/spec/unit/mailgun_spec.rb +127 -0
- data/spec/unit/messages/batch_message_spec.rb +131 -0
- data/spec/unit/messages/message_builder_spec.rb +584 -0
- data/spec/unit/messages/sample_data/mailgun_icon.png +0 -0
- data/spec/unit/messages/sample_data/mime.txt +38 -0
- data/spec/unit/messages/sample_data/rackspace_logo.jpg +0 -0
- data/vcr_cassettes/bounces.yml +175 -0
- data/vcr_cassettes/complaints.yml +175 -0
- data/vcr_cassettes/domains.todo.yml +42 -0
- data/vcr_cassettes/domains.yml +360 -0
- data/vcr_cassettes/email_validation.yml +167 -0
- data/vcr_cassettes/events.yml +108 -0
- data/vcr_cassettes/exceptions.yml +45 -0
- data/vcr_cassettes/list_members.yml +320 -0
- data/vcr_cassettes/mailing_list.todo.yml +43 -0
- data/vcr_cassettes/mailing_list.yml +390 -0
- data/vcr_cassettes/routes.yml +359 -0
- data/vcr_cassettes/send_message.yml +107 -0
- data/vcr_cassettes/stats.yml +44 -0
- data/vcr_cassettes/suppressions.yml +676 -0
- data/vcr_cassettes/unsubscribes.yml +191 -0
- data/vcr_cassettes/webhooks.yml +276 -0
- metadata +263 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
module Railgun
|
2
|
+
|
3
|
+
class Attachment < StringIO
|
4
|
+
|
5
|
+
attr_reader :filename, :content_type, :path,
|
6
|
+
:original_filename, :overwritten_filename
|
7
|
+
|
8
|
+
def initialize(attachment, *args)
|
9
|
+
@path = ''
|
10
|
+
@inline = args.detect { |opt| opt[:inline] }
|
11
|
+
|
12
|
+
if @inline
|
13
|
+
@filename = attachment.cid
|
14
|
+
else
|
15
|
+
@filename = attachment.filename
|
16
|
+
end
|
17
|
+
|
18
|
+
@original_filename = @filename
|
19
|
+
|
20
|
+
if args.detect { |opt| opt[:filename] }
|
21
|
+
@filename = opt[:filename]
|
22
|
+
end
|
23
|
+
|
24
|
+
@overwritten_filename = @filename
|
25
|
+
|
26
|
+
@content_type = attachment.content_type.split(';')[0]
|
27
|
+
|
28
|
+
super attachment.body.decoded
|
29
|
+
end
|
30
|
+
|
31
|
+
def inline?
|
32
|
+
@inline
|
33
|
+
end
|
34
|
+
|
35
|
+
def is_original_filename
|
36
|
+
@original_filename == @overwritten_filename
|
37
|
+
end
|
38
|
+
|
39
|
+
def source_filename
|
40
|
+
@filename
|
41
|
+
end
|
42
|
+
|
43
|
+
def attach_to_message!(mb)
|
44
|
+
if mb.nil?
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
|
48
|
+
if inline?
|
49
|
+
mb.add_inline_image self, @filename
|
50
|
+
else
|
51
|
+
mb.add_attachment self, @filename
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Railgun
|
2
|
+
|
3
|
+
class Error < StandardError
|
4
|
+
|
5
|
+
attr_reader :object
|
6
|
+
|
7
|
+
def initialize(message = nil, object = nil)
|
8
|
+
super(message)
|
9
|
+
|
10
|
+
@object = object
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class ConfigurationError < Error
|
15
|
+
end
|
16
|
+
|
17
|
+
class InternalError < Error
|
18
|
+
|
19
|
+
attr_reader :source_exception
|
20
|
+
|
21
|
+
def initialize(source_exc, message = nil, object = nil)
|
22
|
+
super(message, object)
|
23
|
+
|
24
|
+
@source_exception = source_exc
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,161 @@
|
|
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, :settings
|
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
|
+
# To avoid exception in mail gem v2.6
|
30
|
+
@settings = { return_response: true }
|
31
|
+
|
32
|
+
if (@config[:fake_message_send] || false)
|
33
|
+
Rails.logger.info "NOTE: fake message sending has been enabled for mailgun-ruby!"
|
34
|
+
@mg_client.enable_test_mode!
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def deliver!(mail)
|
39
|
+
mg_message = Railgun.transform_for_mailgun(mail)
|
40
|
+
response = @mg_client.send_message(@domain, mg_message)
|
41
|
+
|
42
|
+
if response.code == 200 then
|
43
|
+
mg_id = response.to_h['id']
|
44
|
+
mail.message_id = mg_id
|
45
|
+
end
|
46
|
+
response
|
47
|
+
end
|
48
|
+
|
49
|
+
def mailgun_client
|
50
|
+
@mg_obj
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
module_function
|
56
|
+
|
57
|
+
# Performs a series of transformations on the `mailgun*` attributes.
|
58
|
+
# After prefixing them with the proper option type, they are added to
|
59
|
+
# the message hash where they will then be sent to the API as JSON.
|
60
|
+
#
|
61
|
+
# @param [Mail::Message] mail message to transform
|
62
|
+
#
|
63
|
+
# @return [Hash] transformed message hash
|
64
|
+
def transform_for_mailgun(mail)
|
65
|
+
message = build_message_object(mail)
|
66
|
+
|
67
|
+
# v:* attributes (variables)
|
68
|
+
mail.mailgun_variables.try(:each) do |k, v|
|
69
|
+
message["v:#{k}"] = v
|
70
|
+
end
|
71
|
+
|
72
|
+
# o:* attributes (options)
|
73
|
+
mail.mailgun_options.try(:each) do |k, v|
|
74
|
+
message["o:#{k}"] = v
|
75
|
+
end
|
76
|
+
|
77
|
+
# h:* attributes (headers)
|
78
|
+
mail.mailgun_headers.try(:each) do |k, v|
|
79
|
+
message["h:#{k}"] = v
|
80
|
+
end
|
81
|
+
|
82
|
+
# recipient variables
|
83
|
+
message['recipient-variables'] = mail.mailgun_recipient_variables.to_json if mail.mailgun_recipient_variables
|
84
|
+
|
85
|
+
# reject blank values
|
86
|
+
message.delete_if do |k, v|
|
87
|
+
v.nil? or (v.respond_to?(:empty) and v.empty?)
|
88
|
+
end
|
89
|
+
|
90
|
+
return message
|
91
|
+
end
|
92
|
+
|
93
|
+
# Acts on a Rails/ActionMailer message object and uses Mailgun::MessageBuilder
|
94
|
+
# to construct a new message.
|
95
|
+
#
|
96
|
+
# @param [Mail::Message] mail message to transform
|
97
|
+
#
|
98
|
+
# @returns [Hash] Message hash from Mailgun::MessageBuilder
|
99
|
+
def build_message_object(mail)
|
100
|
+
mb = Mailgun::MessageBuilder.new
|
101
|
+
|
102
|
+
mb.from mail[:from]
|
103
|
+
mb.reply_to(mail[:reply_to].to_s) if mail[:reply_to].present?
|
104
|
+
mb.subject mail.subject
|
105
|
+
mb.body_html extract_body_html(mail)
|
106
|
+
mb.body_text extract_body_text(mail)
|
107
|
+
|
108
|
+
[:to, :cc, :bcc].each do |rcpt_type|
|
109
|
+
addrs = mail[rcpt_type] || nil
|
110
|
+
case addrs
|
111
|
+
when String
|
112
|
+
# Likely a single recipient
|
113
|
+
mb.add_recipient rcpt_type.to_s, addrs
|
114
|
+
when Array
|
115
|
+
addrs.each do |addr|
|
116
|
+
mb.add_recipient rcpt_type.to_s, addr
|
117
|
+
end
|
118
|
+
when Mail::Field
|
119
|
+
mb.add_recipient rcpt_type.to_s, addrs.to_s
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
return mb.message if mail.attachments.empty?
|
124
|
+
|
125
|
+
mail.attachments.each do |attach|
|
126
|
+
attach = Attachment.new(attach, encoding: 'ascii-8bit', inline: attach.inline?)
|
127
|
+
attach.attach_to_message! mb
|
128
|
+
end
|
129
|
+
|
130
|
+
return mb.message
|
131
|
+
end
|
132
|
+
|
133
|
+
# Returns the decoded HTML body from the Mail::Message object if available,
|
134
|
+
# otherwise nil.
|
135
|
+
#
|
136
|
+
# @param [Mail::Message] mail message to transform
|
137
|
+
#
|
138
|
+
# @return [String]
|
139
|
+
def extract_body_html(mail)
|
140
|
+
begin
|
141
|
+
(mail.html_part || mail).body.decoded || nil
|
142
|
+
rescue
|
143
|
+
nil
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Returns the decoded text body from the Mail::Message object if it is available,
|
148
|
+
# otherwise nil.
|
149
|
+
#
|
150
|
+
# @param [Mail::Message] mail message to transform
|
151
|
+
#
|
152
|
+
# @return [String]
|
153
|
+
def extract_body_text(mail)
|
154
|
+
begin
|
155
|
+
(mail.text_part || mail).body.decoded || nil
|
156
|
+
rescue
|
157
|
+
nil
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
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
|
data/mailgun.gemspec
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'mailgun/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
|
8
|
+
spec.name = 'wj-mailgun-ruby'
|
9
|
+
spec.version = Mailgun::VERSION
|
10
|
+
spec.homepage = 'http://www.mailgun.com'
|
11
|
+
spec.platform = Gem::Platform::RUBY
|
12
|
+
spec.license = 'Apache-2.0'
|
13
|
+
|
14
|
+
spec.summary = "Mailgun's Official Ruby SDK"
|
15
|
+
spec.description = "Mailgun's Official Ruby SDK for interacting with the Mailgun API."
|
16
|
+
|
17
|
+
spec.authors = ['Mailgun', 'Travis Swientek',"greatmanta111"]
|
18
|
+
spec.email = 'support@mailgunhq.com'
|
19
|
+
|
20
|
+
spec.files = `git ls-files -z`.split("\x0")
|
21
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
22
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
23
|
+
spec.require_paths = ["lib"]
|
24
|
+
|
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
|
+
|
35
|
+
spec.add_dependency 'rest-client', '~> 2.0'
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'mailgun'
|
3
|
+
|
4
|
+
vcr_opts = { :cassette_name => "bounces" }
|
5
|
+
|
6
|
+
describe 'For the Bounces endpoint', order: :defined, vcr: vcr_opts do
|
7
|
+
before(:all) do
|
8
|
+
@mg_obj = Mailgun::Client.new(APIKEY, APIHOST, APIVERSION, SSL)
|
9
|
+
@domain = TESTDOMAIN
|
10
|
+
@email = "integration-test-email@#{TESTDOMAIN}"
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'creates a bounce' do
|
14
|
+
@result = @mg_obj.post("#{@domain}/bounces",
|
15
|
+
{:address => @email,
|
16
|
+
:code => 550,
|
17
|
+
:error => "Integration Test"})
|
18
|
+
|
19
|
+
@result.to_h!
|
20
|
+
expect(@result.body["message"]).to eq("Address has been added to the bounces table")
|
21
|
+
expect(@result.body["address"]).to eq(@email)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'get a bounce.' do
|
25
|
+
result = @mg_obj.get("#{@domain}/bounces/#{@email}")
|
26
|
+
|
27
|
+
result.to_h!
|
28
|
+
expect(result.body["code"]).to eq("550")
|
29
|
+
expect(result.body["address"]).to eq(@email)
|
30
|
+
expect(result.body["error"]).to eq("Integration Test")
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'gets a list of bounces.' do
|
34
|
+
result = @mg_obj.get("#{@domain}/bounces")
|
35
|
+
|
36
|
+
result.to_h!
|
37
|
+
expect(result.body["items"].length).to be > 0
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'deletes a bounce' do
|
41
|
+
@mg_obj.delete("#{@domain}/bounces/#{@email}")
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'mailgun'
|
3
|
+
|
4
|
+
vcr_opts = { :cassette_name => "campaigns" }
|
5
|
+
|
6
|
+
describe 'For the campaigns endpoint', vcr: vcr_opts do
|
7
|
+
before(:all) do
|
8
|
+
skip 'pending removal'
|
9
|
+
@mg_obj = Mailgun::Client.new(APIKEY, APIHOST, APIVERSION, SSL)
|
10
|
+
@domain = TESTDOMAIN
|
11
|
+
@campaign_id = "integration_test_campaign"
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'creates a campaign' do
|
15
|
+
result = @mg_obj.post("#{@domain}/campaigns", {:name => 'My Campaign',
|
16
|
+
:id => @campaign_id})
|
17
|
+
|
18
|
+
result.to_h!
|
19
|
+
expect(result.body["message"]).to eq("Campaign created")
|
20
|
+
expect(result.body["campaign"]["id"]).to eq(@campaign_id)
|
21
|
+
expect(result.body["campaign"]["name"]).to eq('My Campaign')
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'get a campaign.' do
|
25
|
+
result = @mg_obj.get("#{@domain}/campaigns/#{@campaign_id}")
|
26
|
+
|
27
|
+
result.to_h!
|
28
|
+
expect(result.body["id"]).to eq(@campaign_id)
|
29
|
+
expect(result.body["name"]).to eq('My Campaign')
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'gets a list of all campaigns.' do
|
33
|
+
result = @mg_obj.get("#{@domain}/campaigns", {:limit => 50})
|
34
|
+
|
35
|
+
result.to_h!
|
36
|
+
expect(result.body["total_count"]).to be > 0
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'update a campaign.' do
|
40
|
+
result = @mg_obj.put("#{@domain}/campaigns/#{@campaign_id}", {:name => 'My Updated Campaign',
|
41
|
+
:id => @campaign_id})
|
42
|
+
|
43
|
+
result.to_h!
|
44
|
+
expect(result.body["message"]).to eq("Campaign updated")
|
45
|
+
expect(result.body["campaign"]["id"]).to eq(@campaign_id)
|
46
|
+
expect(result.body["campaign"]["name"]).to eq('My Updated Campaign')
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'get campaign events.' do
|
50
|
+
expect{@mg_obj.get("#{@domain}/campaigns/#{@campaign_id}/events", {:groupby => "clicked"})}.not_to raise_error
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'get campaign stats.' do
|
54
|
+
expect{@mg_obj.get("#{@domain}/campaigns/#{@campaign_id}/stats", {:groupby => "domain"})}.not_to raise_error
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'removes a campaign' do
|
58
|
+
@mg_obj.delete("#{@domain}/campaigns/#{@campaign_id}")
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'mailgun'
|
3
|
+
|
4
|
+
vcr_opts = { :cassette_name => "complaints" }
|
5
|
+
|
6
|
+
describe 'For the Complaints endpoint', order: :defined, vcr: vcr_opts do
|
7
|
+
before(:all) do
|
8
|
+
@mg_obj = Mailgun::Client.new(APIKEY, APIHOST, APIVERSION, SSL)
|
9
|
+
@domain = TESTDOMAIN
|
10
|
+
@email = "integration-test-email@#{TESTDOMAIN}"
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'creates a complaint' do
|
14
|
+
@result = @mg_obj.post("#{@domain}/complaints", {:address => @email})
|
15
|
+
|
16
|
+
@result.to_h!
|
17
|
+
expect(@result.body["message"]).to eq("Address has been added to the complaints table")
|
18
|
+
expect(@result.body["address"]).to eq(@email)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'get a complaint.' do
|
22
|
+
result = @mg_obj.get("#{@domain}/complaints/#{@email}")
|
23
|
+
|
24
|
+
result.to_h!
|
25
|
+
expect(result.body["address"]).to eq(@email)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'gets a list of complaints.' do
|
29
|
+
result = @mg_obj.get("#{@domain}/complaints")
|
30
|
+
|
31
|
+
result.to_h!
|
32
|
+
expect(result.body["items"].length).to be > 0
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'removes a complaint' do
|
36
|
+
@mg_obj.delete("#{@domain}/complaints/#{@email}")
|
37
|
+
end
|
38
|
+
end
|