mailgun 0.0.3 → 0.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.
data/.gitignore CHANGED
@@ -15,3 +15,7 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ startup.rb
19
+ Guardfile
20
+ .DS_Store
21
+ lib/startup.rb
data/Gemfile CHANGED
@@ -1,9 +1,9 @@
1
1
  source 'http://rubygems.org'
2
2
 
3
- gem "rest-client"
4
-
5
3
  group :development do
6
4
  gem "rspec"
5
+ gem "guard"
6
+ gem 'simplecov', :require => false, :group => :test
7
7
  end
8
8
 
9
9
  # Specify your gem's dependencies in mailgun.gemspec
data/README.md CHANGED
@@ -6,30 +6,133 @@ The official gem repo is at https://github.com/Bushido/mailgun
6
6
 
7
7
  Mailgun exposes the following resources:
8
8
 
9
+ * Mailing Lists
10
+ * Mailing List Members
11
+ * Mailboxes
9
12
  * Routes
10
13
  * Log
11
14
  * Stats
12
15
  * Messages
13
- * Mailboxes
16
+ * Bounces
17
+ * Unsubscribes
18
+ * Complaints
19
+
20
+ Currently the gem only exposes the Mailbox and Routes APIs, but patches are welcome (and easy!).
21
+
22
+ ActionMailer
23
+ ============
24
+
25
+ This gem is unnecessary if you'd like to simply hook up Mailgun to Rails' Action Mailer. Just add the below to your Configuration.
14
26
 
15
- Currently the gem only exposes the Mailbox API, but patches are welcome (and easy!).
27
+ ActionMailer::Base.smtp_settings = {
28
+ :port => 587,
29
+ :address => 'smtp.mailgun.org',
30
+ :user_name => 'postmaster@your.mailgun.domain',
31
+ :password => 'mailgun-smtp-password',
32
+ :domain => 'your.mailgun.domain',
33
+ :authentication => :plain,
34
+ }
35
+ ActionMailer::Base.delivery_method = :smtp
16
36
 
17
37
  Usage
18
38
  =====
19
39
  We mimic the ActiveRecord-style interface.
20
40
 
41
+
42
+ Configuration:
43
+
21
44
  # Initialize your Mailgun object:
45
+ Mailgun.configure do |config|
46
+ config.api_key = 'your-api-key'
47
+ config.domain = 'your-mailgun-domain'
48
+ end
49
+
50
+ @mailgun = Mailgun()
51
+
52
+ # or alternatively:
22
53
  @mailgun = Mailgun(:api_key => 'your-api-key')
23
54
 
55
+
56
+ Mailing Lists:
57
+
58
+ # Create a mailing list
59
+ @mailgun.lists.create "devs@your.mailgun.domain"
60
+
61
+ # List all Mailing lists
62
+ @mailgun.lists.all
63
+
64
+ # Find a mailing list
65
+ @mailgun.lists.find "devs@your.mailgun.domain"
66
+
67
+ # Update a mailing list
68
+ @mailgun.lists.update("devs@your.mailgun.domain", "developers@your.mailgun.domain", "Developers", "Develepor Mailing List")
69
+
70
+ # Delete a mailing list
71
+ @mailgun.lists.delete("developers@your.mailgun.domain")
72
+
73
+ Mailing List Members:
74
+
75
+ # List all members within a mailing list
76
+ @mailgun.list_members.list "devs@your.mailgun.domain"
77
+
78
+ # Find a particular member in a list
79
+ @mailgun.list_members.find "devs@your.mailgun.domain", "bond@mi6.co.uk"
80
+
81
+ # Add a member to a list
82
+ @mailgun.list_members.add "devs@your.mailgun.domain", "Q@mi6.co.uk"
83
+
84
+ # Update a member on a list
85
+ @mailgun.list_members.update "devs@your.mailgun.domain", "Q@mi6.co.uk", "Q", {:gender => 'male'}.to_json, :subscribed => 'no')
86
+
87
+ # Remove a member from a list
88
+ @mailgun.list_members.remove "devs@your.mailgun.domain", "M@mi6.co.uk"
89
+
90
+ Mailboxes:
91
+
24
92
  # Create a mailbox
