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,62 @@
1
+ require 'ostruct'
2
+
3
+ module Mailgun
4
+ # A Mailgun::Response object is instantiated for each response generated
5
+ # by the Client request. The Response object supports deserialization of
6
+ # the JSON result. Or, if you prefer JSON or YAML formatting, call the
7
+ # method for conversion.
8
+ #
9
+ # See the Github documentation for full examples.
10
+ class Response
11
+ # All responses have a payload and a code corresponding to http, though
12
+ # slightly different
13
+ attr_accessor :body, :code
14
+
15
+ def self.from_hash(h)
16
+ # Create a "fake" response object with the data passed from h
17
+ self.new OpenStruct.new(h)
18
+ end
19
+
20
+ def initialize(response)
21
+ @body = response.body
22
+ @code = response.code
23
+ end
24
+
25
+ # Return response as Ruby Hash
26
+ #
27
+ # @return [Hash] A standard Ruby Hash containing the HTTP result.
28
+
29
+ def to_h
30
+ JSON.parse(@body)
31
+ rescue => err
32
+ raise ParseError.new(err), err
33
+ end
34
+
35
+ # Replace @body with Ruby Hash
36
+ #
37
+ # @return [Hash] A standard Ruby Hash containing the HTTP result.
38
+ def to_h!
39
+ @body = JSON.parse(@body)
40
+ rescue => err
41
+ raise ParseError.new(err), err
42
+ end
43
+
44
+ # Return response as Yaml
45
+ #
46
+ # @return [String] A string containing response as YAML
47
+ def to_yaml
48
+ YAML.dump(to_h)
49
+ rescue => err
50
+ raise ParseError.new(err), err
51
+ end
52
+
53
+ # Replace @body with YAML
54
+ #
55
+ # @return [String] A string containing response as YAML
56
+ def to_yaml!
57
+ @body = YAML.dump(to_h)
58
+ rescue => err
59
+ raise ParseError.new(err), err
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,270 @@
1
+ require 'uri'
2
+
3
+ require 'mailgun/exceptions/exceptions'
4
+
5
+ module Mailgun
6
+
7
+ # The Mailgun::Suppressions object makes it easy to manage "suppressions"
8
+ # attached to an account. "Suppressions" means bounces, unsubscribes, and complaints.
9
+ class Suppressions
10
+
11
+ # @param [Mailgun::Client] client API client to use for requests
12
+ # @param [String] domain Domain name to use for the suppression endpoints.
13
+ def initialize(client, domain)
14
+ @client = client
15
+ @domain = domain
16
+
17
+ @paging_next = nil
18
+ @paging_prev = nil
19
+ end
20
+
21
+ ####
22
+ # Paging operations
23
+ ####
24
+
25
+ def next
26
+ response = get_from_paging @paging_next[:path], @paging_next[:params]
27
+ extract_paging response
28
+ response
29
+ end
30
+
31
+ def prev
32
+ response = get_from_paging @paging_prev[:path], @paging_prev[:params]
33
+ extract_paging response
34
+ response
35
+ end
36
+
37
+ ####
38
+ # Bounces Endpoint (/v3/:domain/bounces)
39
+ ####
40
+
41
+ def list_bounces(params = {})
42
+ response = @client.get("#{@domain}/bounces", params)
43
+ extract_paging response
44
+ response
45
+ end
46
+
47
+ def get_bounce(address)
48
+ @client.get("#{@domain}/bounces/#{address}", nil)
49
+ end
50
+
51
+ def create_bounce(params = {})
52
+ @client.post("#{@domain/bounces}", params)
53
+ end
54
+
55
+ # Creates multiple bounces on the Mailgun API.
56
+ # If a bounce does not have a valid structure, it will be added to a list of unsendable bounces.
57
+ # The list of unsendable bounces will be returned at the end of this operation.
58
+ #
59
+ # If more than 999 bounce entries are provided, the list will be split and recursive calls will be made.
60
+ #
61
+ # @param [Array] data Array of bounce hashes
62
+ # @return [Response] Mailgun API response
63
+ # @return [Array] Return values from recursive call for list split.
64
+ def create_bounces(data)
65
+ # `data` should be a list of hashes, with each hash containing *at least* an `address` key.
66
+ split_return = []
67
+ if data.length >= 1000 then
68
+ resp, resp_l = create_bounces data[999..-1]
69
+ split_return.push(resp)
70
+ split_return.concat(resp_l)
71
+ data = data[0..998]
72
+ elsif data.length == 0 then
73
+ return nil, []
74
+ end
75
+
76
+ valid = []
77
+ # Validate the bounces given
78
+ # NOTE: `data` could potentially be very large (1000 elements) so it is
79
+ # more efficient to pop from data and push into a different array as
80
+ # opposed to possibly copying the entire array to another array.
81
+ while not data.empty? do
82
+ bounce = data.pop
83
+ # Bounces MUST contain a `address` key.
84
+ if not bounce.include? :address then
85
+ raise Mailgun::ParameterError.new "Bounce MUST include a :address key: #{bounce}"
86
+ end
87
+
88
+ bounce.each do |k, v|
89
+ # Hash values MUST be strings.
90
+ if not v.is_a? String then
91
+ bounce[k] = v.to_s
92
+ end
93
+ end
94
+
95
+ valid.push bounce
96
+ end
97
+
98
+ response = @client.post("#{@domain}/bounces", valid.to_json, { "Content-Type" => "application/json" })
99
+ return response, split_return
100
+ end
101
+
102
+ def delete_bounce(address)
103
+ @client.delete("#{@domain}/bounces/#{address}")
104
+ end
105
+
106
+ def delete_all_bounces
107
+ @client.delete("#{@domain}/bounces")
108
+ end
109
+
110
+ ####
111
+ # Unsubscribes Endpoint (/v3/:domain/unsubscribes)
112
+ ####
113
+
114
+ def list_unsubscribes(params = {})
115
+ response = @client.get("#{@domain}/unsubscribes", params)
116
+ extract_paging response
117
+ response
118
+ end
119
+
120
+ def get_unsubscribe(address)
121
+ @client.get("#{@domain}/unsubscribes/#{address}")
122
+ end
123
+
124
+ def create_unsubscribe(params = {})
125
+ @client.post("#{@domain}/unsubscribes", params)
126
+ end
127
+
128
+ # Creates multiple unsubscribes on the Mailgun API.
129
+ # If an unsubscribe does not have a valid structure, it will be added to a list of unsendable unsubscribes.
130
+ # The list of unsendable unsubscribes will be returned at the end of this operation.
131
+ #
132
+ # If more than 999 unsubscribe entries are provided, the list will be split and recursive calls will be made.
133
+ #
134
+ # @param [Array] data Array of unsubscribe hashes
135
+ # @return [Response] Mailgun API response
136
+ # @return [Array] Return values from recursive call for list split.
137
+ def create_unsubscribes(data)
138
+ # `data` should be a list of hashes, with each hash containing *at least* an `address` key.
139
+ split_return = []
140
+ if data.length >= 1000 then
141
+ resp, resp_l = create_unsubscribes data[999..-1]
142
+ split_return.push(resp)
143
+ split_return.concat(resp_l)
144
+ data = data[0..998]
145
+ elsif data.length == 0 then
146
+ return nil, []
147
+ end
148
+
149
+ valid = []
150
+ # Validate the unsubscribes given
151
+ while not data.empty? do
152
+ unsubscribe = data.pop
153
+ # unsubscribes MUST contain a `address` key.
154
+ if not unsubscribe.include? :address then
155
+ raise Mailgun::ParameterError.new "Unsubscribe MUST include a :address key: #{unsubscribe}"
156
+ end
157
+
158
+ unsubscribe.each do |k, v|
159
+ # Hash values MUST be strings.
160
+ if not v.is_a? String then
161
+ unsubscribe[k] = v.to_s
162
+ end
163
+ end
164
+
165
+ valid.push unsubscribe
166
+ end
167
+
168
+ response = @client.post("#{@domain}/unsubscribes", valid.to_json, { "Content-Type" => "application/json" })
169
+ return response, split_return
170
+ end
171
+
172
+ def delete_unsubscribe(address, params = {})
173
+ @client.delete("#{@domain}/unsubscribes/#{address}")
174
+ end
175
+
176
+ ####
177
+ # Complaints Endpoint (/v3/:domain/complaints)
178
+ ####
179
+
180
+ def list_complaints(params = {})
181
+ response = @client.get("#{@domain}/complaints", params)
182
+ extract_paging response
183
+ response
184
+ end
185
+
186
+ def get_complaint(address)
187
+ @client.get("#{@domain}/complaints/#{address}", nil)
188
+ end
189
+
190
+ def create_complaint(params = {})
191
+ @client.post("#{@domain}/complaints", params)
192
+ end
193
+
194
+ # Creates multiple complaints on the Mailgun API.
195
+ # If a complaint does not have a valid structure, it will be added to a list of unsendable complaints.
196
+ # The list of unsendable complaints will be returned at the end of this operation.
197
+ #
198
+ # If more than 999 complaint entries are provided, the list will be split and recursive calls will be made.
199
+ #
200
+ # @param [Array] data Array of complaint hashes
201
+ # @return [Response] Mailgun API response
202
+ # @return [Array] Return values from recursive call for list split.
203
+ def create_complaints(data)
204
+ # `data` should be a list of hashes, with each hash containing *at least* an `address` key.
205
+ split_return = []
206
+ if data.length >= 1000 then
207
+ resp, resp_l = create_complaints data[999..-1]
208
+ split_return.push(resp)
209
+ split_return.concat(resp_l)
210
+ data = data[0..998]
211
+ elsif data.length == 0 then
212
+ return nil, []
213
+ end
214
+
215
+ valid = []
216
+ # Validate the complaints given
217
+ while not data.empty? do
218
+ complaint = data.pop
219
+ # complaints MUST contain a `address` key.
220
+ if not complaint.include? :address then
221
+ raise Mailgun::ParameterError.new "Complaint MUST include a :address key: #{complaint}"
222
+ end
223
+
224
+ complaint.each do |k, v|
225
+ # Hash values MUST be strings.
226
+ if not v.is_a? String then
227
+ complaint[k] = v.to_s
228
+ end
229
+ end
230
+
231
+ valid.push complaint
232
+ end
233
+
234
+ response = @client.post("#{@domain}/complaints", valid.to_json, { "Content-Type" => "application/json" })
235
+ return response, split_return
236
+ end
237
+
238
+ def delete_complaint(address)
239
+ @client.delete("#{@domain}/complaints/#{address}")
240
+ end
241
+
242
+ private
243
+
244
+ def get_from_paging(uri, params = {})
245
+ @client.get(uri, params)
246
+ end
247
+
248
+ def extract_paging(response)
249
+ rhash = response.to_h
250
+ return nil unless rhash.include? "paging"
251
+
252
+ page_info = rhash["paging"]
253
+
254
+ # Build the `next` endpoint
255
+ page_next = URI.parse(page_info["next"])
256
+ @paging_next = {
257
+ :path => page_next.path[/\/v[\d](.+)/, 1],
258
+ :params => Hash[URI.decode_www_form page_next.query],
259
+ }
260
+
261
+ # Build the `prev` endpoint
262
+ page_prev = URI.parse(page_info["previous"])
263
+ @paging_prev = {
264
+ :path => page_prev.path[/\/v[\d](.+)/, 1],
265
+ :params => Hash[URI.decode_www_form page_prev.query],
266
+ }
267
+ end
268
+
269
+ end
270
+ end
@@ -0,0 +1,4 @@
1
+ # It's the version. Yeay!
2
+ module Mailgun
3
+ VERSION = '1.1.7'
4
+ end
@@ -0,0 +1,101 @@
1
+ module Mailgun
2
+
3
+ # A Mailgun::Webhooks object is a simple CRUD interface to Mailgun Webhooks.
4
+ # Uses Mailgun
5
+ class Webhooks
6
+
7
+ # Public creates a new Mailgun::Webhooks instance.
8
+ # Defaults to Mailgun::Client
9
+ def initialize(client = Mailgun::Client.new)
10
+ @client = client
11
+ end
12
+
13
+ # Public: Get Webhooks
14
+ #
15
+ # domain - a string the domain name to retrieve webhooks for
16
+ # options - a Hash of options
17
+ #
18
+ # Returns a Hash of the list of domains or nil
19
+ def list(domain, options = {})
20
+ res = @client.get("domains/#{domain}/webhooks", options)
21
+ res.to_h['webhooks']
22
+ end
23
+ alias_method :get_webhooks, :list
24
+
25
+ # Public: Get webook information for a specific action
26
+ #
27
+ # domain - a String of Domain name to find a webhook url for
28
+ # action - a String identifying the webhook to get the URL for
29
+ #
30
+ # Returns a String of the url for the identified webhook or an
31
+ # empty String if one is not set
32
+ def info(domain, action)
33
+ res = @client.get("domains/#{domain}/webhooks/#{action}")
34
+ res.to_h['webhook']['url'] || ''
35
+ rescue NoMethodError
36
+ ''
37
+ end
38
+ alias_method :get_webhook_url, :info
39
+
40
+ # Public: Add webhook
41
+ #
42
+ # domain - A String of the domain name (ex. domain.com)
43
+ # action - A String of the action to create a webhook for
44
+ # url - A String of the url of the webhook
45
+ #
46
+ # Returns a Boolean of whether the webhook was created
47
+ def create(domain, action, url = '')
48
+ res = @client.post("domains/#{domain}/webhooks", id: action, url: url)
49
+ res.to_h['webhook']['url'] == url && res.to_h[message] == 'Webhook has been created'
50
+ end
51
+ alias_method :add, :create
52
+ alias_method :add_webhook, :create
53
+
54
+ # Public: Sets all webhooks to the same URL
55
+ #
56
+ # domain - A String of the domain name
57
+ # url - A String of the url to set all webhooks to
58
+ #
59
+ # Returns true or false
60
+ def create_all(domain, url = '')
61
+ %w(bounce click deliver drop open spam unsubscribe).each do |action|
62
+ add_webhook domain, action, url
63
+ end
64
+ true
65
+ rescue
66
+ false
67
+ end
68
+ alias_method :add_all_webhooks, :create_all
69
+
70
+ # Public: Delete a specific webhook
71
+ #
72
+ # domain - The required String of domain name
73
+ # action - The required String of the webhook action to delete
74
+ #
75
+ # Returns a Boolean of the success
76
+ def remove(domain, action)
77
+ fail Mailgun::ParameterError('Domain not provided to remove webhook from') unless domain
78
+ fail Mailgun::ParameterError('Action not provided to identify webhook to remove') unless action
79
+ @client.delete("domains/#{domain}/webhooks/#{action}").to_h['message'] == 'Webhook has been deleted'
80
+ rescue Mailgun::CommunicationError
81
+ false
82
+ end
83
+ alias_method :delete, :remove
84
+ alias_method :delete_webhook, :remove
85
+
86
+ # Public: Delete all webhooks for a domain
87
+ #
88
+ # domain - A required String of the domain to remove all webhooks for
89
+ #
90
+ # Returns a Boolean on the success
91
+ def remove_all(domain)
92
+ fail Mailgun::ParameterError('Domain not provided to remove webhooks from') unless domain
93
+ %w(bounce click deliver drop open spam unsubscribe).each do |action|
94
+ delete_webhook domain, action
95
+ end
96
+ end
97
+ alias_method :delete_all, :remove_all
98
+ alias_method :delete_all_webooks, :remove_all
99
+
100
+ end
101
+ end
@@ -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