mailgun-tracking 2.0.0 → 3.0.0
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/lib/generators/mailgun/tracking/templates/mailgun_tracking.rb.erb +1 -1
- data/lib/mailgun-tracking.rb +3 -0
- data/lib/mailgun/tracking.rb +65 -33
- data/lib/mailgun/tracking/auth.rb +52 -0
- data/lib/mailgun/tracking/configuration.rb +5 -9
- data/lib/mailgun/tracking/fanout.rb +18 -0
- data/lib/mailgun/tracking/middleware.rb +10 -47
- data/lib/mailgun/tracking/railtie.rb +2 -2
- data/lib/mailgun/tracking/version.rb +8 -7
- data/spec/dummy/logs/test.log +77 -0
- data/spec/dummy/rails/logs/test.log +19 -11
- data/spec/mailgun/tracking/auth_spec.rb +31 -0
- data/spec/mailgun/tracking/middleware_spec.rb +43 -51
- data/spec/mailgun/tracking_spec.rb +11 -4
- data/spec/support/shared_examples/integration/acts_as_rack.rb +6 -4
- metadata +18 -53
- data/lib/mailgun/tracking/exceptions.rb +0 -11
- data/lib/mailgun/tracking/listener.rb +0 -55
- data/lib/mailgun/tracking/notifier.rb +0 -61
- data/lib/mailgun/tracking/payload.rb +0 -96
- data/lib/mailgun/tracking/request.rb +0 -47
- data/lib/mailgun/tracking/signature.rb +0 -48
- data/lib/mailgun/tracking/subscriber.rb +0 -26
- data/lib/mailgun/tracking/subscriber/all_messages.rb +0 -37
- data/lib/mailgun/tracking/subscriber/evented.rb +0 -40
- data/lib/mailgun/tracking/util.rb +0 -67
- data/spec/mailgun/tracking/listener_spec.rb +0 -48
- data/spec/mailgun/tracking/notifier_spec.rb +0 -66
- data/spec/mailgun/tracking/payload_spec.rb +0 -73
- data/spec/mailgun/tracking/request_spec.rb +0 -63
- data/spec/mailgun/tracking/signature_spec.rb +0 -65
- data/spec/mailgun/tracking/subscriber/all_messages_spec.rb +0 -13
- data/spec/mailgun/tracking/subscriber/evented_spec.rb +0 -14
- data/spec/mailgun/tracking/subscriber_spec.rb +0 -15
- data/spec/mailgun/tracking/util_spec.rb +0 -155
@@ -1,66 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe Mailgun::Tracking::Notifier do
|
4
|
-
subject(:notifier) { described_class.new(listener) }
|
5
|
-
|
6
|
-
let(:listener) { instance_double(Mailgun::Tracking::Listener) }
|
7
|
-
|
8
|
-
describe '#empty?' do
|
9
|
-
context 'when there is at least one subscriber' do
|
10
|
-
let(:subscriber) { instance_double(Mailgun::Tracking::Subscriber::AllMessages) }
|
11
|
-
|
12
|
-
before { allow(listener).to receive(:subscribers).and_return([subscriber]) }
|
13
|
-
|
14
|
-
it { is_expected.not_to be_empty }
|
15
|
-
end
|
16
|
-
|
17
|
-
context 'when there are no subscribers' do
|
18
|
-
before { allow(listener).to receive(:subscribers).and_return([]) }
|
19
|
-
|
20
|
-
it { is_expected.to be_empty }
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
describe '#subscribe' do
|
25
|
-
let(:callable) { proc {} }
|
26
|
-
|
27
|
-
before { allow(listener).to receive(:add_subscriber) }
|
28
|
-
|
29
|
-
it 'subscribes on event' do
|
30
|
-
notifier.subscribe(:delivered, callable)
|
31
|
-
|
32
|
-
expect(listener).to have_received(:add_subscriber)
|
33
|
-
.with(:delivered, callable)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
describe '#all' do
|
38
|
-
let(:callable) { proc {} }
|
39
|
-
|
40
|
-
before { allow(listener).to receive(:add_subscriber) }
|
41
|
-
|
42
|
-
it 'subscribes on all events' do
|
43
|
-
notifier.all(callable)
|
44
|
-
|
45
|
-
expect(listener).to have_received(:add_subscriber).with(nil, callable)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
describe '#broadcast' do
|
50
|
-
let(:payload) { Mailgun::Tracking::Payload.new(fixture('delivered.json')) }
|
51
|
-
|
52
|
-
before do
|
53
|
-
allow(Mailgun::Tracking::Signature).to receive(:verify!)
|
54
|
-
allow(listener).to receive(:broadcast)
|
55
|
-
notifier.broadcast(:delivered, payload)
|
56
|
-
end
|
57
|
-
|
58
|
-
it 'verify signature' do
|
59
|
-
expect(Mailgun::Tracking::Signature).to have_received(:verify!).with(payload)
|
60
|
-
end
|
61
|
-
|
62
|
-
it 'broadcasts an event' do
|
63
|
-
expect(listener).to have_received(:broadcast).with(:delivered, payload)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
@@ -1,73 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe Mailgun::Tracking::Payload do
|
4
|
-
subject(:payload) { described_class.new(values) }
|
5
|
-
|
6
|
-
describe '#==' do
|
7
|
-
let(:values) { { 'foo' => 'bar' } }
|
8
|
-
|
9
|
-
it { is_expected.to eq(described_class.new('foo' => 'bar')) }
|
10
|
-
it { is_expected.not_to eq(described_class.new('foo': 'rab')) }
|
11
|
-
it { is_expected.not_to eq('foo') }
|
12
|
-
end
|
13
|
-
|
14
|
-
describe '#respond_to?' do
|
15
|
-
let(:values) { { 'foo' => 'bar', 'boolean': true } }
|
16
|
-
|
17
|
-
it { is_expected.to respond_to(:foo) }
|
18
|
-
it { is_expected.to respond_to(:boolean?) }
|
19
|
-
it { is_expected.not_to respond_to(:foo?) }
|
20
|
-
end
|
21
|
-
|
22
|
-
describe '#[]' do
|
23
|
-
let(:values) { { 'Message-Id' => '<payload@mailgun-tracking.com>' } }
|
24
|
-
|
25
|
-
it 'returns the value object from values' do
|
26
|
-
expect(payload[:message_id]).to eq('<payload@mailgun-tracking.com>')
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
describe '#hash' do
|
31
|
-
let(:values) { { 'foo' => 'bar' } }
|
32
|
-
|
33
|
-
it { expect(payload.hash).to eq(described_class.new('foo' => 'bar').hash) }
|
34
|
-
it { expect(payload.hash).not_to eq(described_class.new('foo': 'rab').hash) }
|
35
|
-
it { expect(payload.hash).not_to eq('foo'.hash) }
|
36
|
-
end
|
37
|
-
|
38
|
-
describe '#to_hash' do
|
39
|
-
let(:values) do
|
40
|
-
{
|
41
|
-
'foo' => 'bar',
|
42
|
-
'list' => [described_class.new('foo' => 'bar')],
|
43
|
-
'bar' => { 'foo' => 'bar', 'bar' => described_class.new('foo' => 'bar') }
|
44
|
-
}
|
45
|
-
end
|
46
|
-
|
47
|
-
it 'recursively call to_hash on its values' do
|
48
|
-
expect(payload.to_hash).to eql(
|
49
|
-
foo: 'bar',
|
50
|
-
list: [{ foo: 'bar' }],
|
51
|
-
bar: { foo: 'bar', bar: { foo: 'bar' } }
|
52
|
-
)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
describe '#to_s' do
|
57
|
-
let(:values) do
|
58
|
-
{
|
59
|
-
'foo' => 'bar',
|
60
|
-
'list' => [described_class.new('foo' => 'bar')],
|
61
|
-
'bar' => { 'foo' => 'bar', 'bar' => described_class.new('foo' => 'bar') }
|
62
|
-
}
|
63
|
-
end
|
64
|
-
|
65
|
-
it 'will call to_s for all embedded stripe objects' do
|
66
|
-
expect(payload.to_s).to eql(JSON.pretty_generate(
|
67
|
-
foo: 'bar',
|
68
|
-
list: [{ foo: 'bar' }],
|
69
|
-
bar: { foo: 'bar', bar: { foo: 'bar' } }
|
70
|
-
))
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
@@ -1,63 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe Mailgun::Tracking::Request do
|
4
|
-
subject(:request) { described_class.new(env) }
|
5
|
-
|
6
|
-
describe '#mailgun_tracking?' do
|
7
|
-
context 'when a request to an endpoint without a POST method' do
|
8
|
-
let(:env) { env_for('http://localhost:3000/mailgun', method: :get) }
|
9
|
-
|
10
|
-
it { is_expected.not_to be_mailgun_tracking }
|
11
|
-
end
|
12
|
-
|
13
|
-
context 'when a request to an endpoint with not acceptable content type' do
|
14
|
-
let(:env) do
|
15
|
-
env_for('http://localhost:3000/mailgun', method: :post, 'CONTENT_TYPE' => 'application/x-www-form-urlencoded')
|
16
|
-
end
|
17
|
-
|
18
|
-
it { is_expected.not_to be_mailgun_tracking }
|
19
|
-
end
|
20
|
-
|
21
|
-
context 'when the request is not to the endpoint' do
|
22
|
-
let(:env) do
|
23
|
-
env_for('http://localhost:3000/_mailgun', method: :post, 'CONTENT_TYPE' => 'application/json; charset=utf-8')
|
24
|
-
end
|
25
|
-
|
26
|
-
it { is_expected.not_to be_mailgun_tracking }
|
27
|
-
end
|
28
|
-
|
29
|
-
context 'when all the above conditions are met' do
|
30
|
-
let(:env) do
|
31
|
-
env_for('http://localhost:3000/mailgun', method: :post, 'CONTENT_TYPE' => 'application/json; charset=utf-8')
|
32
|
-
end
|
33
|
-
|
34
|
-
it { is_expected.to be_mailgun_tracking }
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
describe '#payload' do
|
39
|
-
let(:env) { env_for('http://localhost:3000/mailgun', method: :post, params: { 'signature' => {} }) }
|
40
|
-
|
41
|
-
it 'returns payload' do
|
42
|
-
expect(request.payload).to be_an_instance_of(Mailgun::Tracking::Payload)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
context 'when application/json request' do
|
47
|
-
let(:env) { env_for('/', method: :post, input: input, 'CONTENT_TYPE' => content_type) }
|
48
|
-
let(:input) { 'foo=bar' }
|
49
|
-
let(:content_type) { 'application/json; charset=utf-8' }
|
50
|
-
|
51
|
-
it 'rewinds input' do
|
52
|
-
expect(request.body.read).to eq(input)
|
53
|
-
end
|
54
|
-
|
55
|
-
context 'when input is a hash' do
|
56
|
-
let(:input) { '{"qux": "bin"}' }
|
57
|
-
|
58
|
-
it 'adds a parsed hash to POST params' do
|
59
|
-
expect(request.params['qux']).to eq('bin')
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
@@ -1,65 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe Mailgun::Tracking::Signature do
|
4
|
-
subject(:signature) { described_class.new(payload) }
|
5
|
-
|
6
|
-
describe '.verify!' do
|
7
|
-
context 'when the signature comparison is successful' do
|
8
|
-
let(:payload) do
|
9
|
-
Mailgun::Tracking::Payload.new(
|
10
|
-
signature: {
|
11
|
-
timestamp: '1499697910',
|
12
|
-
token: 'b5751a49a024483da8d41c3684f98b8f',
|
13
|
-
signature: '374e0b1a3deeb57318c783d43ff71093fbf26406a452761dab91bf346a93b49e'
|
14
|
-
}
|
15
|
-
)
|
16
|
-
end
|
17
|
-
|
18
|
-
it { expect(described_class.verify!(payload)).to be true }
|
19
|
-
end
|
20
|
-
|
21
|
-
context 'when the signature comparison is unsuccessful' do
|
22
|
-
let(:payload) do
|
23
|
-
Mailgun::Tracking::Payload.new(
|
24
|
-
signature: {
|
25
|
-
timestamp: '',
|
26
|
-
token: 'b5751a49a024483da8d41c3684f98b8f',
|
27
|
-
signature: '374e0b1a3deeb57318c783d43ff71093fbf26406a452761dab91bf346a93b49e'
|
28
|
-
}
|
29
|
-
)
|
30
|
-
end
|
31
|
-
|
32
|
-
it { expect { described_class.verify!(payload) }.to raise_error(Mailgun::Tracking::InvalidSignature) }
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
describe '#valid?' do
|
37
|
-
context 'when the signature comparison is successful' do
|
38
|
-
let(:payload) do
|
39
|
-
Mailgun::Tracking::Payload.new(
|
40
|
-
signature: {
|
41
|
-
timestamp: '1499697910',
|
42
|
-
token: 'b5751a49a024483da8d41c3684f98b8f',
|
43
|
-
signature: '374e0b1a3deeb57318c783d43ff71093fbf26406a452761dab91bf346a93b49e'
|
44
|
-
}
|
45
|
-
)
|
46
|
-
end
|
47
|
-
|
48
|
-
it { is_expected.to be_valid }
|
49
|
-
end
|
50
|
-
|
51
|
-
context 'when the signature comparison is unsuccessful' do
|
52
|
-
let(:payload) do
|
53
|
-
Mailgun::Tracking::Payload.new(
|
54
|
-
signature: {
|
55
|
-
timestamp: '',
|
56
|
-
token: 'b5751a49a024483da8d41c3684f98b8f',
|
57
|
-
signature: '374e0b1a3deeb57318c783d43ff71093fbf26406a452761dab91bf346a93b49e'
|
58
|
-
}
|
59
|
-
)
|
60
|
-
end
|
61
|
-
|
62
|
-
it { is_expected.not_to be_valid }
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
@@ -1,13 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe Mailgun::Tracking::Subscriber::AllMessages do
|
4
|
-
subject(:subscriber) { described_class.new(callable) }
|
5
|
-
|
6
|
-
it_behaves_like 'subscriber'
|
7
|
-
|
8
|
-
describe '#subscribed_to?' do
|
9
|
-
let(:callable) { proc {} }
|
10
|
-
|
11
|
-
it { is_expected.to be_subscribed_to(:any_event) }
|
12
|
-
end
|
13
|
-
end
|
@@ -1,14 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe Mailgun::Tracking::Subscriber::Evented do
|
4
|
-
subject(:subscriber) { described_class.new('delivered', callable) }
|
5
|
-
|
6
|
-
it_behaves_like 'subscriber'
|
7
|
-
|
8
|
-
describe '#subscribed_to?' do
|
9
|
-
let(:callable) { proc {} }
|
10
|
-
|
11
|
-
it { is_expected.to be_subscribed_to(:delivered) }
|
12
|
-
it { is_expected.not_to be_subscribed_to(:any_event) }
|
13
|
-
end
|
14
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe Mailgun::Tracking::Subscriber do
|
4
|
-
describe '.for' do
|
5
|
-
let(:callable) { proc {} }
|
6
|
-
|
7
|
-
it 'returns an instance of Mailgun::Tracking::Subscriber::AllMessages' do
|
8
|
-
expect(described_class.for(nil, callable)).to be_instance_of(Mailgun::Tracking::Subscriber::AllMessages)
|
9
|
-
end
|
10
|
-
|
11
|
-
it 'returns an instance of Mailgun::Tracking::Subscriber::Evented' do
|
12
|
-
expect(described_class.for('delivered', callable)).to be_instance_of(Mailgun::Tracking::Subscriber::Evented)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
@@ -1,155 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe Mailgun::Tracking::Util do
|
4
|
-
describe '.normalize' do
|
5
|
-
context 'when the keys of the object are strings' do
|
6
|
-
let(:object) do
|
7
|
-
{ 'a' => 1, 'b' => 2 }
|
8
|
-
end
|
9
|
-
|
10
|
-
it 'returns normalized object' do
|
11
|
-
expect(described_class.normalize(object)).to eq(a: 1, b: 2)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
context 'when the object has deep string keys' do
|
16
|
-
let(:object) do
|
17
|
-
{ 'a' => { 'b' => { 'c' => 3 } } }
|
18
|
-
end
|
19
|
-
|
20
|
-
it 'returns normalized object' do
|
21
|
-
expect(described_class.normalize(object)).to eq(a: { b: { c: 3 } })
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
context 'when the keys of the object are symbols' do
|
26
|
-
let(:object) do
|
27
|
-
{ a: 1, b: 2 }
|
28
|
-
end
|
29
|
-
|
30
|
-
it 'returns normalized object' do
|
31
|
-
expect(described_class.normalize(object)).to eq(object)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
context 'when an object has deep symbol keys' do
|
36
|
-
let(:object) do
|
37
|
-
{ a: { b: { c: 3 } } }
|
38
|
-
end
|
39
|
-
|
40
|
-
it 'returns normalized object' do
|
41
|
-
expect(described_class.normalize(object)).to eq(object)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
context 'when the keys of the object are mixed' do
|
46
|
-
let(:object) do
|
47
|
-
{ :a => 1, 'b' => 2 }
|
48
|
-
end
|
49
|
-
|
50
|
-
it 'returns normalized object' do
|
51
|
-
expect(described_class.normalize(object)).to eq(a: 1, b: 2)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
context 'when the keys of the object are integers' do
|
56
|
-
let(:object) do
|
57
|
-
{ 0 => 1, 1 => 2 }
|
58
|
-
end
|
59
|
-
|
60
|
-
it 'returns normalized object' do
|
61
|
-
expect(described_class.normalize(object)).to eq(0 => 1, 1 => 2)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
context 'when an object has deep integer keys' do
|
66
|
-
let(:object) do
|
67
|
-
{ 0 => { 1 => { 2 => 3 } } }
|
68
|
-
end
|
69
|
-
|
70
|
-
it 'returns normalized object' do
|
71
|
-
expect(described_class.normalize(object)).to eq(0 => { 1 => { 2 => 3 } })
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
context 'when the key of the object is illegal symbol' do
|
76
|
-
let(:object) do
|
77
|
-
{ [] => 3 }
|
78
|
-
end
|
79
|
-
|
80
|
-
it 'returns normalized object' do
|
81
|
-
expect(described_class.normalize(object)).to eq([] => 3)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
context 'when an object has deep illegal symbol keys' do
|
86
|
-
let(:object) do
|
87
|
-
{ [] => { [] => 3 } }
|
88
|
-
end
|
89
|
-
|
90
|
-
it 'returns normalized object' do
|
91
|
-
expect(described_class.normalize(object)).to eq([] => { [] => 3 })
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
context 'when the keys of the object are upcase strings' do
|
96
|
-
let(:object) do
|
97
|
-
{ 'A' => 1, 'B' => 2 }
|
98
|
-
end
|
99
|
-
|
100
|
-
it 'returns normalized object' do
|
101
|
-
expect(described_class.normalize(object)).to eq(a: 1, b: 2)
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
context 'when an object has deep upcase string keys' do
|
106
|
-
let(:object) do
|
107
|
-
{ 'A' => { 'B' => { 'C' => 3 } } }
|
108
|
-
end
|
109
|
-
|
110
|
-
it 'returns normalized object' do
|
111
|
-
expect(described_class.normalize(object)).to eq(a: { b: { c: 3 } })
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
context 'when the object value is an array of stringified hashes' do
|
116
|
-
let(:object) do
|
117
|
-
{ 'a' => [{ 'b' => 2 }, { 'c' => 3 }, 4] }
|
118
|
-
end
|
119
|
-
|
120
|
-
it 'returns normalized object' do
|
121
|
-
expect(described_class.normalize(object)).to eq(a: [{ b: 2 }, { c: 3 }, 4])
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
context 'when the object value is an array of symbolized hashes' do
|
126
|
-
let(:object) do
|
127
|
-
{ a: [{ b: 2 }, { c: 3 }, 4] }
|
128
|
-
end
|
129
|
-
|
130
|
-
it 'returns normalized object' do
|
131
|
-
expect(described_class.normalize(object)).to eq(object)
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
context 'when the object value is an array of mixed hashes' do
|
136
|
-
let(:object) do
|
137
|
-
{ a: [{ b: 2 }, { 'c' => 3 }, 4] }
|
138
|
-
end
|
139
|
-
|
140
|
-
it 'returns normalized object' do
|
141
|
-
expect(described_class.normalize(object)).to eq(a: [{ b: 2 }, { c: 3 }, 4])
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
context 'when the object value is an array of upcased hashes' do
|
146
|
-
let(:object) do
|
147
|
-
{ 'A' => [{ 'B' => 2 }, { 'C' => 3 }, 4] }
|
148
|
-
end
|
149
|
-
|
150
|
-
it 'returns normalized object' do
|
151
|
-
expect(described_class.normalize(object)).to eq(a: [{ b: 2 }, { c: 3 }, 4])
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|