mailgun-tracking 1.1.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e61daca288034107cca6ca891a80fa601016a555fb1fb2d07ab9b9755d52a12e
4
- data.tar.gz: 6bc9cac63844b3190418dbf6b2fa14964bfbe372bf6eb23ef55ea6d6aaf8b739
3
+ metadata.gz: cffbc8bee4968f3230841a2a75da150ef6d17bb2e4d860d05897aef61e55f2e1
4
+ data.tar.gz: be71a2d5a143a49d4ada8d050ffd402e09917e31cacc83da954654adfeae358d
5
5
  SHA512:
6
- metadata.gz: 6de25bb55ff82c27c8420111acd1c72d4ee390ead819abc2631b1196e6d37ed62637023ed5048287f0ab399b23d1b942b626d8de1c4c9c58e7d3b2cc69b94ad0
7
- data.tar.gz: dcd30f8087f1fd233b33a20d843ef1517ce1678d51b79bd8570574dabca01ab8963f944501f3e3e3cff1a6f268b1f37329112e7c3392cef3e516bff2fbc7cd09
6
+ metadata.gz: 4451ebb90204a5a887ad09afaac2187b0e86591e880a1bf944a7c671056c93999a8d6ec89920c38928a230e0b86a161eb472f0d57e320085865f12f33dcde843
7
+ data.tar.gz: efda62e3aa07d4fbe150dbfb48d2f2cbaa6204b49de9f0bb7278e473723ed85515dc56cfe84b503bf9c94b18a70cdfec5e94642ed5b927942381852babceb37e
@@ -1,18 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'mailgun/tracking/configuration'
4
- require 'mailgun/tracking/exceptions'
5
- require 'mailgun/tracking/listener'
6
- require 'mailgun/tracking/middleware'
7
- require 'mailgun/tracking/notifier'
8
- require 'mailgun/tracking/payload'
9
- require 'mailgun/tracking/payload/legacy'
10
- require 'mailgun/tracking/signature'
11
- require 'mailgun/tracking/subscriber'
12
- require 'mailgun/tracking/util'
13
- require 'mailgun/tracking/version'
14
- require 'mailgun/tracking/railtie' if defined?(Rails)
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
@@ -49,7 +49,7 @@ module Mailgun
49
49
  # @return [NilClass]
50
50
  def broadcast(event, payload)
51
51
  Signature.verify!(payload)
52
- listener.broadcast(event, payload.body)
52
+ listener.broadcast(event, payload)
53
53
  end
54
54
 
55
55
  private
@@ -1,44 +1,96 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'payload/legacy'
3
+ require 'set'
4
4
 
5
5
  module Mailgun
6
6
  module Tracking
7
7
  # Payload object.
8
8
  class Payload
9
- def initialize(options = {})
10
- @options = options
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
- def body
14
- @options
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
- def event
18
- @event ||= __event_data.fetch('event')
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
- def token
22
- @token ||= __signature.fetch('token')
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
- def timestamp
26
- @timestamp ||= __signature.fetch('timestamp')
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
- def signature
30
- @signature ||= __signature.fetch('signature')
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
- def __signature
36
- @__signature ||= @options.fetch('signature')
72
+ # @return [Class]
73
+ def __metaclass__
74
+ class << self
75
+ self
76
+ end
37
77
  end
38
78
 
39
- def __event_data
40
- @__event_data ||= @options.fetch('event-data')
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, Mailgun::Tracking::Payload::Legacy]
19
+ # @return [Mailgun::Tracking::Payload]
19
20
  def payload
20
- @payload ||= begin
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
- get_header(FORM_HASH)
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 [Hash]
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 [Hash]
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 == OpenSSL::HMAC.hexdigest(digest, Configuration.instance.api_key, data)
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
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'mailgun/tracking/subscriber/all_messages'
4
- require 'mailgun/tracking/subscriber/evented'
3
+ require_relative 'subscriber/all_messages'
4
+ require_relative 'subscriber/evented'
5
5
 
6
6
  module Mailgun
7
7
  module Tracking
@@ -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
  #
@@ -12,14 +12,14 @@ module Mailgun
12
12
  #
13
13
  # @return [Integer]
14
14
  def major
15
- 1
15
+ 2
16
16
  end
17
17
 
18
18
  # Minor version.
19
19
  #
20
20
  # @return [Integer]
21
21
  def minor
22
- 1
22
+ 0
23
23
  end
24
24
 