25
- @mailgun.mailbox.create "new-mailbox@your-domain.com", "password"
93
+ @mailgun.mailboxes.create "new-mailbox@your-domain.com", "password"
26
94
 
27
95
  # List all mailboxes that belong to a domain
28
- @mailgun.mailboxes.list domain.com"
96
+ @mailgun.mailboxes.list "domain.com"
29
97
 
30
98
  # Destroy a mailbox (queue bond-villian laughter)
31
99
  # "I'm sorry Bond, it seems your mailbox will be... destroyed!"
32
- @mailbox.mailboxes.destroy "bond@mi5.co.uk"
100
+ @mailbox.mailboxes.destroy "bond@mi6.co.uk"
101
+
102
+
103
+ Routes:
104
+
105
+ # Initialize your Mailgun object:
106
+ @mailgun = Mailgun(:api_key => 'your-api-key')
107
+
108
+ # Create a route
109
+ # Give it a human-readable description for later, a priority
110
+ # filters, and actions
111
+ @mailgun.routes.create "Description for the new route", 1,
112
+ [:match_recipient, "apowers@mi5.co.uk"],
113
+ [[:forward, "http://my-site.com/incoming-mail-route"],
114
+ [:stop]]
115
+
116
+ # List all routes that belong to a domain
117
+ # limit the query to 100 routes starting from 0
118
+ @mailgun.routes.list 100, 0
119
+
120
+ # Get the details of a route via its id
121
+ @mailgun.routes.find "4e97c1b2ba8a48567f007fb6"
122
+
123
+ # Update a route via its id
124
+ # (all keys are optional)
125
+ @mailgun.routes.update "4e97c1b2ba8a48567f007fb6", {
126
+ :priority => 2,
127
+ :filter => [:match_header, :subject, "*.support"],
128
+ :actions => [[:forward, "http://new-site.com/incoming-emails"]]
129
+ }
130
+
131
+ # Destroy a route via its id
132
+ @mailbox.routes.destroy "4e97c1b2ba8a48567f007fb6"
133
+
134
+ Supported route filters are: `:match_header`, `:match_recipient`, and `:catch_all`
135
+ Supported route actions are: `:forward`, and `:stop`
33
136
 
34
137
  Making Your Changes
35
138
  ===================
@@ -41,18 +144,28 @@ Making Your Changes
41
144
  * After making your changes, be sure to run the Mailgun RSpec specs to make sure everything works.
42
145
  * Submit your change as a Pull Request and update the GitHub issue to let us know it is ready for review.
43
146
 
147
+
148
+ TODO
149
+ =========
150
+
151
+ * Mailgun() is overwriting api key. api key is not persisting
152
+ * Add skip and limit functionality
153
+ * Distinguish failed in logs
154
+ * Distinguish delivered in logs
155
+ * Tracking?
156
+ * Stats?
157
+ * Campaign?
158
+
44
159
  Authors
45
160
  =======
46
161
 
47
162
  * Akash Manohar J (akash@akash.im)
163
+ * Scott Carleton (@scotterc) - for new functionality and major improvements.
48
164
  * Sean Grove (sean@gobushido.com)
165
+
49
166
 
50
- Thanks
51
- ======
52
- Huge thanks to the Mailgun guys for such an amazing service! No time spent on mailservers == way more time spent drinking!
53
-
54
- License & Copyright
167
+ License
55
168
  ===================
56
- Released under the MIT license. See LICENSE for more details.
169
+ Released under the MIT license by CloudFuji. See LICENSE for more details.
57
170
 
58
- All copyright Bushido Inc. 2011
171
+ This is a fork of he Cloudfuji/mailgun repo.
data/lib/mailgun/base.rb CHANGED
@@ -1,11 +1,5 @@
1
1
  module Mailgun
2
2
  class Base
3
- attr_accessor :api_key,
4
- :api_version,
5
- :protocol,
6
- :mailgun_host,
7
- :use_test_mode
8
-
9
3
  # Options taken from
