mailgun-tracking 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/mailgun/tracking/templates/mailgun_tracking.rb.erb +1 -1
  3. data/lib/mailgun-tracking.rb +3 -0
  4. data/lib/mailgun/tracking.rb +65 -33
  5. data/lib/mailgun/tracking/auth.rb +52 -0
  6. data/lib/mailgun/tracking/configuration.rb +5 -9
  7. data/lib/mailgun/tracking/fanout.rb +18 -0
  8. data/lib/mailgun/tracking/middleware.rb +10 -47
  9. data/lib/mailgun/tracking/railtie.rb +2 -2
  10. data/lib/mailgun/tracking/version.rb +8 -7
  11. data/spec/dummy/logs/test.log +77 -0
  12. data/spec/dummy/rails/logs/test.log +19 -11
  13. data/spec/mailgun/tracking/auth_spec.rb +31 -0
  14. data/spec/mailgun/tracking/middleware_spec.rb +43 -51
  15. data/spec/mailgun/tracking_spec.rb +11 -4
  16. data/spec/support/shared_examples/integration/acts_as_rack.rb +6 -4
  17. metadata +18 -53
  18. data/lib/mailgun/tracking/exceptions.rb +0 -11
  19. data/lib/mailgun/tracking/listener.rb +0 -55
  20. data/lib/mailgun/tracking/notifier.rb +0 -61
  21. data/lib/mailgun/tracking/payload.rb +0 -96
  22. data/lib/mailgun/tracking/request.rb +0 -47
  23. data/lib/mailgun/tracking/signature.rb +0 -48
  24. data/lib/mailgun/tracking/subscriber.rb +0 -26
  25. data/lib/mailgun/tracking/subscriber/all_messages.rb +0 -37
  26. data/lib/mailgun/tracking/subscriber/evented.rb +0 -40
  27. data/lib/mailgun/tracking/util.rb +0 -67
  28. data/spec/mailgun/tracking/listener_spec.rb +0 -48
  29. data/spec/mailgun/tracking/notifier_spec.rb +0 -66
  30. data/spec/mailgun/tracking/payload_spec.rb +0 -73
  31. data/spec/mailgun/tracking/request_spec.rb +0 -63
  32. data/spec/mailgun/tracking/signature_spec.rb +0 -65
  33. data/spec/mailgun/tracking/subscriber/all_messages_spec.rb +0 -13
  34. data/spec/mailgun/tracking/subscriber/evented_spec.rb +0 -14
  35. data/spec/mailgun/tracking/subscriber_spec.rb +0 -15
  36. data/spec/mailgun/tracking/util_spec.rb +0 -155
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Mailgun
4
- module Tracking
5
- # Wraps the {Listener} which gives a friendlier way to subscribe or broadcast.
6
- class Notifier
7
- # Initializes a new Notifier object.
8
- #
9
- # @param listener [Mailgun::Tracking::Listener] Event listener.
10
- #
11
- # @return [Mailgun::Tracking::Notifier]
12
- def initialize(listener = Listener.new)
13
- @listener = listener
14
- end
15
-
16
- # Returns true if there is at least one subscriber.
17
- #
18
- # @return [Boolean]
19
- def empty?
20
- listener.subscribers.empty?
21
- end
22
-
23
- # Adds subscriber for the specified event.
24
- #
25
- # @param event [Symbol, String] The name of event.
26
- # @param callable [Proc, Class] The listener of event.
27
- # The callable objects should respond to call.
28
- #
29
- # @return [NilClass]
30
- def subscribe(event, callable = Proc.new)
31
- listener.add_subscriber(event, callable)
32
- end
33
-
34
- # Adds a subscriber for all events.
35
- #
36
- # @param callable [Proc, Class] The listener of event.
37
- # The callable objects should respond to call.
38
- #
39
- # @return [NilClass]
40
- def all(callable = Proc.new)
41
- listener.add_subscriber(nil, callable)
42
- end
43
-
44
- # Broadcasts parameters to event subscribers.
45
- #
46
- # @param event [String] The name of event.
47
- # @param payload [Hash] The response parameters.
48
- #
49
- # @return [NilClass]
50
- def broadcast(event, payload)
51
- Signature.verify!(payload)
52
- listener.broadcast(event, payload)
53
- end
54
-
55
- private
56
-
57
- # @return [Mailgun::Tracking::Listener]
58
- attr_reader :listener
59
- end
60
- end
61
- end
@@ -1,96 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'set'
4
-
5
- module Mailgun
6
- module Tracking
7
- # Payload object.
8
- class Payload
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
27
- end
28
-
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]
36
- end
37
-
38
- # @return [Boolean]
39
- def ==(other)
40
- return false unless other.is_a?(Payload)
41
-
42
- values == other.values
43
- end
44
-
45
- alias eql? ==
46
-
47
- # @return [Integer] The object's hash value (for equality checking)
48
- def hash
49
- values.hash
50
- end
51
-
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
63
- end
64
-
65
- # @return [String] The string representation of the payload.
66
- def to_s
67
- JSON.pretty_generate(to_hash)
68
- end
69
-
70
- private
71
-
72
- # @return [Class]
73
- def __metaclass__
74
- class << self
75
- self
76
- end
77
- end
78
-
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
89
- end
90
-
91
- protected
92
-
93
- attr_reader :values
94
- end
95
- end
96
- end
@@ -1,47 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'rack/request'
4
-
5
- module Mailgun
6
- module Tracking
7
- # Provides a convenient interface to a Rack environment.
8
- class Request < ::Rack::Request
9
- # Checks if the request is respond to the specified URL.
10
- #
11
- # @return [Boolean]
12
- def mailgun_tracking?
13
- return false unless post?
14
- return false unless media_type == APPLICATION_JSON
15
-
16
- path == Configuration.instance.endpoint
17
- end
18
-
19
- # @return [Mailgun::Tracking::Payload]
20
- def payload
21
- @payload ||= Payload.new(params)
22
- end
23
-
24
- # A Mailgun::Tracking::Request::JSON is used to parsing JSON requests.
25
- module JSON
26
- APPLICATION_JSON = 'application/json'
27
- FORM_HASH = 'rack.request.form_hash'
28
- FORM_INPUT = 'rack.request.form_input'
29
-
30
- # Returns the data received in the request body.
31
- #
32
- # @return [Hash]
33
- def POST
34
- return super unless media_type == APPLICATION_JSON
35
-
36
- body = self.body.read
37
- self.body.rewind
38
- env.update(FORM_HASH => ::JSON.parse(body), FORM_INPUT => body)
39
-
40
- env[FORM_HASH]
41
- end
42
- end
43
-
44
- include JSON
45
- end
46
- end
47
- end
@@ -1,48 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'openssl'
4
-
5
- module Mailgun
6
- module Tracking
7
- # A Mailgun::Tracking::Signature object is used to verify the signature.
8
- class Signature
9
- # Verify the signature of the response parameters.
10
- #
11
- # @param payload [Mailgun::Tracking::Payload]
12
- # @raise [InvalidSignature] Error raised when signature is invalid.
13
- #
14
- # @return [Boolean]
15
- # Always returns true.
16
- def self.verify!(payload)
17
- signature = new(payload)
18
- raise InvalidSignature unless signature.valid?
19
-
20
- true
21
- end
22
-
23
- # Initializes a new Signature object.
24
- #
25
- # @param payload [Mailgun::Tracking::Payload]
26
- #
27
- # @return [Mailgun::Tracking::Signature]
28
- def initialize(payload)
29
- @payload = payload
30
- end
31
-
32
- # @return [Boolean]
33
- def valid?
34
- @payload.signature.signature == \
35
- OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, Configuration.instance.api_key, data)
36
- end
37
-
38
- private
39
-
40
- # Joins the timestamp and the response token.
41
- #
42
- # @return [String]
43
- def data
44
- [@payload.signature.timestamp, @payload.signature.token].join
45
- end
46
- end
47
- end
48
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'subscriber/all_messages'
4
- require_relative 'subscriber/evented'
5
-
6
- module Mailgun
7
- module Tracking
8
- # Namespace for classes that wraps subscribers.
9
- module Subscriber
10
- # Determines the type of subscription.
11
- #
12
- # @param name [Symbol, String] The name of event.
13
- # @param callable [Proc, Class] The callable object.
14
- # The callable objects should respond to call.
15
- #
16
- # @return [Mailgun::Tracking::Subscriber::Evented, Mailgun::Tracking::Subscriber::AllMessages]
17
- def self.for(name, callable)
18
- if name
19
- Evented.new(name, callable)
20
- else
21
- AllMessages.new(callable)
22
- end
23
- end
24
- end
25
- end
26
- end
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Mailgun
4
- module Tracking
5
- module Subscriber
6
- # Wraps the subscriber for events.
7
- class AllMessages
8
- # Initializes a new AllMessages object.
9
- #
10
- # @param callable [Proc, Class] The callable object.
11
- # The callable objects should respond to call.
12
- #
13
- # @return [Mailgun::Tracking::Subscriber::AllMessages]
14
- def initialize(callable)
15
- @callable = callable
16
- end
17
-
18
- # Invokes the callable object.
19
- #
20
- # @param payload [Hash] The response parameters.
21
- #
22
- # @return [void]
23
- def call(payload)
24
- @callable.call(payload)
25
- end
26
-
27
- # Checks if a callable object is a subscribed to the specified event.
28
- #
29
- # @return [Boolean]
30
- # Always returns true.
31
- def subscribed_to?(*)
32
- true
33
- end
34
- end
35
- end
36
- end
37
- end
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Mailgun
4
- module Tracking
5
- module Subscriber
6
- # Wraps the evented subscriber.
7
- class Evented
8
- # Initializes a new Evented object.
9
- #
10
- # @param name [Symbol, String] The name of event.
11
- # @param callable [Proc, Class] The callable object.
12
- # The callable objects should respond to call.
13
- #
14
- # @return [Mailgun::Tracking::Subscriber::Evented]
15
- def initialize(name, callable)
16
- @name = name.to_sym
17
- @callable = callable
18
- end
19
-
20
- # Invokes the callable object.
21
- #
22
- # @param payload [Hash] The response parameters.
23
- #
24
- # @return [void]
25
- def call(payload)
26
- @callable.call(payload)
27
- end
28
-
29
- # Checks if a callable object is a subscribed to the specified event.
30
- #
31
- # @param name [String] The name of event.
32
- #
33
- # @return [Boolean]
34
- def subscribed_to?(name)
35
- @name == name.to_sym
36
- end
37
- end
38
- end
39
- end
40
- end
@@ -1,67 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Mailgun
4
- module Tracking
5
- # Utility methods.
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
-
25
- class << self
26
- # Returns a new hash with all keys converted to symbols in downcase.
27
- #
28
- # @param options [Hash]
29
- #
30
- # @return [Hash]
31
- def normalize(options = {})
32
- object = normalize_keys(options.dup)
33
- object
34
- end
35
-
36
- private
37
-
38
- def normalize_keys(options)
39
- return from_h(options) if options.is_a?(Hash)
40
- return from_ary(options) if options.is_a?(Array)
41
-
42
- options
43
- end
44
-
45
- def from_h(options)
46
- Hash[options.map do |key, value|
47
- [underscore(key), normalize_keys(value)]
48
- end]
49
- end
50
-
51
- def from_ary(options)
52
- options.map(&method(:normalize_keys))
53
- end
54
-
55
- def underscore(key)
56
- return key unless key.respond_to?(:to_sym)
57
-
58
- key.to_s
59
- .dup
60
- .tr('-', '_')
61
- .downcase
62
- .to_sym
63
- end
64
- end
65
- end
66
- end
67
- end
@@ -1,48 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe Mailgun::Tracking::Listener do
4
- subject(:listener) { described_class.new }
5
-
6
- let(:callable) { proc {} }
7
- let(:subscriber) { instance_double(Mailgun::Tracking::Subscriber::Evented) }
8
-
9
- describe '#add_subscriber' do
10
- before { allow(Mailgun::Tracking::Subscriber).to receive(:for).with(/delivered/, callable) { subscriber } }
11
-
12
- it 'adds subscriber' do
13
- expect { listener.add_subscriber(:delivered, callable) }.to change(listener, :subscribers)
14
- .from([])
15
- .to([subscriber])
16
- end
17
-
18
- it 'adds multiple subscribers with the same name' do
19
- expect do
20
- listener.add_subscriber(:delivered, callable)
21
- listener.add_subscriber('delivered', callable)
22
- end.to change(listener, :subscribers).from([]).to([subscriber, subscriber])
23
- end
24
- end
25
-
26
- describe '#broadcast' do
27
- let(:payload) { fixture('delivered.json') }
28
-
29
- before do
30
- allow(subscriber).to receive(:call)
31
- allow(subscriber).to receive(:subscribed_to?).with(/delivered/).and_return(true)
32
- allow(Mailgun::Tracking::Subscriber).to receive(:for).with(:delivered, callable) { subscriber }
33
-
34
- listener.add_subscriber(:delivered, callable)
35
- end
36
-
37
- it 'executes subscriber' do
38
- listener.broadcast(:delivered, payload)
39
- expect(subscriber).to have_received(:call).with(payload)
40
- end
41
-
42
- it 'executes multiple subscribers' do
43
- listener.add_subscriber(:delivered, callable)
44
- listener.broadcast('delivered', payload)
45
- expect(subscriber).to have_received(:call).with(payload).twice
46
- end
47
- end
48
- end