mailgun-ruby 1.4.3 → 1.4.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.
@@ -6,55 +6,74 @@ require 'mailgun'
6
6
  vcr_opts = { cassette_name: 'webhooks' }
7
7
 
8
8
  describe 'For the webhooks endpoint', order: :defined, vcr: vcr_opts do
9
- before(:all) do
10
- @mg_obj = Mailgun::Client.new(APIKEY, APIHOST, APIVERSION, SSL)
11
- @domain = 'DOMAIN.TEST'
12
- @testhook = 'accepted'
13
- @testhookup = 'accepted'
14
- end
9
+ let(:api_version) { 'v3' }
10
+ let(:mg_client) { Mailgun::Client.new(APIKEY, APIHOST, api_version, SSL) }
11
+ let(:mg_obj) { Mailgun::Webhooks.new(mg_client) }
12
+ let(:domain) { 'DOMAIN.TEST' }
13
+ let(:testhook) { 'accepted' }
14
+ let(:testhookup) { 'accepted' }
15
15
 
16
16
  it 'creates a webhook' do
17
- result = @mg_obj.post("domains/#{@domain}/webhooks", { id: @testhook,
18
- url: "http://example.com/mailgun/events/#{@testhook}" })
17
+ result = mg_obj.create(domain, testhook, "http://example.com/mailgun/events/#{testhook}")
19
18
 
20
- result.to_h!
21
- expect(result.body['message']).to eq('Webhook has been created')
22
- expect(result.body['webhook']['urls']).to include("http://example.com/mailgun/events/#{@testhook}")
19
+ expect(result).to be_truthy
23
20
  end
24
21
 
25
- it 'gets a webhook.' do
26
- result = @mg_obj.get("domains/#{@domain}/webhooks/#{@testhook}")
22
+ it 'gets a webhook' do
23
+ result = mg_obj.get(domain, testhook)
27
24
 
28
- result.to_h!
29
- expect(result.body['webhook']['urls']).to include("http://example.com/mailgun/events/#{@testhook}")
25
+ expect(result).to include("http://example.com/mailgun/events/#{testhook}")
30
26
  end
31
27
 
32
- it 'gets a list of all webhooks.' do
33
- result = @mg_obj.get("domains/#{@domain}/webhooks")
28
+ it 'gets a list of all webhooks' do
29
+ result = mg_obj.list(domain)
34
30
 
35
- result.to_h!
36
- expect(result.body['webhooks']['accepted']['urls']).to include("http://example.com/mailgun/events/#{@testhook}")
31
+ expect(result['accepted']['urls'][0]).to include("http://example.com/mailgun/events/#{testhook}")
37
32
  end
38
33
 
39
- it 'updates a webhook.' do
40
- result = @mg_obj.put(
41
- "domains/#{@domain}/webhooks/#{@testhook}",
42
- {
43
- id: @testhook,
44
- url: "http://example.com/mailgun/events/#{@testhookup}"
45
- }
46
- )
34
+ it 'updates a webhook' do
35
+ result = mg_obj.update(domain, testhook, "http://example.com/mailgun/events/#{testhookup}")
47
36
 
48
- result.to_h!
49
- expect(result.body['message']).to eq('Webhook has been updated')
50
- expect(result.body['webhook']['urls']).to include("http://example.com/mailgun/events/#{@testhookup}")
37
+ expect(result).to be_truthy
51
38
  end
52
39
 
53
40
  it 'removes a webhook' do
54
- result = @mg_obj.delete("domains/#{@domain}/webhooks/#{@testhook}")
41
+ result = mg_obj.remove(domain, testhook)
42
+
43
+ expect(result).to be_truthy
44
+ end
45
+ end
46
+
47
+ describe 'For the v4 webhooks endpoint', vcr: { cassette_name: 'webhooks_v4' } do
48
+ let(:mg_client) { Mailgun::Client.new(APIKEY, APIHOST, 'v4', SSL) }
49
+ let(:mg_obj) { Mailgun::Webhooks.new(mg_client) }
50
+ let(:domain) { 'DOMAIN.TEST' }
51
+ let(:url) { 'http://example.com/mailgun/events/v4' }
52
+
53
+ describe '#create_v4' do
54
+ it 'creates webhooks for multiple event types' do
55
+ result = mg_obj.create_v4(domain, url: url, event_types: %w[accepted delivered])
56
+
57
+ expect(result.keys).to contain_exactly('accepted', 'delivered')
58
+ expect(result['accepted']['urls']).to include(url)
59
+ expect(result['delivered']['urls']).to include(url)
60
+ end
61
+ end
62
+
63
+ describe '#update_v4' do
64
+ it 'replaces the event types associated with a url' do
65
+ result = mg_obj.update_v4(domain, url: url, event_types: %w[clicked opened])
66
+
67
+ expect(result.keys).to contain_exactly('clicked', 'opened')
68
+ expect(result['clicked']['urls']).to include(url)
69
+ end
70
+ end
71
+
72
+ describe '#remove_v4' do
73
+ it 'removes the urls from all event types' do
74
+ result = mg_obj.remove_v4(domain, url: [url, 'http://example.com/mailgun/events/v4-other'])
55
75
 