25
25
  # Patch version.
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'hanami'
4
+
5
+ Hanami::Environment.class_eval do
6
+ def root
7
+ @root ||= Pathname.new(__dir__)
8
+ end
9
+ end
10
+
11
+ Hanami.configure do
12
+ middleware.use(Mailgun::Tracking::Middleware)
13
+ end
@@ -1,53 +1,11 @@
1
- # Logfile created on 2019-10-16 14:23:57 +0300 by logger.rb/66358
2
- I, [2019-10-16T14:23:57.963086 #9298] INFO -- : Started POST "/mailgun" for 127.0.0.1 at 2019-10-16 14:23:57 +0300
3
- I, [2019-10-16T14:23:57.966124 #9298] INFO -- : Started POST "/mailgun" for 127.0.0.1 at 2019-10-16 14:23:57 +0300
4
- I, [2019-10-16T16:59:48.896283 #12252] INFO -- : Started POST "/mailgun" for 127.0.0.1 at 2019-10-16 16:59:48 +0300
5
- I, [2019-10-16T16:59:48.901335 #12252] INFO -- : Started POST "/mailgun" for 127.0.0.1 at 2019-10-16 16:59:48 +0300
6
- I, [2019-10-16T17:00:24.034817 #12268] INFO -- : Started POST "/mailgun" for 127.0.0.1 at 2019-10-16 17:00:24 +0300
7
- F, [2019-10-16T17:00:24.036968 #12268] FATAL -- :
8
- NameError (undefined local variable or method `byebug' for #<Mailgun::Tracking::Middleware:0x00007fa1e4122950>):
9
-
10
- lib/mailgun/tracking/middleware.rb:25:in `_call'
11
- lib/mailgun/tracking/middleware.rb:20:in `call'
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": "1572632560",
4
- "token": "e71ad7ff8d6f43b62d31615083bde85fb55e4e2b5121cb1239",
5
- "signature": "9d21d1689889ec5f4ffa3c7e5e6598c8450c3eada8f6cc03e47b2342a9c9da61"
3
+ "timestamp": "1499697910",
4
+ "token": "b5751a49a024483da8d41c3684f98b8f",
5
+ "signature": "374e0b1a3deeb57318c783d43ff71093fbf26406a452761dab91bf346a93b49e"
6
6
  },
7
7
  "event-data": {
8
8
  "tags": [
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dummy/hanami/application'
4
+
5
+ RSpec.describe 'Hanami', type: :integration do
6
+ it_behaves_like 'acts as rack' do
7
+ let(:app) { Hanami.app }
8
+ end
9
+ end
@@ -24,7 +24,7 @@ RSpec.describe Mailgun::Tracking::Listener do
24
24
  end
25
25
 
26
26
  describe '#broadcast' do
27
- let(:payload) { fixture('legacy/delivered.json') }
27
+ let(:payload) { fixture('delivered.json') }
28
28
 
29
29
  before do
30
30
  allow(subscriber).to receive(:call)
@@ -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) { instance_double(Mailgun::Tracking::Payload::Legacy, event: 'delivered') }
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('legacy/delivered.json') }
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('legacy/delivered.json') }
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) { instance_double(Mailgun::Tracking::Payload, body: fixture('legacy/delivered.json')) }
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, fixture('legacy/delivered.json'))
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(options) }
4
+ subject(:payload) { described_class.new(values) }
5
5
 
6
- let(:options) { fixture('delivered.json') }
6
+ describe '#==' do
7
+ let(:values) { { 'foo' => 'bar' } }
7
8
 
8
- describe '#body' do
9
- it 'returns body' do
10
- expect(payload.body).to eq(options)
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 '#event' do
15
- it 'returns event' do
16
- expect(payload.event).to eq(options.fetch('event-data').fetch('event'))
17
- end
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 '#token' do
21
- it 'returns token' do
22
- expect(payload.token).to eq(options.fetch('signature').fetch('token'))
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 '#timestamp' do
27
- it 'returns timestamp' do
28
- expect(payload.timestamp).to eq(options.fetch('signature').fetch('timestamp'))
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 '#signature' do
33
- it 'returns signature' do
34
- expect(payload.signature).to eq(options.fetch('signature').fetch('signature'))
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 a endpoint with a POST method' do
8
- let(:env) { env_for('http://localhost:3000/mailgun', method: :post) }
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.to be_mailgun_tracking }
10
+ it { is_expected.not_to be_mailgun_tracking }
11
11
  end
12
12
 
13
- context 'when a request to a endpoint with a POST method' do
14
- let(:env) { env_for('http://localhost:3000/mailgun', method: :get) }
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) { env_for('http://localhost:3000') }
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
- describe '#payload' do
27
- context 'when the parameters contain the timestamp' do
28
- let(:env) { env_for('http://localhost:3000/mailgun', method: :post, params: { 'timestamp' => '1572632560' }) }
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
- context 'when the parameters do not contain the timestamp' do
36
- let(:env) { env_for('http://localhost:3000/mailgun', method: :post, params: { 'signature' => {} }) }
38
+ describe '#payload' do
39
+ let(:env) { env_for('http://localhost:3000/mailgun', method: :post, params: { 'signature' => {} }) }
37
40
 
38
- it 'returns payload' do
39
- expect(request.payload).to be_an_instance_of(Mailgun::Tracking::Payload)
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
- instance_double(
10
- Mailgun::Tracking::Payload,
11
- timestamp: '1499697910',
12
- token: 'b5751a49a024483da8d41c3684f98b8f',
13
- signature: '374e0b1a3deeb57318c783d43ff71093fbf26406a452761dab91bf346a93b49e'
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
- instance_double(
23
- Mailgun::Tracking::Payload,
24
- timestamp: '',
25
- token: 'b5751a49a024483da8d41c3684f98b8f',
26
- signature: '374e0b1a3deeb57318c783d43ff71093fbf26406a452761dab91bf346a93b49e'
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
- instance_double(
38
- Mailgun::Tracking::Payload,
39
- timestamp: '1499697910',
40
- token: 'b5751a49a024483da8d41c3684f98b8f',
41
- signature: '374e0b1a3deeb57318c783d43ff71093fbf26406a452761dab91bf346a93b49e'
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
- instance_double(
51
- Mailgun::Tracking::Payload,
52
- timestamp: '',
53
- token: 'b5751a49a024483da8d41c3684f98b8f',
54
- signature: '374e0b1a3deeb57318c783d43ff71093fbf26406a452761dab91bf346a93b49e'
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('legacy/delivered.json') }
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
@@ -3,7 +3,7 @@
3
3
  RSpec.shared_examples 'subscriber' do
4
4
  describe '#call' do
5
5
  let(:callable) { proc {} }
6
- let(:payload) { fixture('legacy/delivered.json') }
6
+ let(:payload) { fixture('delivered.json') }
7
7
 
8
8
  before { allow(callable).to receive(:call).with(payload) }
9
9
 
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: 1.1.0
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-15 00:00:00.000000000 Z
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/fixtures/legacy/delivered.json
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