mailgun-tracking 2.0.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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