56
- result.to_h!
57
- expect(result.body['message']).to eq('Webhook has been deleted')
58
- expect(result.body['webhook']['urls']).to include("http://example.com/mailgun/events/#{@testhookup}")
76
+ expect(result).to eq({})
77
+ end
59
78
  end
60
79
  end
data/spec/spec_helper.rb CHANGED
@@ -17,6 +17,8 @@ require 'mailgun'
17
17
 
18
18
  require 'vcr'
19
19
  require 'webmock/rspec'
20
+ # Keep repeated query keys (e.g. ?url=a&url=b) instead of collapsing them to the last value
21
+ WebMock::Config.instance.query_values_notation = :flat_array
20
22
  require 'rspec/its'
21
23
 
22
24
  # WebMock.disable_net_connect!(allow_localhost: true)
@@ -43,6 +45,7 @@ VCR.configure do |c|
43
45
  c.hook_into :webmock
44
46
  c.default_cassette_options = { record: :new_episodes }
45
47
  c.filter_sensitive_data('<APIKEY>') { APIKEY }
48
+ c.filter_sensitive_data('<BASIC_AUTH>') { ["api:#{APIKEY}"].pack('m0') } if APIKEY
46
49
  c.filter_sensitive_data('DOMAIN.TEST') { TESTDOMAIN }
47
50
  c.filter_sensitive_data('<PUBKEY>') { PUB_APIKEY }
48
51
 
@@ -54,6 +54,30 @@ describe Mailgun::Client do
54
54
  it 'supports EU api host' do
55
55
  expect { described_class.new(api_key, 'api.eu.mailgun.net') }.not_to raise_error
56
56
  end
57
+
58
+ context 'when test_mode is not passed' do
59
+ around do |example|
60
+ original = Mailgun.test_mode
61
+ example.run
62
+ ensure
63
+ Mailgun.test_mode = original
64
+ end
65
+
66
+ it 'enables test mode when Mailgun.test_mode is true' do
67
+ Mailgun.test_mode = true
68
+ expect(described_class.new(api_key).test_mode?).to be(true)
69
+ end
70
+
71
+ it 'disables test mode when Mailgun.test_mode is false' do
72
+ Mailgun.test_mode = false
73
+ expect(described_class.new(api_key).test_mode?).to be(false)
74
+ end
75
+
76
+ it 'disables test mode when Mailgun.test_mode is not set' do
77
+ Mailgun.test_mode = nil
78
+ expect(described_class.new(api_key).test_mode?).to be(false)
79
+ end
80
+ end
57
81
  end
58
82
 