10
4
  # http://documentation.mailgun.net/quickstart.html#authentication
11
5
  # * Mailgun host - location of mailgun api servers
@@ -13,22 +7,86 @@ module Mailgun
13
7
  # * API key and version
14
8
  # * Test mode - if enabled, doesn't actually send emails (see http://documentation.mailgun.net/user_manual.html#sending-in-test-mode)
15
9
  def initialize(options)
16
- @mailgun_host = options.fetch(:mailgun_host) {"api.mailgun.net"}
17
- @protocol = options.fetch(:protocol) { "https" }
18
- @api_version = options.fetch(:api_version) { "v2" }
19
- @test_mode = options.fetch(:test_mode) { false }
20
-
21
- @api_key = options.fetch(:api_key) { raise ArgumentError(":api_key is a required argument to initialize Mailgun") }
10
+ Mailgun.mailgun_host = options.fetch(:mailgun_host) {"api.mailgun.net"}
11
+ Mailgun.protocol = options.fetch(:protocol) { "https" }
12
+ Mailgun.api_version = options.fetch(:api_version) { "v2" }
13
+ Mailgun.test_mode = options.fetch(:test_mode) { false }
14
+ Mailgun.api_key = options.fetch(:api_key) { raise ArgumentError.new(":api_key is a required argument to initialize Mailgun") if Mailgun.api_key.nil?}
22
15
  end
23
16
 
24
17
  # Returns the base url used in all Mailgun API calls
25
18
  def base_url
26
- "#{@protocol}://api:#{api_key}@#{mailgun_host}/#{api_version}"
19
+ "#{Mailgun.protocol}://api:#{Mailgun.api_key}@#{Mailgun.mailgun_host}/#{Mailgun.api_version}"
27
20
  end
28
21
 
29
22
  # Returns an instance of Mailgun::Mailbox configured for the current API user
30
23
  def mailboxes
31
24
  @mailboxes ||= Mailgun::Mailbox.new(self)
32
25
  end
26
+
27
+ def routes
28
+ @routes ||= Mailgun::Route.new(self)
29
+ end
30
+
31
+ def bounces
32
+ @bounces ||= Mailgun::Bounce.new(self)
33
+ end
34
+
35
+ def unsubscribes
36
+ @unsubscribes ||= Mailgun::Unsubscribe.new(self)
37
+ end
38
+
39
+ def complaints
40
+ @complaints ||= Mailgun::Complaint.new(self)
41
+ end
42
+
43
+ def log
44
+ @log ||= Mailgun::Log.new(self)
45
+ end
46
+
47
+ def lists
48
+ @lists ||= Mailgun::List.new(self)
49
+ end
50
+
51
+ def list_members
52
+ @list_members ||= Mailgun::List::Member.new(self)
53
+ end
54
+ end
55
+
56
+
57
+ # Submits the API call to the Mailgun server
58
+ def self.submit(method, url, parameters={})
59
+ begin
60
+ return JSON(RestClient.send(method, url, parameters))
61
+ rescue => e
62
+ error_message = nil
63
+ if e.respond_to? :http_body
64
+ begin
65
+ error_message = JSON(e.http_body)["message"]
66
+ rescue
67
+ raise e
68
+ end
69
+ raise Mailgun::Error.new(error_message)
70
+ end
71
+ raise e
72
+ end
73
+ end
74
+
75
+ #
76
+ # @TODO Create root module to give this a better home
77
+ #
78
+ class << self
79
+ attr_accessor :api_key,
80
+ :api_version,
81
+ :protocol,
82
+ :mailgun_host,
83
+ :test_mode,
84
+ :domain
85
+
86
+ def configure
87
+ yield self
88
+ true
89
+ end
90
+ alias :config :configure
33
91
  end
34
92
  end
