mailgun-ruby 1.4.1 → 1.4.3
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 +4 -4
- data/.github/workflows/ci.yml +30 -8
- data/.rubocop.yml +64 -4
- data/Gemfile +3 -1
- data/README.md +1 -1
- data/Rakefile +5 -8
- data/docs/AnalyticsTags.md +63 -0
- data/lib/mailgun/address.rb +5 -5
- data/lib/mailgun/chains.rb +2 -3
- data/lib/mailgun/client.rb +56 -56
- data/lib/mailgun/domains/domains.rb +11 -10
- data/lib/mailgun/events/events.rb +4 -3
- data/lib/mailgun/exceptions/exceptions.rb +12 -15
- data/lib/mailgun/helpers/api_version_checker.rb +6 -1
- data/lib/mailgun/lists/opt_in_handler.rb +6 -10
- data/lib/mailgun/logs/logs.rb +4 -2
- data/lib/mailgun/messages/batch_message.rb +10 -10
- data/lib/mailgun/messages/message_builder.rb +40 -56
- data/lib/mailgun/metrics/metrics.rb +12 -6
- data/lib/mailgun/response.rb +12 -10
- data/lib/mailgun/subaccounts/subaccounts.rb +13 -8
- data/lib/mailgun/suppressions.rb +36 -43
- data/lib/mailgun/tags/analytics_tags.rb +37 -2
- data/lib/mailgun/tags/tags.rb +29 -19
- data/lib/mailgun/templates/templates.rb +40 -29
- data/lib/mailgun/version.rb +3 -1
- data/lib/mailgun/webhooks/webhooks.rb +22 -19
- data/lib/mailgun-ruby.rb +2 -0
- data/lib/mailgun.rb +4 -4
- data/lib/railgun/attachment.rb +12 -19
- data/lib/railgun/errors.rb +2 -3
- data/lib/railgun/mailer.rb +37 -41
- data/lib/railgun/railtie.rb +2 -0
- data/lib/railgun.rb +2 -0
- data/mailgun.gemspec +15 -11
- data/spec/integration/analytics_tags_spec.rb +54 -0
- data/spec/integration/bounces_spec.rb +12 -11
- data/spec/integration/campaign_spec.rb +20 -18
- data/spec/integration/complaints_spec.rb +8 -6
- data/spec/integration/domains_spec.rb +12 -18
- data/spec/integration/email_validation_spec.rb +35 -34
- data/spec/integration/events_spec.rb +8 -8
- data/spec/integration/list_members_spec.rb +27 -26
- data/spec/integration/list_spec.rb +22 -21
- data/spec/integration/logs_spec.rb +49 -47
- data/spec/integration/mailer_spec.rb +7 -3
- data/spec/integration/mailgun_spec.rb +85 -92
- data/spec/integration/metrics_spec.rb +137 -131
- data/spec/integration/routes_spec.rb +41 -40
- data/spec/integration/stats_spec.rb +4 -2
- data/spec/integration/subaccounts_spec.rb +9 -10
- data/spec/integration/suppressions_spec.rb +222 -44
- data/spec/integration/templates_spec.rb +14 -12
- data/spec/integration/unsubscribes_spec.rb +8 -6
- data/spec/integration/webhook_spec.rb +18 -12
- data/spec/spec_helper.rb +15 -8
- data/spec/unit/client_spec.rb +424 -0
- data/spec/unit/connection/test_client.rb +108 -55
- data/spec/unit/events/events_spec.rb +48 -29
- data/spec/unit/exceptions/exceptions_spec.rb +8 -7
- data/spec/unit/helpers/api_version_checker_spec.rb +206 -0
- data/spec/unit/lists/opt_in_handler_spec.rb +11 -7
- data/spec/unit/mailgun_spec.rb +71 -68
- data/spec/unit/messages/batch_message_spec.rb +37 -36
- data/spec/unit/messages/message_builder_spec.rb +170 -169
- data/spec/unit/railgun/content_type_spec.rb +31 -30
- data/spec/unit/railgun/mailer_spec.rb +62 -59
- data/spec/unit/response_spec.rb +225 -0
- data/vcr_cassettes/For_the_suppressions_handling_class/creates_a_single_bounce.yml +55 -0
- data/vcr_cassettes/analytics_tags.yml +187 -0
- data/vcr_cassettes/suppressions.yml +1053 -170
- metadata +95 -29
- data/.rubocop_todo.yml +0 -22
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'time'
|
|
2
4
|
require 'json'
|
|
3
5
|
|
|
4
6
|
module Mailgun
|
|
5
7
|
class UnitClient < Mailgun::Client
|
|
6
|
-
|
|
7
8
|
attr_reader :options, :block, :body, :code, :response
|
|
8
9
|
|
|
9
10
|
def initialize(endpoint, &block)
|
|
@@ -14,9 +15,10 @@ module Mailgun
|
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
def [](endpoint, &new_block)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
if block_given?
|
|
19
|
+
self.class.new(endpoint, &new_block)
|
|
20
|
+
elsif block
|
|
21
|
+
self.class.new(endpoint, &block)
|
|
20
22
|
else
|
|
21
23
|
self.class.new(endpoint)
|
|
22
24
|
end
|
|
@@ -26,10 +28,8 @@ module Mailgun
|
|
|
26
28
|
perform_data_validation(working_domain, data)
|
|
27
29
|
case data
|
|
28
30
|
when Hash
|
|
29
|
-
if data.
|
|
30
|
-
if data[:message].is_a?(String)
|
|
31
|
-
data[:message] = convert_string_to_file(data[:message])
|
|
32
|
-
end
|
|
31
|
+
if data.key?(:message)
|
|
32
|
+
data[:message] = convert_string_to_file(data[:message]) if data[:message].is_a?(String)
|
|
33
33
|
post("#{working_domain}/messages.mime", data)
|
|
34
34
|
else
|
|
35
35
|
post("#{working_domain}/messages", data)
|
|
@@ -37,80 +37,133 @@ module Mailgun
|
|
|
37
37
|
when MessageBuilder
|
|
38
38
|
post("#{working_domain}/messages", data.message)
|
|
39
39
|
else
|
|
40
|
-
raise ParameterError.new(
|
|
40
|
+
raise ParameterError.new('Unknown data type for data parameter.', data)
|
|
41
41
|
end
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
def post(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
end
|
|
44
|
+
def post(_path, _data)
|
|
45
|
+
Mailgun::Response.new(response_generator(@endpoint))
|
|
46
|
+
rescue StandardError => e
|
|
47
|
+
p e
|
|
48
|
+
raise CommunicationError.new(e), e.response if e.respond_to? :response
|
|
49
|
+
|
|
50
|
+
raise CommunicationError, e.message
|
|
52
51
|
end
|
|
53
52
|
|
|
54
|
-
def get(
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
raise CommunicationError.new(e), e.response
|
|
59
|
-
end
|
|
53
|
+
def get(_path, _query_string = nil)
|
|
54
|
+
Mailgun::Response.new(response_generator(@endpoint))
|
|
55
|
+
rescue StandardError => e
|
|
56
|
+
raise CommunicationError.new(e), e.response
|
|
60
57
|
end
|
|
61
58
|
|
|
62
|
-
def put(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
raise CommunicationError.new(e), e.response
|
|
67
|
-
end
|
|
59
|
+
def put(_path, _data)
|
|
60
|
+
Mailgun::Response.new(response_generator(@endpoint))
|
|
61
|
+
rescue StandardError => e
|
|
62
|
+
raise CommunicationError.new(e), e.response
|
|
68
63
|
end
|
|
69
64
|
|
|
70
|
-
def delete(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
raise CommunicationError.new(e), e.response
|
|
75
|
-
end
|
|
65
|
+
def delete(_path)
|
|
66
|
+
Mailgun::Response.new(response_generator(@endpoint))
|
|
67
|
+
rescue StandardError => e
|
|
68
|
+
raise CommunicationError.new(e), e.response
|
|
76
69
|
end
|
|
77
70
|
|
|
78
71
|
private
|
|
79
72
|
|
|
80
73
|
def perform_data_validation(working_domain, data)
|
|
81
|
-
|
|
74
|
+
raise ParameterError.new('Missing working domain', working_domain) unless working_domain
|
|
82
75
|
return true unless data.is_a?(Hash) && data.present?
|
|
76
|
+
|
|
83
77
|
message = data.respond_to?(:message) ? data.message : data
|
|
84
78
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
79
|
+
if message.fetch('to', []).empty? && message.fetch(:to, []).empty?
|
|
80
|
+
raise ParameterError.new(
|
|
81
|
+
'Missing `to` recipient, message should contain at least 1 recipient',
|
|
82
|
+
working_domain
|
|
83
|
+
)
|
|
84
|
+
end
|
|
85
|
+
return unless message.fetch('from', []).empty? && message.fetch(:from, []).empty?
|
|
86
|
+
|
|
87
|
+
raise ParameterError.new(
|
|
90
88
|
'Missing a `from` sender, message should contain at least 1 `from` sender',
|
|
91
89
|
working_domain
|
|
92
|
-
)
|
|
90
|
+
)
|
|
93
91
|
end
|
|
94
92
|
|
|
95
93
|
def response_generator(resource_endpoint)
|
|
96
|
-
if resource_endpoint ==
|
|
94
|
+
if resource_endpoint == 'messages'
|
|
97
95
|
t = Time.now
|
|
98
|
-
id = "<#{t.to_i}.#{rand(
|
|
99
|
-
return Response.from_hash({ body: JSON.generate({
|
|
96
|
+
id = "<#{t.to_i}.#{rand(99_999_999)}.5817@example.com>"
|
|
97
|
+
return Response.from_hash({ body: JSON.generate({ 'message' => 'Queued. Thank you.', 'id' => id }) })
|
|
100
98
|
end
|
|
101
|
-
if resource_endpoint ==
|
|
102
|
-
return Response.from_hash(
|
|
99
|
+
if resource_endpoint == 'bounces'
|
|
100
|
+
return Response.from_hash(
|
|
101
|
+
{ body: JSON.generate(
|
|
102
|
+
{
|
|
103
|
+
'total_count' => 1,
|
|
104
|
+
'items' =>
|
|
105
|
+
{
|
|
106
|
+
'created_at' => 'Fri, 21 Oct 2011 11:02:55 GMT',
|
|
107
|
+
'status' => 550,
|
|
108
|
+
'address' => 'baz@example.com',
|
|
109
|
+
'error' => "Message was not accepted -- invalid mailbox. \
|
|
110
|
+
Local mailbox baz@example.com is unavailable: user not found"
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
) }
|
|
114
|
+
)
|
|
103
115
|
end
|
|
104
|
-
if resource_endpoint ==
|
|
105
|
-
return Response.from_hash(
|
|
116
|
+
if resource_endpoint == 'lists'
|
|
117
|
+
return Response.from_hash(
|
|
118
|
+
{ body: JSON.generate(
|
|
119
|
+
{
|
|
120
|
+
'member' =>
|
|
121
|
+
{
|
|
122
|
+
'vars' => { 'age' => 26 },
|
|
123
|
+
'name' => 'Foo Bar',
|
|
124
|
+
'subscribed' => false,
|
|
125
|
+
'address' => 'bar@example.com'
|
|
126
|
+
},
|
|
127
|
+
'message' => 'Mailing list member has been updated'
|
|
128
|
+
}
|
|
129
|
+
) }
|
|
130
|
+
)
|
|
106
131
|
end
|
|
107
|
-
if resource_endpoint ==
|
|
108
|
-
return Response.from_hash(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
132
|
+
if resource_endpoint == 'campaigns'
|
|
133
|
+
return Response.from_hash(
|
|
134
|
+
{
|
|
135
|
+
body: JSON.generate(
|
|
136
|
+
{
|
|
137
|
+
'message' => 'Campaign has been deleted',
|
|
138
|
+
'id' => 'ABC123'
|
|
139
|
+
}
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
)
|
|
112
143
|
end
|
|
144
|
+
return unless resource_endpoint == 'events'
|
|
145
|
+
|
|
146
|
+
Response.from_hash(
|
|
147
|
+
{ body: JSON.generate(
|
|
148
|
+
{ 'items' => [],
|
|
149
|
+
'paging' => {
|
|
150
|
+
'next' =>
|
|
151
|
+
"https://api.mailgun.net/v3/thisisatestdomainformailgun.com/events/W3siYiI6I\
|
|
152
|
+
CIyMDE0LTA1LTA3VDAwOjQ1OjUxLjc0MDg5MiswMDowMCIsICJlIjogIjIwMTQtMDUtMDVUMDA6NDU6NTEuNzQwOTgzK\
|
|
153
|
+
zAwOjAwIn0sIHsiYiI6ICIyMDE0LTA1LTA3VDAwOjQ1OjUxLjc0MDg5MiswMDowMCIsICJlIjogIjIwMTQtMDUtMDVUM\
|
|
154
|
+
DA6NDU6NTEuNzQwOTgzKzAwOjAwIn0sIFsiZiJdLCBudWxsLCB7ImFjY291bnQuaWQiOiAiNGU4MjMwZjYxNDc2ZDg2N\
|
|
155
|
+
zEzMDBjNDc2IiwgImRvbWFpbi5uYW1lIjogInRoaXNpc2F0ZXN0ZG9tYWluZm9ybWFpbGd1bi5jb20iLCAic2V2ZXJpd\
|
|
156
|
+
HkiOiAiTk9UIGludGVybmFsIn0sIDEwMCwgbnVsbF0=",
|
|
157
|
+
'previous' =>
|
|
158
|
+
"https://api.mailgun.net/v2/thisisatestdomainformailgun.com/events/W3siYiI6IC\
|
|
159
|
+
IyMDE0LTA1LTA3VDAwOjQ1OjUxLjc0MDg5MiswMDowMCIsICJlIjogIjIwMTQtMDUtMDVUMDA6NDU6NTEuNzQwOTgzKzA\
|
|
160
|
+
wOjAwIn0sIHsiYiI6ICIyMDE0LTA1LTA3VDAwOjQ1OjUxLjc0MDg5MiswMDowMCIsICJlIjogIjIwMTQtMDUtMDdUMDA6\
|
|
161
|
+
NDU6NTEuNzQxODkyKzAwOjAwIn0sIFsicCIsICJmIl0sIG51bGwsIHsiYWNjb3VudC5pZCI6ICI0ZTgyMzBmNjE0NzZkO\
|
|
162
|
+
DY3MTMwMGM0NzYiLCAiZG9tYWluLm5hbWUiOiAidGhpc2lzYXRlc3Rkb21haW5mb3JtYWlsZ3VuLmNvbSIsICJzZXZlcm\
|
|
163
|
+
l0eSI6ICJOT1QgaW50ZXJuYWwifSwgMTAwLCBudWxsXQ=="
|
|
164
|
+
} }
|
|
165
|
+
) }
|
|
166
|
+
)
|
|
113
167
|
end
|
|
114
168
|
end
|
|
115
|
-
|
|
116
169
|
end
|
|
@@ -1,66 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'spec_helper'
|
|
2
4
|
|
|
3
5
|
describe 'The method get' do
|
|
4
|
-
it '
|
|
6
|
+
it 'returns a proper hash of log data.' do
|
|
5
7
|
@mg_obj = Mailgun::UnitClient.new('events')
|
|
6
|
-
events = Mailgun::Events.new(@mg_obj,
|
|
7
|
-
result = events.get
|
|
8
|
+
events = Mailgun::Events.new(@mg_obj, 'samples.mailgun.org')
|
|
9
|
+
result = events.get
|
|
8
10
|
|
|
9
|
-
expect(result.body).to include(
|
|
10
|
-
expect(result.body).to include(
|
|
11
|
+
expect(result.body).to include('items')
|
|
12
|
+
expect(result.body).to include('paging')
|
|
11
13
|
end
|
|
12
14
|
end
|
|
13
15
|
|
|
14
16
|
describe 'Pagination' do
|
|
15
|
-
it '
|
|
17
|
+
it 'returns a proper hash of log data.' do
|
|
16
18
|
@mg_obj = Mailgun::UnitClient.new('events')
|
|
17
|
-
events = Mailgun::Events.new(@mg_obj,
|
|
18
|
-
result = events.get
|
|
19
|
+
events = Mailgun::Events.new(@mg_obj, 'samples.mailgun.org')
|
|
20
|
+
result = events.get
|
|
19
21
|
|
|
20
22
|
json = JSON.parse(result.body)
|
|
21
|
-
expect(json).to include(
|
|
22
|
-
expect(json[
|
|
23
|
-
expect(json[
|
|
23
|
+
expect(json).to include('paging')
|
|
24
|
+
expect(json['paging']).to include('next')
|
|
25
|
+
expect(json['paging']).to include('previous')
|
|
24
26
|
end
|
|
25
27
|
|
|
26
|
-
it '
|
|
27
|
-
events = Mailgun::Events.new(@mg_obj,
|
|
28
|
-
output = events.send(
|
|
28
|
+
it 'calculates proper next-page url' do
|
|
29
|
+
events = Mailgun::Events.new(@mg_obj, 'samples.mailgun.org')
|
|
30
|
+
output = events.send(
|
|
31
|
+
:extract_endpoint_from,
|
|
32
|
+
"/v3/samples.mailgun.org/events/W3siYiI6ICIyMDE3LTA1\
|
|
33
|
+
LTEwVDIwOjA2OjU0LjU3NiswMDowMCIsICJlIjogIjIwMTctMDUtMDhUMj\
|
|
34
|
+
A6MDY6NTQuNTc3KzAwOjAwIn0sIHsiYiI6ICIyMDE3LTA1LTEwVDIwOjA2\
|
|
35
|
+
OjU0LjU3NiswMDowMCIsICJlIjogIjIwMTctMDUtMDhUMjA6MDY6NTQuNT\
|
|
36
|
+
c3KzAwOjAwIn0sIFsiZiJdLCBudWxsLCBbWyJhY2NvdW50LmlkIiwgIjU4\
|
|
37
|
+
MDUyMTg2NzhmYTE2MTNjNzkwYjUwZiJdLCBbImRvbWFpbi5uYW1lIiwgIn\
|
|
38
|
+
NhbmRib3gyOTcwMTUyYWYzZDM0NTU5YmZjN2U3MTcwM2E2Y2YyNC5tYWls\
|
|
39
|
+
Z3VuLm9yZyJdXSwgMTAwLCBudWxsXQ=="
|
|
40
|
+
)
|
|
29
41
|
|
|
30
|
-
expect(output).to eq
|
|
42
|
+
expect(output).to eq "W3siYiI6ICIyMDE3LTA1LTEwVDIwOjA2O\
|
|
43
|
+
jU0LjU3NiswMDowMCIsICJlIjogIjIwMTctMDUtMDhUMjA6MDY6NTQuNTc3\
|
|
44
|
+
KzAwOjAwIn0sIHsiYiI6ICIyMDE3LTA1LTEwVDIwOjA2OjU0LjU3NiswMDo\
|
|
45
|
+
wMCIsICJlIjogIjIwMTctMDUtMDhUMjA6MDY6NTQuNTc3KzAwOjAwIn0sIF\
|
|
46
|
+
siZiJdLCBudWxsLCBbWyJhY2NvdW50LmlkIiwgIjU4MDUyMTg2NzhmYTE2M\
|
|
47
|
+
TNjNzkwYjUwZiJdLCBbImRvbWFpbi5uYW1lIiwgInNhbmRib3gyOTcwMTUy\
|
|
48
|
+
YWYzZDM0NTU5YmZjN2U3MTcwM2E2Y2YyNC5tYWlsZ3VuLm9yZyJdXSwgMTA\
|
|
49
|
+
wLCBudWxsXQ=="
|
|
31
50
|
end
|
|
32
51
|
end
|
|
33
52
|
|
|
34
53
|
describe 'The method next' do
|
|
35
|
-
it '
|
|
54
|
+
it 'returns the next series of data.' do
|
|
36
55
|
@mg_obj = Mailgun::UnitClient.new('events')
|
|
37
|
-
events = Mailgun::Events.new(@mg_obj,
|
|
38
|
-
result = events.next
|
|
56
|
+
events = Mailgun::Events.new(@mg_obj, 'samples.mailgun.org')
|
|
57
|
+
result = events.next
|
|
39
58
|
|
|
40
|
-
expect(result.body).to include(
|
|
41
|
-
expect(result.body).to include(
|
|
59
|
+
expect(result.body).to include('items')
|
|
60
|
+
expect(result.body).to include('paging')
|
|
42
61
|
end
|
|
43
62
|
end
|
|
44
63
|
|
|
45
64
|
describe 'The method previous' do
|
|
46
|
-
it '
|
|
65
|
+
it 'returns the previous series of data.' do
|
|
47
66
|
@mg_obj = Mailgun::UnitClient.new('events')
|
|
48
|
-
events = Mailgun::Events.new(@mg_obj,
|
|
49
|
-
result = events.previous
|
|
67
|
+
events = Mailgun::Events.new(@mg_obj, 'samples.mailgun.org')
|
|
68
|
+
result = events.previous
|
|
50
69
|
|
|
51
|
-
expect(result.body).to include(
|
|
52
|
-
expect(result.body).to include(
|
|
70
|
+
expect(result.body).to include('items')
|
|
71
|
+
expect(result.body).to include('paging')
|
|
53
72
|
end
|
|
54
73
|
end
|
|
55
74
|
|
|
56
75
|
describe 'The method each' do
|
|
57
|
-
it '
|
|
76
|
+
it 'iterates over all event items.' do
|
|
58
77
|
@mg_obj = Mailgun::UnitClient.new('events')
|
|
59
|
-
events = Mailgun::Events.new(@mg_obj,
|
|
78
|
+
events = Mailgun::Events.new(@mg_obj, 'samples.mailgun.org')
|
|
60
79
|
# Events from the UnitClient are actually empty.
|
|
61
80
|
count = 0
|
|
62
|
-
events.each do |
|
|
63
|
-
count
|
|
81
|
+
events.each do |_e|
|
|
82
|
+
count += 1
|
|
64
83
|
end
|
|
65
84
|
|
|
66
85
|
# Better than nothing..
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'spec_helper'
|
|
2
4
|
|
|
3
5
|
RSpec.describe Mailgun::CommunicationError do
|
|
@@ -9,22 +11,21 @@ RSpec.describe Mailgun::CommunicationError do
|
|
|
9
11
|
end.not_to raise_error
|
|
10
12
|
end
|
|
11
13
|
|
|
12
|
-
context
|
|
13
|
-
it
|
|
14
|
+
context 'when the Response body has an `Error` property' do
|
|
15
|
+
it 'uses the `Error` property as the API message' do
|
|
14
16
|
subject = described_class.new('Boom!', { status: 401, body: '{"Error":"unauthorized"}' })
|
|
15
17
|
|
|
16
|
-
expect(subject.message).to eq(
|
|
18
|
+
expect(subject.message).to eq('Boom!: unauthorized')
|
|
17
19
|
end
|
|
18
20
|
end
|
|
19
21
|
|
|
20
|
-
context
|
|
21
|
-
it
|
|
22
|
+
context 'when the Response body has an `error` property' do
|
|
23
|
+
it 'uses the `Error` property as the API message' do
|
|
22
24
|
subject = described_class.new('Boom!', { status: 401, body: '{"error":"not found"}' })
|
|
23
25
|
|
|
24
|
-
expect(subject.message).to eq(
|
|
26
|
+
expect(subject.message).to eq('Boom!: not found')
|
|
25
27
|
end
|
|
26
28
|
end
|
|
27
|
-
|
|
28
29
|
end
|
|
29
30
|
end
|
|
30
31
|
end
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
require 'mailgun'
|
|
6
|
+
require 'mailgun/helpers/api_version_checker'
|
|
7
|
+
|
|
8
|
+
describe Mailgun::ApiVersionChecker do
|
|
9
|
+
# Build a minimal host class that includes the module, with a controllable
|
|
10
|
+
# @client so we can set api_version per example.
|
|
11
|
+
subject(:instance) { host_class.new(client) }
|
|
12
|
+
|
|
13
|
+
let(:client) { double(:client) }
|
|
14
|
+
|
|
15
|
+
let(:host_class) do
|
|
16
|
+
Class.new do
|
|
17
|
+
include Mailgun::ApiVersionChecker
|
|
18
|
+
|
|
19
|
+
attr_reader :result
|
|
20
|
+
|
|
21
|
+
def initialize(client)
|
|
22
|
+
@client = client
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def do_something(arg = nil)
|
|
26
|
+
@result = arg || :called
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# ──────────────────────────────────────────────────────────────────────────
|
|
32
|
+
# .included / module structure
|
|
33
|
+
# ──────────────────────────────────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
describe '.included' do
|
|
36
|
+
it 'extends the including class with ClassMethods' do
|
|
37
|
+
expect(host_class).to respond_to(:requires_api_version)
|
|
38
|
+
expect(host_class).to respond_to(:enforces_api_version)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'does not expose ClassMethods as instance methods' do
|
|
42
|
+
expect(instance).not_to respond_to(:requires_api_version)
|
|
43
|
+
expect(instance).not_to respond_to(:enforces_api_version)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# ──────────────────────────────────────────────────────────────────────────
|
|
48
|
+
# .requires_api_version (warns but still calls through)
|
|
49
|
+
# ──────────────────────────────────────────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
describe '.requires_api_version' do
|
|
52
|
+
before { host_class.requires_api_version('v3', :do_something) }
|
|
53
|
+
|
|
54
|
+
context 'when the client is on the expected version' do
|
|
55
|
+
before { allow(client).to receive(:api_version).and_return('v3') }
|
|
56
|
+
|
|
57
|
+
it 'does not emit a warning' do
|
|
58
|
+
expect { instance.do_something }.not_to output.to_stderr
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'still calls the original method' do
|
|
62
|
+
instance.do_something(:payload)
|
|
63
|
+
expect(instance.result).to eq(:payload)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it 'forwards all arguments to the original method' do
|
|
67
|
+
instance.do_something(:forwarded_arg)
|
|
68
|
+
expect(instance.result).to eq(:forwarded_arg)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
context 'when the client is on a different version' do
|
|
73
|
+
before { allow(client).to receive(:api_version).and_return('v4') }
|
|
74
|
+
|
|
75
|
+
it 'emits a warning to stderr' do
|
|
76
|
+
expect { instance.do_something }
|
|
77
|
+
.to output(/WARN: Client api version must be v3/).to_stderr
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it 'still calls the original method despite the warning' do
|
|
81
|
+
instance.do_something(:payload)
|
|
82
|
+
expect(instance.result).to eq(:payload)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it 'warning message includes the expected version' do
|
|
86
|
+
expect { instance.do_something }
|
|
87
|
+
.to output(/v3/).to_stderr
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it 'wraps multiple methods when given a list' do
|
|
92
|
+
host_class.class_eval { def do_other = @result = :other }
|
|
93
|
+
host_class.requires_api_version('v3', :do_something, :do_other)
|
|
94
|
+
|
|
95
|
+
allow(client).to receive(:api_version).and_return('v4')
|
|
96
|
+
|
|
97
|
+
expect { instance.do_something }.to output(/v3/).to_stderr
|
|
98
|
+
expect { instance.do_other }.to output(/v3/).to_stderr
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it 'wraps only the listed methods, leaving others unwrapped' do
|
|
102
|
+
host_class.class_eval { def unwrapped = @result = :unwrapped }
|
|
103
|
+
host_class.requires_api_version('v3', :do_something)
|
|
104
|
+
|
|
105
|
+
allow(client).to receive(:api_version).and_return('v4')
|
|
106
|
+
|
|
107
|
+
# unwrapped should not warn
|
|
108
|
+
expect { instance.unwrapped }.not_to output.to_stderr
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# ──────────────────────────────────────────────────────────────────────────
|
|
113
|
+
# .enforces_api_version (raises on version mismatch)
|
|
114
|
+
# ──────────────────────────────────────────────────────────────────────────
|
|
115
|
+
|
|
116
|
+
describe '.enforces_api_version' do
|
|
117
|
+
before { host_class.enforces_api_version('v3', :do_something) }
|
|
118
|
+
|
|
119
|
+
context 'when the client is on the expected version' do
|
|
120
|
+
before { allow(client).to receive(:api_version).and_return('v3') }
|
|
121
|
+
|
|
122
|
+
it 'does not raise' do
|
|
123
|
+
expect { instance.do_something }.not_to raise_error
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it 'calls the original method' do
|
|
127
|
+
instance.do_something(:payload)
|
|
128
|
+
expect(instance.result).to eq(:payload)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it 'forwards all arguments to the original method' do
|
|
132
|
+
instance.do_something(:forwarded_arg)
|
|
133
|
+
expect(instance.result).to eq(:forwarded_arg)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
context 'when the client is on a different version' do
|
|
138
|
+
before { allow(client).to receive(:api_version).and_return('v4') }
|
|
139
|
+
|
|
140
|
+
it 'raises Mailgun::ParameterError' do
|
|
141
|
+
expect { instance.do_something }
|
|
142
|
+
.to raise_error(Mailgun::ParameterError)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it 'raises with a message including the expected version' do
|
|
146
|
+
expect { instance.do_something }
|
|
147
|
+
.to raise_error(Mailgun::ParameterError, /v3/)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it 'does not call the original method' do
|
|
151
|
+
begin
|
|
152
|
+
instance.do_something
|
|
153
|
+
rescue StandardError
|
|
154
|
+
nil
|
|
155
|
+
end
|
|
156
|
+
expect(instance.result).to be_nil
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it 'wraps multiple methods when given a list' do
|
|
161
|
+
host_class.class_eval { def do_other = @result = :other }
|
|
162
|
+
host_class.enforces_api_version('v3', :do_something, :do_other)
|
|
163
|
+
|
|
164
|
+
allow(client).to receive(:api_version).and_return('v4')
|
|
165
|
+
|
|
166
|
+
expect { instance.do_something }.to raise_error(Mailgun::ParameterError)
|
|
167
|
+
expect { instance.do_other }.to raise_error(Mailgun::ParameterError)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
it 'wraps only the listed methods, leaving others unwrapped' do
|
|
171
|
+
host_class.class_eval { def unwrapped = @result = :unwrapped }
|
|
172
|
+
host_class.enforces_api_version('v3', :do_something)
|
|
173
|
+
|
|
174
|
+
allow(client).to receive(:api_version).and_return('v4')
|
|
175
|
+
|
|
176
|
+
expect { instance.unwrapped }.not_to raise_error
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# ──────────────────────────────────────────────────────────────────────────
|
|
181
|
+
# requires vs enforces contrast
|
|
182
|
+
# ──────────────────────────────────────────────────────────────────────────
|
|
183
|
+
|
|
184
|
+
describe 'requires_api_version vs enforces_api_version' do
|
|
185
|
+
before do
|
|
186
|
+
host_class.class_eval do
|
|
187
|
+
def soft_method = @result = :soft
|
|
188
|
+
def hard_method = @result = :hard
|
|
189
|
+
end
|
|
190
|
+
host_class.requires_api_version('v3', :soft_method)
|
|
191
|
+
host_class.enforces_api_version('v3', :hard_method)
|
|
192
|
+
|
|
193
|
+
allow(client).to receive(:api_version).and_return('v4')
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
it 'requires_api_version warns but does not raise' do
|
|
197
|
+
expect { instance.soft_method }.to output(/v3/).to_stderr
|
|
198
|
+
expect(instance.result).to eq(:soft)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
it 'enforces_api_version raises and does not call through' do
|
|
202
|
+
expect { instance.hard_method }.to raise_error(Mailgun::ParameterError)
|
|
203
|
+
expect(instance.result).not_to eq(:hard)
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
@@ -1,22 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'spec_helper'
|
|
2
4
|
|
|
3
5
|
describe 'The method generate_hash' do
|
|
4
|
-
before
|
|
5
|
-
@mailing_list =
|
|
6
|
-
@secret_app_id =
|
|
7
|
-
@recipient_address =
|
|
8
|
-
@precalculated_hash =
|
|
6
|
+
before do
|
|
7
|
+
@mailing_list = 'mylist@example.com'
|
|
8
|
+
@secret_app_id = 'mysupersecretpassword'
|
|
9
|
+
@recipient_address = 'bob@example.com'
|
|
10
|
+
@precalculated_hash =
|
|
11
|
+
"eyJoIjoiMmY3ZmY1MzFlOGJmMjA0OWNhMTI3ZmU4ZTQyNjZkOTljYzhkMTdk%0AMiIsInAiOiJleUpzSWpv\
|
|
12
|
+
aWJYbHNhWE4wUUdWNFlXMXdiR1V1WTI5dElpd2lj%0AaUk2SW1KdllrQmxlR0Z0Y0d4bExtTnZcbmJTSjlcbiJ9%0A"
|
|
9
13
|
end
|
|
10
14
|
|
|
11
15
|
it 'generates a web safe hash for the recipient wishing to subscribe' do
|
|
12
16
|
my_hash = Mailgun::OptInHandler.generate_hash(@mailing_list, @secret_app_id, @recipient_address)
|
|
13
|
-
|
|
17
|
+
|
|
14
18
|
expect(my_hash).to eq(@precalculated_hash)
|
|
15
19
|
end
|
|
16
20
|
|
|
17
21
|
it 'generates a web safe hash for the recipient wishing to subscribe' do
|
|
18
22
|
validate_result = Mailgun::OptInHandler.validate_hash(@secret_app_id, @precalculated_hash)
|
|
19
|
-
|
|
23
|
+
|
|
20
24
|
expect(validate_result.length).to eq(2)
|
|
21
25
|
expect(validate_result['recipient_address']).to eq(@recipient_address)
|
|
22
26
|
expect(validate_result['mailing_list']).to eq(@mailing_list)
|