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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/mailgun/tracking/templates/mailgun_tracking.rb.erb +1 -1
  3. data/lib/mailgun-tracking.rb +3 -0
  4. data/lib/mailgun/tracking.rb +65 -33
  5. data/lib/mailgun/tracking/auth.rb +52 -0
  6. data/lib/mailgun/tracking/configuration.rb +5 -9
  7. data/lib/mailgun/tracking/fanout.rb +18 -0
  8. data/lib/mailgun/tracking/middleware.rb +10 -47
  9. data/lib/mailgun/tracking/railtie.rb +2 -2
  10. data/lib/mailgun/tracking/version.rb +8 -7
  11. data/spec/dummy/logs/test.log +77 -0
  12. data/spec/dummy/rails/logs/test.log +19 -11
  13. data/spec/mailgun/tracking/auth_spec.rb +31 -0
  14. data/spec/mailgun/tracking/middleware_spec.rb +43 -51
  15. data/spec/mailgun/tracking_spec.rb +11 -4
  16. data/spec/support/shared_examples/integration/acts_as_rack.rb +6 -4
  17. metadata +18 -53
  18. data/lib/mailgun/tracking/exceptions.rb +0 -11
  19. data/lib/mailgun/tracking/listener.rb +0 -55
  20. data/lib/mailgun/tracking/notifier.rb +0 -61
  21. data/lib/mailgun/tracking/payload.rb +0 -96
  22. data/lib/mailgun/tracking/request.rb +0 -47
  23. data/lib/mailgun/tracking/signature.rb +0 -48
  24. data/lib/mailgun/tracking/subscriber.rb +0 -26
  25. data/lib/mailgun/tracking/subscriber/all_messages.rb +0 -37
  26. data/lib/mailgun/tracking/subscriber/evented.rb +0 -40
  27. data/lib/mailgun/tracking/util.rb +0 -67
  28. data/spec/mailgun/tracking/listener_spec.rb +0 -48
  29. data/spec/mailgun/tracking/notifier_spec.rb +0 -66
  30. data/spec/mailgun/tracking/payload_spec.rb +0 -73
  31. data/spec/mailgun/tracking/request_spec.rb +0 -63
  32. data/spec/mailgun/tracking/signature_spec.rb +0 -65
  33. data/spec/mailgun/tracking/subscriber/all_messages_spec.rb +0 -13
  34. data/spec/mailgun/tracking/subscriber/evented_spec.rb +0 -14
  35. data/spec/mailgun/tracking/subscriber_spec.rb +0 -15
  36. 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