@@ -0,0 +1,35 @@
1
+ module Mailgun
2
+ class Bounce
3
+ # Used internally, called from Mailgun::Base
4
+ def initialize(mailgun)
5
+ @mailgun = mailgun
6
+ end
7
+
8
+ # List all bounces for a given domain
9
+ # * domain the domain for which all bounces will listed
10
+ def list(domain = Mailgun.domain)
11
+ response = Mailgun.submit :get, bounce_url(domain)
12
+
13
+ if response
14
+ response["items"].collect {|item| item["address"]}
15
+ end
16
+ end
17
+
18
+ def find(domain = Mailgun.domain, email)
19
+ Mailgun.submit :get, bounce_url(domain, email)
20
+ end
21
+
22
+ def add(domain = Mailgun.domain, email)
23
+ Mailgun.submit :post, bounce_url(domain), :address => email
24
+ end
25
+
26
+ private
27
+
28
+ # Helper method to generate the proper url for Mailgun mailbox API calls
29
+ def bounce_url(domain, address=nil)
30
+ domain = Mailgun.domain if Mailgun.domain
31
+ "#{@mailgun.base_url}/#{domain}/bounces#{'/' + address if address}"
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,38 @@
1
+ module Mailgun
2
+ class Complaint
3
+ # Used internally, called from Mailgun::Base
4
+ def initialize(mailgun)
5
+ @mailgun = mailgun
6
+ end
7
+
8
+ # List all complaints for a given domain
9
+ # * domain the domain for which all complaints will listed
10
+ def list(domain = Mailgun.domain)
11
+ response = Mailgun.submit :get, complaint_url(domain)
12
+
13
+ if response
14
+ response["items"].collect {|item| item["address"]}
15
+ end
16
+ end
17
+
18
+ def find(domain = Mailgun.domain, email)
19
+ Mailgun.submit :get, complaint_url(domain, email)
20
+ end
21
+
22
+ def add(domain=Mailgun.domain, email)
23
+ Mailgun.submit :post, complaint_url(domain), {:address => email}
24
+ end
25
+
26
+ def remove(domain = Mailgun.domain, email)
27
+ Mailgun.submit :delete, complaint_url(domain, email)
28
+ end
29
+
30
+ private
31
+
32
+ # Helper method to generate the proper url for Mailgun complaints API calls
33
+ def complaint_url(domain, address=nil)
34
+ "#{@mailgun.base_url}/#{domain}/complaints#{'/' + address if address}"
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,57 @@
1
+ module Mailgun
2
+ class List::Member
3
+ # Used internally, called from Mailgun::Base
4
+ def initialize(mailgun)
5
+ @mailgun = mailgun
6
+ end
7
+
8
+ ## List Member functionality
9
+
10
+ # List all mailing list members
11
+ # TODO add parameters: subscribed, limit, skip
12
+ def list(address)
13
+ response = Mailgun.submit :get, list_member_url(address)
14
+
15
+ if response
16
+ response["items"].collect {|item| item["address"]}
17
+ end
18
+ end
19
+
20
+ # List a single mailing list member by a given address
21
+ def find(address, member_address)
22
+ Mailgun.submit :get, list_member_url(address, member_address)
23
+ end
24
+
25
+ # Adds a mailing list member with a given address
26
+ # TODO add name, vars, subscribed, upsert
27
+ def add(address, member_address, name=nil, vars={}, subscribed='yes', upsert='no')
28
+ params = {:address => member_address, :subscribed => subscribed, :upsert => 'no'}
29
+ params[:name] = name if name
30
+ params[:vars] = vars unless vars.empty?
31
+ Mailgun.submit :post, list_member_url(address), params
32
+ end
33
+
34
+ # Update a mailing list member with a given address
35
+ # with an optional new member_address, name, vars and subscribed
36
+ def update(address, member_address, name=nil, vars={}, subscribed='yes')
37
+ params = {:address => member_address, :subscribed => subscribed}
38
+ params[:name] = name if name
39
+ params[:vars] = vars unless vars.empty?
40
+ Mailgun.submit :put, list_member_url(address, member_address), params
41
+ end
42
+
43
+ # Deletes a mailing list member with a given address
44
+ def remove(address, member_address)
45
+ Mailgun.submit :delete, list_member_url(address, member_address)
46
+ end
47
+
48
+
49
+ private
50
+
51
+ # Helper method to generate the proper url for Mailgun mailbox API calls
52
+ def list_member_url(address, member_address=nil)
53
+ "#{@mailgun.base_url}/lists#{'/' + address}/members#{'/' + member_address if member_address}"
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,59 @@
1
+ module Mailgun
2
+ class List
3
+ # Used internally, called from Mailgun::Base
4
+ def initialize(mailgun)
5
+ @mailgun = mailgun
6
+ end
7
+
8
+
9
+ ## List functionality
10
+
11
+ # TODO add default domain functionality for the address names of lists
12
+
13
+ # List all mailing lists
14
+ def all
15
+ response = Mailgun.submit :get, list_url
16
+
17
+ if response
18
+ response["items"].collect {|item| item["address"]}
19
+ end
20
+ end
21
+
22
+ # List a single mailing list by a given address
23
+ def find(address)
24
+ Mailgun.submit :get, list_url(address)
25
+ end
26
+
27
+ # Create a mailing list with a given address
28
+ # with an optional name and description
29
+ def create(address, name=nil, description=nil)
30
+ params = {:address => address}
31
+ params[:name] = name if name
32
+ params[:description] = description if description
33
+ Mailgun.submit :post, list_url, params
34
+ end
35
+
36
+ # Update a mailing list with a given address
37
+ # with an optional new address, name or description
38
+ def update(address, new_address, name=nil, description=nil)
39
+ params = {:address => new_address}
40
+ params[:name] = name if name
41
+ params[:description] = description if description
42
+ Mailgun.submit :put, list_url(address), params
43
+ end
44
+
45
+ # Deletes a mailing list with a given address
46
+ def delete(address)
47
+ Mailgun.submit :delete, list_url(address)
48
+ end
49
+
50
+
51
+ private
52
+
53
+ # Helper method to generate the proper url for Mailgun mailbox API calls
54
+ def list_url(address=nil)
55
+ "#{@mailgun.base_url}/lists#{'/' + address if address}"
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,26 @@
1
+ module Mailgun
2
+ class Log
3
+ # Used internally, called from Mailgun::Base
4
+ def initialize(mailgun)
5
+ @mailgun = mailgun
6
+ end
7
+
8
+ # List all logs for a given domain
9
+ # * domain the domain for which all complaints will listed
10
+ def list(domain=Mailgun.domain, limit=100, skip=0)
11
+ response = Mailgun.submit :get, log_url(domain), {:limit => limit, :skip => skip}
12
+
13
+ if response
14
+ response["items"].collect {|item| item["message"]}
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ # Helper method to generate the proper url for Mailgun complaints API calls
21
+ def log_url(domain)
22
+ "#{@mailgun.base_url}/#{domain}/log"
23
+ end
24
+
25
+ end
26
+ end
@@ -5,17 +5,21 @@ module Mailgun
5
5
  def initialize(mailgun)