59
83
  # ---------------------------------------------------------------------------
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Mailgun::Webhooks do
6
+ let(:client) { double(:client) }
7
+ let(:webhooks) { described_class.new(client) }
8
+ let(:domain) { 'example.com' }
9
+ let(:url) { 'https://example.com/mailgun/events' }
10
+
11
+ %i[create_all add_all_webhooks].each do |method|
12
+ describe "##{method}" do
13
+ it 'creates every webhook action with the provided URL' do
14
+ described_class::ACTIONS.each do |action|
15
+ expect(webhooks).to receive(:create).with(domain, action, url).ordered
16
+ end
17
+
18
+ expect { webhooks.public_send(method, domain, url) }
19
+ .to output(/`#{method}` method will be deprecated/).to_stderr
20
+ end
21
+ end
22
+ end
23
+
24
+ context 'with v3 endpoints' do
25
+ let(:client) { instance_double(Mailgun::Client, api_version: 'v3') }
26
+
27
+ describe '#create' do
28
+ let(:urls) { [url, 'https://example.com/other'] }
29
+ let(:response) do
30
+ instance_double(Mailgun::Response,
31
+ to_h: { 'message' => 'Webhook has been created', 'webhook' => { 'urls' => urls } })
32
+ end
33
+
34
+ before { allow(client).to receive(:post).and_return(response) }
35
+
36
+ it 'returns true when a single url was created' do
37
+ expect(webhooks.create(domain, 'delivered', url)).to be(true)
38
+ end
39
+
40
+ it 'returns true when multiple urls were created' do
41
+ expect(webhooks.create(domain, 'delivered', urls)).to be(true)
42
+ end
43
+
44
+ it 'returns false when a url is missing from the response' do
45
+ expect(webhooks.create(domain, 'delivered', [url, 'https://example.com/missing'])).to be(false)
46
+ end
47
+ end
48
+
49
+ describe 'parameter validation' do
50
+ it 'raises a ParameterError when the domain is missing' do
51
+ [nil, ''].each do |blank|
52
+ expect { webhooks.update(blank, 'delivered', url) }
53
+ .to raise_error(Mailgun::ParameterError, 'Domain not provided to update webhooks')
54
+ expect { webhooks.remove(blank, 'delivered') }
55
+ .to raise_error(Mailgun::ParameterError, 'Domain not provided to remove webhook from')
56
+ end
57
+ end
58
+
59
+ it 'raises a ParameterError when the action is missing' do
60
+ [nil, ''].each do |blank|
61
+ expect { webhooks.update(domain, blank, url) }
62
+ .to raise_error(Mailgun::ParameterError, 'Action not provided to identify webhook to update')
63
+ expect { webhooks.remove(domain, blank) }
64
+ .to raise_error(Mailgun::ParameterError, 'Action not provided to identify webhook to remove')
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ context 'with v4 endpoints' do
71
+ let(:api_version) { 'v4' }
72
+ let(:client) { instance_double(Mailgun::Client, api_version: api_version) }
73
+ let(:response) { instance_double(Mailgun::Response, to_h: { 'webhooks' => { 'accepted' => { 'urls' => [url] } } }) }
74
+
75
+ describe '#create_v4' do
76
+ it 'posts the url and event types to the domain webhooks endpoint' do
77
+ expect(client).to receive(:post)
78
+ .with("domains/#{domain}/webhooks", { url: url, event_types: %w[accepted delivered] })
79
+ .and_return(response)
80
+
81
+ expect(webhooks.create_v4(domain, url: url, event_types: %w[accepted delivered]))
82
+ .to eq('accepted' => { 'urls' => [url] })
83
+ end
84
+ end
85
+
86
+ describe '#update_v4' do
87
+ it 'puts the url and event types to the domain webhooks endpoint' do
88
+ expect(client).to receive(:put)
89
+ .with("domains/#{domain}/webhooks", { url: url, event_types: 'accepted' })
90
+ .and_return(response)
91
+
92
+ expect(webhooks.update_v4(domain, url: url, event_types: 'accepted'))
93
+ .to eq('accepted' => { 'urls' => [url] })
94
+ end
95
+ end
96
+
97
+ describe '#remove_v4' do
98
+ it 'sends the urls as query params' do
99
+ urls = [url, 'https://example.com/other']
100
+ expect(client).to receive(:delete)
101
+ .with("domains/#{domain}/webhooks", { url: urls })
102
+ .and_return(response)
103
+
104
+ webhooks.remove_v4(domain, url: urls)
105
+ end
106
+ end
107
+
108
+ context 'when the client api version is not v4' do
109
+ let(:api_version) { 'v3' }
110
+
111
+ it 'raises a ParameterError' do
112
+ expect { webhooks.create_v4(domain, url: url, event_types: 'accepted') }
113
+ .to raise_error(Mailgun::ParameterError, /must be v4/)
114
+ expect { webhooks.update_v4(domain, url: url, event_types: 'accepted') }
115
+ .to raise_error(Mailgun::ParameterError, /must be v4/)
116
+ expect { webhooks.remove_v4(domain, url: url) }
117
+ .to raise_error(Mailgun::ParameterError, /must be v4/)
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,269 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://api.mailgun.net/v1/webhooks
6
+ body:
7
+ encoding: UTF-8
8
+ string: description=test&event_types=accepted&url=http%3A%2F%2Fexample.com%2Fmailgun%2Fevents
9
+ headers:
10
+ User-Agent:
11
+ - mailgun-sdk-ruby/1.4.4
12
+ Accept:
13
+ - "*/*"
14
+ Authorization:
15
+ - Basic xxx
16
+ Content-Type:
17
+ - application/x-www-form-urlencoded
18
+ Accept-Encoding:
19
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
20
+ response:
21
+ status:
22
+ code: 200
23
+ message: OK
24
+ headers:
25
+ Access-Control-Allow-Credentials:
26
+ - 'true'
27
+ Access-Control-Allow-Origin:
28
+ - "*"
29
+ Cache-Control:
30
+ - no-store
31
+ Content-Length:
32
+ - '42'
33
+ Content-Type:
34
+ - application/json; charset=utf-8
35
+ Date:
36
+ - Mon, 25 May 2026 09:11:02 GMT
37
+ Strict-Transport-Security:
38
+ - max-age=63072000; includeSubDomains
39
+ X-Mailgun-Key-Id:
40
+ - xxx
41
+ X-Xss-Protection:
42
+ - 1; mode=block
43
+ Server-Timing:
44
+ - cfReqDur;dur=382.358
45
+ Cf-Team:
46
+ - xxx
47
+ body:
48
+ encoding: UTF-8
49
+ string: '{"webhook_id":"test"}
50
+
51
+ '
52
+ recorded_at: Mon, 25 May 2026 09:11:02 GMT
53
+ - request:
54
+ method: get
55
+ uri: https://api.mailgun.net/v1/webhooks?webhook_ids=
56
+ body:
57
+ encoding: US-ASCII
58
+ string: ''
59
+ headers:
60
+ User-Agent:
61
+ - mailgun-sdk-ruby/1.4.4
62
+ Accept:
63
+ - "*/*"
64
+ Authorization:
65
+ - Basic xxx
66
+ Accept-Encoding:
67
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
68
+ response:
69
+ status:
70
+ code: 200
71
+ message: OK
72
+ headers:
73
+ Access-Control-Allow-Credentials:
74
+ - 'true'
75
+ Access-Control-Allow-Origin:
76
+ - "*"
77
+ Cache-Control:
78
+ - no-store
79
+ Content-Length:
80
+ - '183'
81
+ Content-Type:
82
+ - application/json; charset=utf-8
83
+ Date:
84
+ - Mon, 25 May 2026 09:16:27 GMT
85
+ Strict-Transport-Security:
86
+ - max-age=63072000; includeSubDomains
87
+ X-Mailgun-Key-Id:
88
+ - xxx
89
+ X-Xss-Protection:
90
+ - 1; mode=block
91
+ Server-Timing:
92
+ - cfReqDur;dur=201.477
93
+ Cf-Team:
94
+ - xxx
95
+ body:
96
+ encoding: UTF-8
97
+ string: '{"webhooks":[{"webhook_id":"test","description":"test","url":"http://example.com/mailgun/events","event_types":["accepted"],"created_at":"2026-05-25T09:11:02Z"}]}
98
+
99
+ '
100
+ recorded_at: Mon, 25 May 2026 09:16:27 GMT
101
+ - request:
102
+ method: get
103
+ uri: https://api.mailgun.net/v1/webhooks/test
104
+ body:
105
+ encoding: US-ASCII
106
+ string: ''
107
+ headers:
108
+ User-Agent:
109
+ - mailgun-sdk-ruby/1.4.4
110
+ Accept:
111
+ - "*/*"
112
+ Authorization:
113
+ - Basic xxx
114
+ Accept-Encoding:
115
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
116
+ response:
117
+ status:
118
+ code: 200
119
+ message: OK
120
+ headers:
121
+ Access-Control-Allow-Credentials:
122
+ - 'true'
123
+ Access-Control-Allow-Origin:
124
+ - "*"
125
+ Cache-Control:
126
+ - no-store
127
+ Content-Length:
128
+ - '168'
129
+ Content-Type:
130
+ - application/json; charset=utf-8
131
+ Date:
132
+ - Mon, 25 May 2026 09:22:12 GMT
133
+ Strict-Transport-Security:
134
+ - max-age=63072000; includeSubDomains
135
+ X-Mailgun-Key-Id:
136
+ - xxx
137
+ X-Xss-Protection:
138
+ - 1; mode=block
139
+ Server-Timing:
140
+ - cfReqDur;dur=234.628
141
+ Cf-Team:
142
+ - xxx
143
+ body:
144
+ encoding: UTF-8
145
+ string: '{"webhook_id":"test","description":"test","url":"http://example.com/mailgun/events","event_types":["accepted"],"created_at":"2026-05-25T09:11:02Z"}
146
+
147
+ '
148
+ recorded_at: Mon, 25 May 2026 09:22:12 GMT
149
+ - request:
150
+ method: put
151
+ uri: https://api.mailgun.net/v1/webhooks/test
152
+ body:
153
+ encoding: UTF-8
154
+ string: description=test2&event_types=accepted&url=http%3A%2F%2Fexample.com%2Fmailgun%2Fevents
155
+ headers:
156
+ User-Agent:
157
+ - mailgun-sdk-ruby/1.4.4
158
+ Accept:
159
+ - "*/*"
160
+ Authorization:
161
+ - Basic xxx
162
+ Content-Type:
163
+ - application/x-www-form-urlencoded
164
+ Accept-Encoding:
165
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
166
+ response:
167
+ status:
168
+ code: 204
169
+ message: No Content
170
+ headers:
171
+ Access-Control-Allow-Credentials:
172
+ - 'true'
173
+ Access-Control-Allow-Origin:
174
+ - "*"
175
+ Cache-Control:
176
+ - no-store
177
+ Date:
178
+ - Mon, 25 May 2026 12:03:17 GMT
179
+ Strict-Transport-Security:
180
+ - max-age=63072000; includeSubDomains
181
+ X-Mailgun-Key-Id:
182
+ - xxx
183
+ X-Xss-Protection:
184
+ - 1; mode=block
185
+ body:
186
+ encoding: UTF-8
187
+ string: ''
188
+ recorded_at: Mon, 25 May 2026 12:03:17 GMT
189
+ - request:
190
+ method: delete
191
+ uri: https://api.mailgun.net/v1/webhooks/test
192
+ body:
193
+ encoding: US-ASCII
194
+ string: ''
195
+ headers:
196
+ User-Agent:
197
+ - mailgun-sdk-ruby/1.4.4
198
+ Accept:
199
+ - "*/*"
200
+ Authorization:
201
+ - Basic xxx
202
+ Accept-Encoding:
203
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
204
+ response:
205
+ status:
206
+ code: 204
207
+ message: No Content
208
+ headers:
209
+ Access-Control-Allow-Credentials:
210
+ - 'true'
211
+ Access-Control-Allow-Origin:
212
+ - "*"
213
+ Cache-Control:
214
+ - no-store
215
+ Date:
216
+ - Mon, 25 May 2026 12:08:15 GMT
217
+ Strict-Transport-Security:
218
+ - max-age=63072000; includeSubDomains
219
+ X-Mailgun-Key-Id:
220
+ - xxx
221
+ X-Xss-Protection:
222
+ - 1; mode=block
223
+ body:
224
+ encoding: UTF-8
225
+ string: ''
226
+ recorded_at: Mon, 25 May 2026 12:08:15 GMT
227
+ - request:
228
+ method: delete
229
+ uri: https://api.mailgun.net/v1/webhooks?all=true
230
+ body:
231
+ encoding: US-ASCII
232
+ string: ''
233
+ headers:
234
+ User-Agent:
235
+ - mailgun-sdk-ruby/1.4.4
236
+ Accept:
237
+ - "*/*"
238
+ Authorization:
239
+ - Basic xxx
240
+ Accept-Encoding:
241
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
242
+ response:
243
+ status:
244
+ code: 204
245
+ message: No Content
246
+ headers:
247
+ Access-Control-Allow-Credentials:
248
+ - 'true'
249
+ Access-Control-Allow-Origin:
250
+ - "*"
251
+ Cache-Control:
252
+ - no-store
253
+ Date:
254
+ - Mon, 25 May 2026 20:28:16 GMT
255
+ Strict-Transport-Security:
256
+ - max-age=63072000; includeSubDomains
257
+ X-Mailgun-Key-Id:
258
+ - xxx
259
+ X-Xss-Protection:
260
+ - 1; mode=block
261
+ Server-Timing:
262
+ - cfReqDur;dur=246.503
263
+ Cf-Team:
264
+ - xxx
265
+ body:
266
+ encoding: UTF-8
267
+ string: ''
268
+ recorded_at: Mon, 25 May 2026 20:28:16 GMT
269
+ recorded_with: VCR 6.4.0