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.
Files changed (83) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/.rubocop.yml +8 -0
  4. data/.rubocop_todo.yml +22 -0
  5. data/.ruby-env.yml.example +12 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +24 -0
  8. data/Gemfile +6 -0
  9. data/LICENSE +191 -0
  10. data/README.md +241 -0
  11. data/Rakefile +35 -0
  12. data/docs/Domains.md +54 -0
  13. data/docs/Events.md +46 -0
  14. data/docs/MessageBuilder.md +105 -0
  15. data/docs/Messages.md +107 -0
  16. data/docs/OptInHandler.md +103 -0
  17. data/docs/Snippets.md +526 -0
  18. data/docs/Suppressions.md +82 -0
  19. data/docs/Webhooks.md +40 -0
  20. data/lib/mailgun-ruby.rb +2 -0
  21. data/lib/mailgun.rb +39 -0
  22. data/lib/mailgun/address.rb +45 -0
  23. data/lib/mailgun/chains.rb +16 -0
  24. data/lib/mailgun/client.rb +199 -0
  25. data/lib/mailgun/domains/domains.rb +84 -0
  26. data/lib/mailgun/events/events.rb +120 -0
  27. data/lib/mailgun/exceptions/exceptions.rb +65 -0
  28. data/lib/mailgun/lists/opt_in_handler.rb +58 -0
  29. data/lib/mailgun/messages/batch_message.rb +125 -0
  30. data/lib/mailgun/messages/message_builder.rb +413 -0
  31. data/lib/mailgun/response.rb +62 -0
  32. data/lib/mailgun/suppressions.rb +270 -0
  33. data/lib/mailgun/version.rb +4 -0
  34. data/lib/mailgun/webhooks/webhooks.rb +101 -0
  35. data/lib/railgun.rb +8 -0
  36. data/lib/railgun/attachment.rb +56 -0
  37. data/lib/railgun/errors.rb +27 -0
  38. data/lib/railgun/mailer.rb +161 -0
  39. data/lib/railgun/message.rb +17 -0
  40. data/lib/railgun/railtie.rb +9 -0
  41. data/mailgun.gemspec +37 -0
  42. data/spec/integration/bounces_spec.rb +44 -0
  43. data/spec/integration/campaign_spec.rb +60 -0
  44. data/spec/integration/complaints_spec.rb +38 -0
  45. data/spec/integration/domains_spec.rb +39 -0
  46. data/spec/integration/email_validation_spec.rb +57 -0
  47. data/spec/integration/events_spec.rb +28 -0
  48. data/spec/integration/list_members_spec.rb +63 -0
  49. data/spec/integration/list_spec.rb +58 -0
  50. data/spec/integration/mailgun_spec.rb +121 -0
  51. data/spec/integration/messages/sample_data/mime.txt +38 -0
  52. data/spec/integration/routes_spec.rb +74 -0
  53. data/spec/integration/stats_spec.rb +15 -0
  54. data/spec/integration/suppressions_spec.rb +126 -0
  55. data/spec/integration/unsubscribes_spec.rb +42 -0
  56. data/spec/integration/webhook_spec.rb +54 -0
  57. data/spec/spec_helper.rb +45 -0
  58. data/spec/unit/connection/test_client.rb +99 -0
  59. data/spec/unit/events/events_spec.rb +50 -0
  60. data/spec/unit/lists/opt_in_handler_spec.rb +24 -0
  61. data/spec/unit/mailgun_spec.rb +127 -0
  62. data/spec/unit/messages/batch_message_spec.rb +131 -0
  63. data/spec/unit/messages/message_builder_spec.rb +584 -0
  64. data/spec/unit/messages/sample_data/mailgun_icon.png +0 -0
  65. data/spec/unit/messages/sample_data/mime.txt +38 -0
  66. data/spec/unit/messages/sample_data/rackspace_logo.jpg +0 -0
  67. data/vcr_cassettes/bounces.yml +175 -0
  68. data/vcr_cassettes/complaints.yml +175 -0
  69. data/vcr_cassettes/domains.todo.yml +42 -0
  70. data/vcr_cassettes/domains.yml +360 -0
  71. data/vcr_cassettes/email_validation.yml +167 -0
  72. data/vcr_cassettes/events.yml +108 -0
  73. data/vcr_cassettes/exceptions.yml +45 -0
  74. data/vcr_cassettes/list_members.yml +320 -0
  75. data/vcr_cassettes/mailing_list.todo.yml +43 -0
  76. data/vcr_cassettes/mailing_list.yml +390 -0
  77. data/vcr_cassettes/routes.yml +359 -0
  78. data/vcr_cassettes/send_message.yml +107 -0
  79. data/vcr_cassettes/stats.yml +44 -0
  80. data/vcr_cassettes/suppressions.yml +676 -0
  81. data/vcr_cassettes/unsubscribes.yml +191 -0
  82. data/vcr_cassettes/webhooks.yml +276 -0
  83. 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
@@ -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
@@ -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