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 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