6
6
  @mailgun = mailgun
7
7
  end
8
-
8
+
9
9
  # List all mailboxes for a given domain
10
10
  # * domain the domain for which all mailboxes will listed
11
- def list(domain)
12
- submit :get, mailbox_url(domain)
11
+ def list(domain = Mailgun.domain)
12
+ response = Mailgun.submit :get, mailbox_url(domain)
13
+
14
+ if response
15
+ response["items"].collect {|item| item["mailbox"]}
16
+ end
13
17
  end
14
18
 
15
19
 
16
20
  # Creates a mailbox on the Mailgun server with the given password
17
21
  def create(address, password)
18
- submit :post, mailbox_url(address.split("@").last), :mailbox => address,
22
+ Mailgun.submit :post, mailbox_url(address.split("@").last), :mailbox => address,
19
23
  :password => password
20
24
  end
21
25
 
@@ -24,7 +28,7 @@ module Mailgun
24
28
  def update_password(address, password)
25
29
  mailbox_name, domain = address.split("@")
26
30
 
27
- submit :put, mailbox_url(domain, mailbox_name), :password => password
31
+ Mailgun.submit :put, mailbox_url(domain, mailbox_name), :password => password
28
32
  end
29
33
 
30
34
 
@@ -32,7 +36,7 @@ module Mailgun
32
36
  def destroy(address)
