mailgun-tracking 1.1.0 → 2.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/mailgun/tracking.rb +12 -13
- data/lib/mailgun/tracking/middleware.rb +1 -1
- data/lib/mailgun/tracking/notifier.rb +1 -1
- data/lib/mailgun/tracking/payload.rb +69 -17
- data/lib/mailgun/tracking/request.rb +4 -9
- data/lib/mailgun/tracking/signature.rb +5 -9
- data/lib/mailgun/tracking/subscriber.rb +2 -2
- data/lib/mailgun/tracking/util.rb +18 -0
- data/lib/mailgun/tracking/version.rb +2 -2
- data/spec/dummy/hanami/application.rb +13 -0
- data/spec/dummy/rails/logs/test.log +11 -53
- data/spec/fixtures/delivered.json +3 -3
- data/spec/integration/hanami/hanami_spec.rb +9 -0
- data/spec/mailgun/tracking/listener_spec.rb +1 -1
- data/spec/mailgun/tracking/middleware_spec.rb +3 -3
- data/spec/mailgun/tracking/notifier_spec.rb +2 -2
- data/spec/mailgun/tracking/payload_spec.rb +55 -19
- data/spec/mailgun/tracking/request_spec.rb +20 -18
- data/spec/mailgun/tracking/signature_spec.rb +24 -20
- data/spec/support/shared_examples/integration/acts_as_rack.rb +5 -5
- data/spec/support/shared_examples/subscriber.rb +1 -1
- metadata +6 -7
- data/lib/mailgun/tracking/payload/legacy.rb +0 -38
- data/spec/fixtures/legacy/delivered.json +0 -13
- data/spec/mailgun/tracking/payload/legacy_spec.rb +0 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cffbc8bee4968f3230841a2a75da150ef6d17bb2e4d860d05897aef61e55f2e1
|
4
|
+
data.tar.gz: be71a2d5a143a49d4ada8d050ffd402e09917e31cacc83da954654adfeae358d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4451ebb90204a5a887ad09afaac2187b0e86591e880a1bf944a7c671056c93999a8d6ec89920c38928a230e0b86a161eb472f0d57e320085865f12f33dcde843
|
7
|
+
data.tar.gz: efda62e3aa07d4fbe150dbfb48d2f2cbaa6204b49de9f0bb7278e473723ed85515dc56cfe84b503bf9c94b18a70cdfec5e94642ed5b927942381852babceb37e
|
data/lib/mailgun/tracking.rb
CHANGED
@@ -1,18 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
require 'mailgun/tracking/request'
|
3
|
+
require_relative 'tracking/configuration'
|
4
|
+
require_relative 'tracking/exceptions'
|
5
|
+
require_relative 'tracking/listener'
|
6
|
+
require_relative 'tracking/middleware'
|
7
|
+
require_relative 'tracking/notifier'
|
8
|
+
require_relative 'tracking/payload'
|
9
|
+
require_relative 'tracking/signature'
|
10
|
+
require_relative 'tracking/subscriber'
|
11
|
+
require_relative 'tracking/util'
|
12
|
+
require_relative 'tracking/version'
|
13
|
+
require_relative 'tracking/railtie' if defined?(Rails)
|
14
|
+
require_relative 'tracking/request'
|
16
15
|
|
17
16
|
# Module for interacting with the Mailgun.
|
18
17
|
module Mailgun
|
@@ -46,7 +46,7 @@ module Mailgun
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def handle_event
|
49
|
-
Mailgun::Tracking.notifier.broadcast(@request.payload.event, @request.payload)
|
49
|
+
Mailgun::Tracking.notifier.broadcast(@request.payload.event_data.event, @request.payload)
|
50
50
|
null_response
|
51
51
|
rescue InvalidSignature
|
52
52
|
bad_request
|
@@ -1,44 +1,96 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require 'set'
|
4
4
|
|
5
5
|
module Mailgun
|
6
6
|
module Tracking
|
7
7
|
# Payload object.
|
8
8
|
class Payload
|
9
|
-
|
10
|
-
|
9
|
+
TRY_TO_HASH = lambda do |value|
|
10
|
+
return if value.nil?
|
11
|
+
|
12
|
+
value.respond_to?(:to_hash) ? value.to_hash : value
|
13
|
+
end.freeze
|
14
|
+
|
15
|
+
# Initializes a new Payload object.
|
16
|
+
#
|
17
|
+
# @param values [Hash]
|
18
|
+
#
|
19
|
+
# @return [Mailgun::Tracking::Payload]
|
20
|
+
def initialize(values = {})
|
21
|
+
@values = Util.normalize(values)
|
22
|
+
|
23
|
+
@values.each do |key, value|
|
24
|
+
define_instance_methods([key], values) unless __metaclass__.method_defined?(key)
|
25
|
+
@values[key] = Util.convert_to_payload_object(value)
|
26
|
+
end
|
11
27
|
end
|
12
28
|
|
13
|
-
|
14
|
-
|
29
|
+
# Returns a value of the payload with the given key.
|
30
|
+
#
|
31
|
+
# @param key [String]
|
32
|
+
#
|
33
|
+
# @return a value of the payload.
|
34
|
+
def [](key)
|
35
|
+
@values[key]
|
15
36
|
end
|
16
37
|
|
17
|
-
|
18
|
-
|
38
|
+
# @return [Boolean]
|
39
|
+
def ==(other)
|
40
|
+
return false unless other.is_a?(Payload)
|
41
|
+
|
42
|
+
values == other.values
|
19
43
|
end
|
20
44
|
|
21
|
-
|
22
|
-
|
45
|
+
alias eql? ==
|
46
|
+
|
47
|
+
# @return [Integer] The object's hash value (for equality checking)
|
48
|
+
def hash
|
49
|
+
values.hash
|
23
50
|
end
|
24
51
|
|
25
|
-
|
26
|
-
|
52
|
+
# @return [Hash] Recursively convert payload objects to the hash.
|
53
|
+
def to_hash
|
54
|
+
@values.each_with_object({}) do |(key, value), memo|
|
55
|
+
memo[key] =
|
56
|
+
case value
|
57
|
+
when Array
|
58
|
+
value.map(&TRY_TO_HASH)
|
59
|
+
else
|
60
|
+
TRY_TO_HASH.call(value)
|
61
|
+
end
|
62
|
+
end
|
27
63
|
end
|
28
64
|
|
29
|
-
|
30
|
-
|
65
|
+
# @return [String] The string representation of the payload.
|
66
|
+
def to_s
|
67
|
+
JSON.pretty_generate(to_hash)
|
31
68
|
end
|
32
69
|
|
33
70
|
private
|
34
71
|
|
35
|
-
|
36
|
-
|
72
|
+
# @return [Class]
|
73
|
+
def __metaclass__
|
74
|
+
class << self
|
75
|
+
self
|
76
|
+
end
|
37
77
|
end
|
38
78
|
|
39
|
-
|
40
|
-
|
79
|
+
# @return [void]
|
80
|
+
def define_instance_methods(keys, values)
|
81
|
+
__metaclass__.instance_eval do
|
82
|
+
keys.each do |key|
|
83
|
+
define_method(key) { @values[key] }
|
84
|
+
next unless [FalseClass, TrueClass].include?(values[key].class)
|
85
|
+
|
86
|
+
define_method(:"#{key}?") { @values[key] }
|
87
|
+
end
|
88
|
+
end
|
41
89
|
end
|
90
|
+
|
91
|
+
protected
|
92
|
+
|
93
|
+
attr_reader :values
|
42
94
|
end
|
43
95
|
end
|
44
96
|
end
|
@@ -11,19 +11,14 @@ module Mailgun
|
|
11
11
|
# @return [Boolean]
|
12
12
|
def mailgun_tracking?
|
13
13
|
return false unless post?
|
14
|
+
return false unless media_type == APPLICATION_JSON
|
14
15
|
|
15
16
|
path == Configuration.instance.endpoint
|
16
17
|
end
|
17
18
|
|
18
|
-
# @return [Mailgun::Tracking::Payload
|
19
|
+
# @return [Mailgun::Tracking::Payload]
|
19
20
|
def payload
|
20
|
-
@payload ||=
|
21
|
-
if params.key?('timestamp')
|
22
|
-
::Mailgun::Tracking::Payload::Legacy.new(params)
|
23
|
-
else
|
24
|
-
::Mailgun::Tracking::Payload.new(params)
|
25
|
-
end
|
26
|
-
end
|
21
|
+
@payload ||= Payload.new(params)
|
27
22
|
end
|
28
23
|
|
29
24
|
# A Mailgun::Tracking::Request::JSON is used to parsing JSON requests.
|
@@ -42,7 +37,7 @@ module Mailgun
|
|
42
37
|
self.body.rewind
|
43
38
|
env.update(FORM_HASH => ::JSON.parse(body), FORM_INPUT => body)
|
44
39
|
|
45
|
-
|
40
|
+
env[FORM_HASH]
|
46
41
|
end
|
47
42
|
end
|
48
43
|
|
@@ -8,7 +8,7 @@ module Mailgun
|
|
8
8
|
class Signature
|
9
9
|
# Verify the signature of the response parameters.
|
10
10
|
#
|
11
|
-
# @param payload [
|
11
|
+
# @param payload [Mailgun::Tracking::Payload]
|
12
12
|
# @raise [InvalidSignature] Error raised when signature is invalid.
|
13
13
|
#
|
14
14
|
# @return [Boolean]
|
@@ -22,7 +22,7 @@ module Mailgun
|
|
22
22
|
|
23
23
|
# Initializes a new Signature object.
|
24
24
|
#
|
25
|
-
# @param payload [
|
25
|
+
# @param payload [Mailgun::Tracking::Payload]
|
26
26
|
#
|
27
27
|
# @return [Mailgun::Tracking::Signature]
|
28
28
|
def initialize(payload)
|
@@ -31,21 +31,17 @@ module Mailgun
|
|
31
31
|
|
32
32
|
# @return [Boolean]
|
33
33
|
def valid?
|
34
|
-
@payload.signature ==
|
34
|
+
@payload.signature.signature == \
|
35
|
+
OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, Configuration.instance.api_key, data)
|
35
36
|
end
|
36
37
|
|
37
38
|
private
|
38
39
|
|
39
|
-
# @return [OpenSSL::Digest::SHA256]
|
40
|
-
def digest
|
41
|
-
OpenSSL::Digest::SHA256.new
|
42
|
-
end
|
43
|
-
|
44
40
|
# Joins the timestamp and the response token.
|
45
41
|
#
|
46
42
|
# @return [String]
|
47
43
|
def data
|
48
|
-
[@payload.timestamp, @payload.token].join
|
44
|
+
[@payload.signature.timestamp, @payload.signature.token].join
|
49
45
|
end
|
50
46
|
end
|
51
47
|
end
|
@@ -4,6 +4,24 @@ module Mailgun
|
|
4
4
|
module Tracking
|
5
5
|
# Utility methods.
|
6
6
|
module Util
|
7
|
+
class << self
|
8
|
+
# Converts a hash of fields or an array of hashes into a {Payload}.
|
9
|
+
#
|
10
|
+
# @param data [Hash, Array] Hash of fields and values to be converted into a Payload.
|
11
|
+
#
|
12
|
+
# @return [Mailgun::Tracking::Payload, Array(Mailgun::Tracking::Payload)]
|
13
|
+
def convert_to_payload_object(data)
|
14
|
+
case data
|
15
|
+
when Array
|
16
|
+
data.map { |item| convert_to_payload_object(item) }
|
17
|
+
when Hash
|
18
|
+
Payload.new(data)
|
19
|
+
else
|
20
|
+
data
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
7
25
|
class << self
|
8
26
|
# Returns a new hash with all keys converted to symbols in downcase.
|
9
27
|
#
|
@@ -1,53 +1,11 @@
|
|
1
|
-
# Logfile created on 2019-
|
2
|
-
I, [2019-
|
3
|
-
I, [2019-
|
4
|
-
I, [2019-
|
5
|
-
I, [2019-
|
6
|
-
I, [2019-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
I, [2019-10-16T17:00:24.064195 #12268] INFO -- : Started POST "/mailgun" for 127.0.0.1 at 2019-10-16 17:00:24 +0300
|
13
|
-
F, [2019-10-16T17:00:24.066858 #12268] FATAL -- :
|
14
|
-
NameError (undefined local variable or method `byebug' for #<Mailgun::Tracking::Middleware:0x00007fa1e51220d0>):
|
15
|
-
|
16
|
-
lib/mailgun/tracking/middleware.rb:25:in `_call'
|
17
|
-
lib/mailgun/tracking/middleware.rb:20:in `call'
|
18
|
-
I, [2019-10-16T17:00:40.123028 #12291] INFO -- : Started POST "/mailgun" for 127.0.0.1 at 2019-10-16 17:00:40 +0300
|
19
|
-
F, [2019-10-16T17:00:40.132743 #12291] FATAL -- :
|
20
|
-
LoadError (cannot load such file -- byebug):
|
21
|
-
|
22
|
-
lib/mailgun/tracking/middleware.rb:25:in `_call'
|
23
|
-
lib/mailgun/tracking/middleware.rb:20:in `call'
|
24
|
-
I, [2019-10-16T17:00:40.154766 #12291] INFO -- : Started POST "/mailgun" for 127.0.0.1 at 2019-10-16 17:00:40 +0300
|
25
|
-
F, [2019-10-16T17:00:40.157648 #12291] FATAL -- :
|
26
|
-
LoadError (cannot load such file -- byebug):
|
27
|
-
|
28
|
-
lib/mailgun/tracking/middleware.rb:25:in `_call'
|
29
|
-
lib/mailgun/tracking/middleware.rb:20:in `call'
|
30
|
-
I, [2019-10-16T17:01:20.206080 #12320] INFO -- : Started POST "/mailgun" for 127.0.0.1 at 2019-10-16 17:01:20 +0300
|
31
|
-
F, [2019-10-16T17:18:16.030799 #12320] FATAL -- :
|
32
|
-
SystemExit (exit):
|
33
|
-
|
34
|
-
(byebug):1:in `exit'
|
35
|
-
(byebug):1:in `_call'
|
36
|
-
lib/mailgun/tracking/middleware.rb:29:in `_call'
|
37
|
-
lib/mailgun/tracking/middleware.rb:22:in `call'
|
38
|
-
I, [2019-10-16T17:18:16.198543 #12320] INFO -- : Started POST "/mailgun" for 127.0.0.1 at 2019-10-16 17:18:16 +0300
|
39
|
-
I, [2019-11-07T18:53:57.134697 #53967] INFO -- : Started POST "/mailgun" for 127.0.0.1 at 2019-11-07 18:53:57 +0200
|
40
|
-
I, [2019-11-07T18:53:57.158453 #53967] INFO -- : Started POST "/mailgun" for 127.0.0.1 at 2019-11-07 18:53:57 +0200
|
41
|
-
F, [2019-11-07T18:53:57.160416 #53967] FATAL -- :
|
42
|
-
KeyError (key not found: "event"):
|
43
|
-
lib/mailgun/tracking/payload/legacy.rb:17:in `fetch'
|
44
|
-
lib/mailgun/tracking/payload/legacy.rb:17:in `event'
|
45
|
-
lib/mailgun/tracking/middleware.rb:60:in `handle_event'
|
46
|
-
lib/mailgun/tracking/middleware.rb:43:in `call!'
|
47
|
-
lib/mailgun/tracking/middleware.rb:20:in `call'
|
48
|
-
|
49
|
-
|
50
|
-
I, [2019-11-07T18:55:50.036561 #53989] INFO -- : Started POST "/mailgun" for 127.0.0.1 at 2019-11-07 18:55:50 +0200
|
51
|
-
I, [2019-11-07T18:55:50.076942 #53989] INFO -- : Started POST "/mailgun" for 127.0.0.1 at 2019-11-07 18:55:50 +0200
|
52
|
-
I, [2019-11-07T18:56:17.648709 #53999] INFO -- : Started POST "/mailgun" for 127.0.0.1 at 2019-11-07 18:56:17 +0200
|
53
|
-
I, [2019-11-07T18:56:17.662346 #53999] INFO -- : Started POST "/mailgun" for 127.0.0.1 at 2019-11-07 18:56:17 +0200
|
1
|
+
# Logfile created on 2019-11-18 19:44:22 +0200 by logger.rb/66358
|
2
|
+
I, [2019-11-18T19:44:22.794652 #78850] INFO -- : Started POST "/mailgun" for 127.0.0.1 at 2019-11-18 19:44:22 +0200
|
3
|
+
I, [2019-11-18T19:44:22.822729 #78850] INFO -- : Started POST "/mailgun" for 127.0.0.1 at 2019-11-18 19:44:22 +0200
|
4
|
+
I, [2019-11-18T19:45:24.283699 #78864] INFO -- : Started POST "/mailgun" for 127.0.0.1 at 2019-11-18 19:45:24 +0200
|
5
|
+
I, [2019-11-18T19:45:24.287326 #78864] INFO -- : Started POST "/mailgun" for 127.0.0.1 at 2019-11-18 19:45:24 +0200
|
6
|
+
I, [2019-11-18T19:45:51.702415 #78878] INFO -- : Started POST "/mailgun" for 127.0.0.1 at 2019-11-18 19:45:51 +0200
|
7
|
+
I, [2019-11-18T19:45:51.710237 #78878] INFO -- : Started POST "/mailgun" for 127.0.0.1 at 2019-11-18 19:45:51 +0200
|
8
|
+
I, [2019-11-18T19:46:04.908573 #78887] INFO -- : Started POST "/mailgun" for 127.0.0.1 at 2019-11-18 19:46:04 +0200
|
9
|
+
I, [2019-11-18T19:46:04.912208 #78887] INFO -- : Started POST "/mailgun" for 127.0.0.1 at 2019-11-18 19:46:04 +0200
|
10
|
+
I, [2019-11-18T19:58:57.597641 #78976] INFO -- : Started POST "/mailgun" for 127.0.0.1 at 2019-11-18 19:58:57 +0200
|
11
|
+
I, [2019-11-18T19:58:57.608212 #78976] INFO -- : Started POST "/mailgun" for 127.0.0.1 at 2019-11-18 19:58:57 +0200
|
@@ -1,8 +1,8 @@
|
|
1
1
|
{
|
2
2
|
"signature": {
|
3
|
-
"timestamp": "
|
4
|
-
"token": "
|
5
|
-
"signature": "
|
3
|
+
"timestamp": "1499697910",
|
4
|
+
"token": "b5751a49a024483da8d41c3684f98b8f",
|
5
|
+
"signature": "374e0b1a3deeb57318c783d43ff71093fbf26406a452761dab91bf346a93b49e"
|
6
6
|
},
|
7
7
|
"event-data": {
|
8
8
|
"tags": [
|
@@ -9,7 +9,7 @@ RSpec.describe Mailgun::Tracking::Middleware do
|
|
9
9
|
instance_double(Mailgun::Tracking::Request, payload: payload, media_type: 'application/x-www-form-urlencoded')
|
10
10
|
end
|
11
11
|
let(:env) { env_for('http://localhost:3000') }
|
12
|
-
let(:payload) {
|
12
|
+
let(:payload) { Mailgun::Tracking::Payload.new('event-data' => { event: 'delivered' }) }
|
13
13
|
|
14
14
|
before do
|
15
15
|
allow(Mailgun::Tracking).to receive(:notifier).and_return(notifier)
|
@@ -32,7 +32,7 @@ RSpec.describe Mailgun::Tracking::Middleware do
|
|
32
32
|
end
|
33
33
|
|
34
34
|
context 'when request is respond to the specified URL and the signature comparison is unsuccessful' do
|
35
|
-
let(:params) { fixture('
|
35
|
+
let(:params) { fixture('delivered.json') }
|
36
36
|
|
37
37
|
before do
|
38
38
|
allow(notifier).to receive(:broadcast).and_raise(Mailgun::Tracking::InvalidSignature)
|
@@ -49,7 +49,7 @@ RSpec.describe Mailgun::Tracking::Middleware do
|
|
49
49
|
end
|
50
50
|
|
51
51
|
context 'when request is respond to the specified URL and the signature comparison is successful' do
|
52
|
-
let(:params) { fixture('
|
52
|
+
let(:params) { fixture('delivered.json') }
|
53
53
|
|
54
54
|
before do
|
55
55
|
allow(request).to receive(:mailgun_tracking?).and_return(true)
|
@@ -47,7 +47,7 @@ RSpec.describe Mailgun::Tracking::Notifier do
|
|
47
47
|
end
|
48
48
|
|
49
49
|
describe '#broadcast' do
|
50
|
-
let(:payload) {
|
50
|
+
let(:payload) { Mailgun::Tracking::Payload.new(fixture('delivered.json')) }
|
51
51
|
|
52
52
|
before do
|
53
53
|
allow(Mailgun::Tracking::Signature).to receive(:verify!)
|
@@ -60,7 +60,7 @@ RSpec.describe Mailgun::Tracking::Notifier do
|
|
60
60
|
end
|
61
61
|
|
62
62
|
it 'broadcasts an event' do
|
63
|
-
expect(listener).to have_received(:broadcast).with(:delivered,
|
63
|
+
expect(listener).to have_received(:broadcast).with(:delivered, payload)
|
64
64
|
end
|
65
65
|
end
|
66
66
|
end
|
@@ -1,37 +1,73 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
RSpec.describe Mailgun::Tracking::Payload do
|
4
|
-
subject(:payload) { described_class.new(
|
4
|
+
subject(:payload) { described_class.new(values) }
|
5
5
|
|
6
|
-
|
6
|
+
describe '#==' do
|
7
|
+
let(:values) { { 'foo' => 'bar' } }
|
7
8
|
|
8
|
-
|
9
|
-
it '
|
10
|
-
|
11
|
-
end
|
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
12
|
end
|
13
13
|
|
14
|
-
describe '#
|
15
|
-
|
16
|
-
|
17
|
-
|
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?) }
|
18
20
|
end
|
19
21
|
|
20
|
-
describe '#
|
21
|
-
|
22
|
-
|
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>')
|
23
27
|
end
|
24
28
|
end
|
25
29
|
|
26
|
-
describe '#
|
27
|
-
|
28
|
-
|
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
|
+
)
|
29
53
|
end
|
30
54
|
end
|
31
55
|
|
32
|
-
describe '#
|
33
|
-
|
34
|
-
|
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
|
+
))
|
35
71
|
end
|
36
72
|
end
|
37
73
|
end
|
@@ -4,40 +4,42 @@ RSpec.describe Mailgun::Tracking::Request do
|
|
4
4
|
subject(:request) { described_class.new(env) }
|
5
5
|
|
6
6
|
describe '#mailgun_tracking?' do
|
7
|
-
context 'when a request to
|
8
|
-
let(:env) { env_for('http://localhost:3000/mailgun', method: :
|
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
9
|
|
10
|
-
it { is_expected.
|
10
|
+
it { is_expected.not_to be_mailgun_tracking }
|
11
11
|
end
|
12
12
|
|
13
|
-
context 'when a request to
|
14
|
-
let(:env)
|
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
|
15
17
|
|
16
18
|
it { is_expected.not_to be_mailgun_tracking }
|
17
19
|
end
|
18
20
|
|
19
21
|
context 'when the request is not to the endpoint' do
|
20
|
-
let(:env)
|
22
|
+
let(:env) do
|
23
|
+
env_for('http://localhost:3000/_mailgun', method: :post, 'CONTENT_TYPE' => 'application/json; charset=utf-8')
|
24
|
+
end
|
21
25
|
|
22
26
|
it { is_expected.not_to be_mailgun_tracking }
|
23
27
|
end
|
24
|
-
end
|
25
28
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
it 'returns legacy payload' do
|
31
|
-
expect(request.payload).to be_an_instance_of(Mailgun::Tracking::Payload::Legacy)
|
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
32
|
end
|
33
|
+
|
34
|
+
it { is_expected.to be_mailgun_tracking }
|
33
35
|
end
|
36
|
+
end
|
34
37
|
|
35
|
-
|
36
|
-
|
38
|
+
describe '#payload' do
|
39
|
+
let(:env) { env_for('http://localhost:3000/mailgun', method: :post, params: { 'signature' => {} }) }
|
37
40
|
|
38
|
-
|
39
|
-
|
40
|
-
end
|
41
|
+
it 'returns payload' do
|
42
|
+
expect(request.payload).to be_an_instance_of(Mailgun::Tracking::Payload)
|
41
43
|
end
|
42
44
|
end
|
43
45
|
|
@@ -6,11 +6,12 @@ RSpec.describe Mailgun::Tracking::Signature do
|
|
6
6
|
describe '.verify!' do
|
7
7
|
context 'when the signature comparison is successful' do
|
8
8
|
let(:payload) do
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
Mailgun::Tracking::Payload.new(
|
10
|
+
signature: {
|
11
|
+
timestamp: '1499697910',
|
12
|
+
token: 'b5751a49a024483da8d41c3684f98b8f',
|
13
|
+
signature: '374e0b1a3deeb57318c783d43ff71093fbf26406a452761dab91bf346a93b49e'
|
14
|
+
}
|
14
15
|
)
|
15
16
|
end
|
16
17
|
|
@@ -19,11 +20,12 @@ RSpec.describe Mailgun::Tracking::Signature do
|
|
19
20
|
|
20
21
|
context 'when the signature comparison is unsuccessful' do
|
21
22
|
let(:payload) do
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
Mailgun::Tracking::Payload.new(
|
24
|
+
signature: {
|
25
|
+
timestamp: '',
|
26
|
+
token: 'b5751a49a024483da8d41c3684f98b8f',
|
27
|
+
signature: '374e0b1a3deeb57318c783d43ff71093fbf26406a452761dab91bf346a93b49e'
|
28
|
+
}
|
27
29
|
)
|
28
30
|
end
|
29
31
|
|
@@ -34,11 +36,12 @@ RSpec.describe Mailgun::Tracking::Signature do
|
|
34
36
|
describe '#valid?' do
|
35
37
|
context 'when the signature comparison is successful' do
|
36
38
|
let(:payload) do
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
Mailgun::Tracking::Payload.new(
|
40
|
+
signature: {
|
41
|
+
timestamp: '1499697910',
|
42
|
+
token: 'b5751a49a024483da8d41c3684f98b8f',
|
43
|
+
signature: '374e0b1a3deeb57318c783d43ff71093fbf26406a452761dab91bf346a93b49e'
|
44
|
+
}
|
42
45
|
)
|
43
46
|
end
|
44
47
|
|
@@ -47,11 +50,12 @@ RSpec.describe Mailgun::Tracking::Signature do
|
|
47
50
|
|
48
51
|
context 'when the signature comparison is unsuccessful' do
|
49
52
|
let(:payload) do
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
53
|
+
Mailgun::Tracking::Payload.new(
|
54
|
+
signature: {
|
55
|
+
timestamp: '',
|
56
|
+
token: 'b5751a49a024483da8d41c3684f98b8f',
|
57
|
+
signature: '374e0b1a3deeb57318c783d43ff71093fbf26406a452761dab91bf346a93b49e'
|
58
|
+
}
|
55
59
|
)
|
56
60
|
end
|
57
61
|
|
@@ -6,7 +6,7 @@ end
|
|
6
6
|
|
7
7
|
RSpec.shared_examples 'acts as rack' do
|
8
8
|
let(:app) { Dummy::Application }
|
9
|
-
let(:payload) { fixture('
|
9
|
+
let(:payload) { fixture('delivered.json') }
|
10
10
|
let(:delivered) { instance_double(Delivered) }
|
11
11
|
|
12
12
|
before do
|
@@ -23,15 +23,15 @@ RSpec.shared_examples 'acts as rack' do
|
|
23
23
|
end
|
24
24
|
|
25
25
|
it do
|
26
|
-
post('/mailgun', payload)
|
27
|
-
expect(delivered).to have_received(:call).with(payload).twice
|
26
|
+
post('/mailgun', payload.to_json, 'CONTENT_TYPE' => 'application/json')
|
27
|
+
expect(delivered).to have_received(:call).with(Mailgun::Tracking::Payload.new(payload)).twice
|
28
28
|
end
|
29
29
|
|
30
30
|
context 'when the signature comparison is unsuccessful' do
|
31
|
-
before { payload['timestamp'] = '' }
|
31
|
+
before { payload['signature']['timestamp'] = '' }
|
32
32
|
|
33
33
|
it do
|
34
|
-
post('/mailgun', payload)
|
34
|
+
post('/mailgun', payload.to_json, 'CONTENT_TYPE' => 'application/json')
|
35
35
|
expect(last_response).to be_bad_request
|
36
36
|
end
|
37
37
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mailgun-tracking
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Artem Chubchenko
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-11-
|
11
|
+
date: 2019-11-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -166,7 +166,6 @@ files:
|
|
166
166
|
- lib/mailgun/tracking/middleware.rb
|
167
167
|
- lib/mailgun/tracking/notifier.rb
|
168
168
|
- lib/mailgun/tracking/payload.rb
|
169
|
-
- lib/mailgun/tracking/payload/legacy.rb
|
170
169
|
- lib/mailgun/tracking/railtie.rb
|
171
170
|
- lib/mailgun/tracking/request.rb
|
172
171
|
- lib/mailgun/tracking/signature.rb
|
@@ -175,12 +174,13 @@ files:
|
|
175
174
|
- lib/mailgun/tracking/subscriber/evented.rb
|
176
175
|
- lib/mailgun/tracking/util.rb
|
177
176
|
- lib/mailgun/tracking/version.rb
|
177
|
+
- spec/dummy/hanami/application.rb
|
178
178
|
- spec/dummy/rack/application.rb
|
179
179
|
- spec/dummy/rails/application.rb
|
180
180
|
- spec/dummy/rails/logs/test.log
|
181
181
|
- spec/dummy/sinatra/application.rb
|
182
182
|
- spec/fixtures/delivered.json
|
183
|
-
- spec/
|
183
|
+
- spec/integration/hanami/hanami_spec.rb
|
184
184
|
- spec/integration/rack/rack_spec.rb
|
185
185
|
- spec/integration/rails/rails_spec.rb
|
186
186
|
- spec/integration/sinatra/sinatra_spec.rb
|
@@ -188,7 +188,6 @@ files:
|
|
188
188
|
- spec/mailgun/tracking/listener_spec.rb
|
189
189
|
- spec/mailgun/tracking/middleware_spec.rb
|
190
190
|
- spec/mailgun/tracking/notifier_spec.rb
|
191
|
-
- spec/mailgun/tracking/payload/legacy_spec.rb
|
192
191
|
- spec/mailgun/tracking/payload_spec.rb
|
193
192
|
- spec/mailgun/tracking/request_spec.rb
|
194
193
|
- spec/mailgun/tracking/signature_spec.rb
|
@@ -232,23 +231,23 @@ summary: Integration with Mailgun Webhooks
|
|
232
231
|
test_files:
|
233
232
|
- spec/spec_helper.rb
|
234
233
|
- spec/dummy/sinatra/application.rb
|
234
|
+
- spec/dummy/hanami/application.rb
|
235
235
|
- spec/dummy/rack/application.rb
|
236
236
|
- spec/dummy/rails/application.rb
|
237
237
|
- spec/dummy/rails/logs/test.log
|
238
238
|
- spec/integration/sinatra/sinatra_spec.rb
|
239
|
+
- spec/integration/hanami/hanami_spec.rb
|
239
240
|
- spec/integration/rack/rack_spec.rb
|
240
241
|
- spec/integration/rails/rails_spec.rb
|
241
242
|
- spec/support/rack_helpers.rb
|
242
243
|
- spec/support/fixture.rb
|
243
244
|
- spec/support/shared_examples/integration/acts_as_rack.rb
|
244
245
|
- spec/support/shared_examples/subscriber.rb
|
245
|
-
- spec/fixtures/legacy/delivered.json
|
246
246
|
- spec/fixtures/delivered.json
|
247
247
|
- spec/mailgun/tracking_spec.rb
|
248
248
|
- spec/mailgun/tracking/configuration_spec.rb
|
249
249
|
- spec/mailgun/tracking/version_spec.rb
|
250
250
|
- spec/mailgun/tracking/signature_spec.rb
|
251
|
-
- spec/mailgun/tracking/payload/legacy_spec.rb
|
252
251
|
- spec/mailgun/tracking/notifier_spec.rb
|
253
252
|
- spec/mailgun/tracking/subscriber_spec.rb
|
254
253
|
- spec/mailgun/tracking/middleware_spec.rb
|
@@ -1,38 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Mailgun
|
4
|
-
module Tracking
|
5
|
-
class Payload
|
6
|
-
# Legacy payload object.
|
7
|
-
class Legacy
|
8
|
-
def initialize(options = {})
|
9
|
-
@options = options
|
10
|
-
warn(<<~DEPRECATION)
|
11
|
-
[Mailgun::Tracking] The Legacy class refers to a previous version of the API
|
12
|
-
which is deprecated and it will be removed in the next major version.
|
13
|
-
DEPRECATION
|
14
|
-
end
|
15
|
-
|
16
|
-
def body
|
17
|
-
@options
|
18
|
-
end
|
19
|
-
|
20
|
-
def event
|
21
|
-
@event ||= @options.fetch('event')
|
22
|
-
end
|
23
|
-
|
24
|
-
def token
|
25
|
-
@token ||= @options.fetch('token')
|
26
|
-
end
|
27
|
-
|
28
|
-
def timestamp
|
29
|
-
@timestamp ||= @options.fetch('timestamp')
|
30
|
-
end
|
31
|
-
|
32
|
-
def signature
|
33
|
-
@signature ||= @options.fetch('signature')
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
@@ -1,13 +0,0 @@
|
|
1
|
-
{
|
2
|
-
"timestamp": "1499697910",
|
3
|
-
"token": "b5751a49a024483da8d41c3684f98b8f",
|
4
|
-
"signature": "374e0b1a3deeb57318c783d43ff71093fbf26406a452761dab91bf346a93b49e",
|
5
|
-
"domain": "mail.example.com",
|
6
|
-
"my_var_1": "Mailgun Variable #1",
|
7
|
-
"my-var-2": "awesome",
|
8
|
-
"message-headers": "[[\"Received\", \"by luna.mailgun.net with SMTP mgrt 8734663311733; Fri, 03 May 2013 18:26:27 +0000\"], [\"Content-Type\", [\"multipart/alternative\", {\"boundary\": \"eb663d73ae0a4d6c9153cc0aec8b7520\"}]], [\"Mime-Version\", \"1.0\"], [\"Subject\", \"Test deliver webhook\"], [\"From\", \"Bob <bob@sandboxfb0641fa1b904bb28ba00cba62dca7f1.mailgun.org>\"], [\"To\", \"Alice <alice@example.com>\"], [\"Message-Id\", \"<20130503182626.18666.16540@sandboxfb0641fa1b904bb28ba00cba62dca7f1.mailgun.org>\"], [\"X-Mailgun-Variables\", \"{\\\"my_var_1\\\": \\\"Mailgun Variable #1\\\", \\\"my-var-2\\\": \\\"awesome\\\"}\"], [\"Date\", \"Fri, 03 May 2013 18:26:27 +0000\"], [\"Sender\", \"bob@sandboxfb0641fa1b904bb28ba00cba62dca7f1.mailgun.org\"]]",
|
9
|
-
"Message-Id": "<20160329071939.35138.9413.6915422C@mail.example.com>",
|
10
|
-
"recipient": "alice@example.com",
|
11
|
-
"event": "delivered",
|
12
|
-
"body-plain": ""
|
13
|
-
}
|
@@ -1,37 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe Mailgun::Tracking::Payload::Legacy do
|
4
|
-
subject(:legacy) { described_class.new(options) }
|
5
|
-
|
6
|
-
let(:options) { fixture('legacy/delivered.json') }
|
7
|
-
|
8
|
-
describe '#body' do
|
9
|
-
it 'returns body' do
|
10
|
-
expect(legacy.body).to eq(options)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
describe '#event' do
|
15
|
-
it 'returns event' do
|
16
|
-
expect(legacy.event).to eq(options.fetch('event'))
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
describe '#token' do
|
21
|
-
it 'returns token' do
|
22
|
-
expect(legacy.token).to eq(options.fetch('token'))
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
describe '#timestamp' do
|
27
|
-
it 'returns timestamp' do
|
28
|
-
expect(legacy.timestamp).to eq(options.fetch('timestamp'))
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
describe '#signature' do
|
33
|
-
it 'returns signature' do
|
34
|
-
expect(legacy.signature).to eq(options.fetch('signature'))
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|