33
37
  mailbox_name, domain = address.split("@")
34
38
 
35
- submit :delete, mailbox_url(domain, mailbox_name)
39
+ Mailgun.submit :delete, mailbox_url(domain, mailbox_name)
36
40
  end
37
41
 
38
42
 
@@ -43,10 +47,5 @@ module Mailgun
43
47
  "#{@mailgun.base_url}/#{domain}/mailboxes#{'/' + mailbox_name if mailbox_name}"
44
48
  end
45
49
 
46
-
47
- # Submits the API call to the Mailgun server
48
- def submit(method, url, parameters={})
49
- JSON(RestClient.send(method, url, parameters))
50
- end
51
50
  end
52
51
  end
@@ -0,0 +1,4 @@
1
+ module Mailgun
2
+ class Error < StandardError
3
+ end
4
+ end
@@ -0,0 +1,94 @@
1
+ module Mailgun
2
+ class Route
3
+
4
+ def initialize(mailgun)
5
+ @mailgun = mailgun
6
+ end
7
+
8
+ def list(limit=100, skip=0)
9
+ Mailgun.submit(:get, route_url, :limit => limit, :skip => skip)["items"] || []
10
+ end
11
+
12
+ def find(route_id)
13
+ Mailgun.submit(:get, route_url(route_id))["route"]
14
+ end
15
+
16
+ def create(description, priority, filter, actions)
17
+ data = ::Multimap.new
18
+
19
+ data['priority'] = priority
20
+ data['description'] = description
21
+ data['expression'] = build_filter(filter)
22
+
23
+ actions = build_actions(actions)
24
+
25
+ actions.each do |action|
26
+ data['action'] = action
27
+ end
28
+
29
+ # TODO: Raise an error or return false if unable to create route
30
+ Mailgun.submit(:post, route_url, data)["route"]["id"]
31
+ end
32
+
33
+ def update(route_id, params)
34
+ data = ::Multimap.new
35
+
36
+ ['priority', 'description'].each do |key|
37
+ data[key] = params[key] if params.has_key?(key)
38
+ end
39
+
40
+ data['expression'] = build_filter(params['expression']) if params.has_key?('expression')
41
+
42
+ if params.has_key?('actions')
43
+ actions = build_actions(params['actions'])
44
+
45
+ actions.each do |action|
46
+ data['action'] = action
47
+ end
48
+ end
49
+
50
+ Mailgun.submit(:put, route_url(route_id), data)
51
+ end
52
+
53
+ def destroy(route_id)
54
+ Mailgun.submit(:delete, route_url(route_id))["id"]
55
+ end
56
+
57
+ private
58
+
59
+ def route_url(route_id=nil)
60
+ "#{@mailgun.base_url}/routes#{'/' + route_id if route_id}"
61
+ end
62
+
63
+ def build_actions(actions)
64
+ _actions = []
65
+
66
+ actions.each do |action|
67
+ case action.first.to_sym
68
+ when :forward
69
+ _actions << "forward(\"#{action.last}\")"
70
+ when :stop
71
+ _actions << "stop()"
72
+ else
73
+ raise Mailgun::Error.new("Unsupported action requested, see http://documentation.mailgun.net/user_manual.html#routes for a list of allowed actions")
74
+ end
75
+ end
76
+
77
+ _actions
78
+ end
79
+
80
+
81
+ def build_filter(filter)
82
+ case filter.first.to_sym
83
+ when :match_recipient
84
+ return "match_recipient('#{filter.last}')"
85
+ when :match_header
86
+ return "match_header('#{filter[1]}', '#{filter.last}')"
87
+ when :catch_all
88
+ return "catch_all()"
89
+ else
90
+ raise Mailgun::Error.new("Unsupported filter requested, see http://documentation.mailgun.net/user_manual.html#routes for a list of allowed filters")
91
+ end
92
+ end
93
+ end
94
+ end