mailgun-tracking 0.2.0 → 0.2.1

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
  SHA1:
3
- metadata.gz: 87dcc660603620c0e746e9ed5cf5cc1584149ece
4
- data.tar.gz: aad18416dca8c386cb96104c67a07512f847e84c
3
+ metadata.gz: 0f2b53add355033fc2a5b6cc4f93dc1818fdd4f5
4
+ data.tar.gz: 2abd9e0afdc9621f48584160547756b721ca0e10
5
5
  SHA512:
6
- metadata.gz: d858782b928aa9b6e4ef1025c9614001f64ada0ac7a9fd42e37ddbe24b9f08ff2d1a94428a80f67dbb08bca0d853dfbb1d06fa06f5c6784a47df75335a09844e
7
- data.tar.gz: 3fb5f7a36243749311a452ea220df5dcc1fea92ac9671f54febdb9d818f64f7ebdc47c812d7c75acbc930c336b790c236f1eb1de5b810f5025d5d0e805592d8b
6
+ metadata.gz: 8d6781261987073ad9cee2f8bf7b516892798a8b08dd5aebfe392204fcb417e2033ac8481a4f4c17a9114c7ecaf052b1b4e4f314bb9165ebedad7752f017dcd2
7
+ data.tar.gz: cff2a9d86aec1d5bb1b23e39074ae3eafb8e96cdf1907b8ec63b7055119c269f2321764393622206921d8ea82849435633cf8485d2e5d92369b69d18865a6751
@@ -0,0 +1,19 @@
1
+ module Mailgun
2
+ module Tracking
3
+ # Creates the Mailgun Tracking initializer file for Rails apps.
4
+ #
5
+ # @example Invokation from terminal
6
+ # rails generate mailgun:tracking:install API_KEY ENDPOINT
7
+ class InstallGenerator < Rails::Generators::Base
8
+ source_root File.expand_path('../templates', __FILE__)
9
+
10
+ argument :api_key, required: false
11
+ argument :endpoint, required: false
12
+
13
+ # Copies the initialization file.
14
+ def copy_initializer_file
15
+ template('mailgun_tracking.rb.erb', 'config/initializers/mailgun_tracking.rb')
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,15 @@
1
+ Mailgun::Tracking.configure do |config|
2
+ # Mailgun API public key.
3
+ <% if api_key -%>
4
+ config.api_key = <%= api_key %>
5
+ <% else -%>
6
+ config.api_key = ENV['MAILGUN_API_KEY']
7
+ <% end -%>
8
+
9
+ # Mailgun Webhook API endpoint. Default is '/mailgun'.
10
+ <% if endpoint -%>
11
+ config.endpoint = <%= endpoint %>
12
+ <% else -%>
13
+ # config.endpoint = '/mailgun-tracking'
14
+ <% end -%>
15
+ end
@@ -7,14 +7,30 @@ require 'mailgun/tracking/subscriber'
7
7
  require 'mailgun/tracking/version'
8
8
  require 'mailgun/tracking/railtie' if defined?(Rails)
9
9
 
10
+ # Module for interacting with the Mailgun.
10
11
  module Mailgun
12
+ # Namespace for classes and modules that handle Mailgun Webhooks.
11
13
  module Tracking
12
14
  DEFAULT_ENDPOINT = '/mailgun'.freeze
13
15
 
14
16
  class << self
15
- attr_accessor :api_key,
16
- :endpoint
17
+ # Mailgun API public key.
18
+ #
19
+ # @return [String]
20
+ attr_accessor :api_key
17
21
 
22
+ # Mailgun Webhook API endpoint
23
+ #
24
+ # @return [String]
25
+ attr_accessor :endpoint
26
+
27
+ # Default way to setup Mailgun Tracking.
28
+ #
29
+ # @example
30
+ # Mailgun::Tracking.configure do |config|
31
+ # config.api_key = ENV['MAILGUN_API_KEY']
32
+ # config.endpoint = '/mailgun-tracking'
33
+ # end
18
34
  def configure
19
35
  yield(self)
20
36
  end
@@ -24,6 +40,22 @@ module Mailgun
24
40
 
25
41
  module_function
26
42
 
43
+ # A Notifier instance.
44
+ #
45
+ # @example
46
+ # Mailgun::Tracking.configure do |config|
47
+ # config.notifier.subscribe :delivered do |payload|
48
+ # # Do something with the incoming data.
49
+ # end
50
+ #
51
+ # config.notifier.subscribe :bounced, Bounced.new
52
+ #
53
+ # config.notifier.all do |payload|
54
+ # # Handle all event types.
55
+ # end
56
+ # end
57
+ #
58
+ # @return [Mailgun::Tracking::Notifier]
27
59
  def notifier
28
60
  @notifier ||= Notifier.new
29
61
  end
@@ -1,6 +1,9 @@
1
1
  module Mailgun
2
2
  module Tracking
3
+ # A general Mailgun Tracking exception.
3
4
  Error = Class.new(StandardError)
5
+
6
+ # Raised when signature is invalid.
4
7
  InvalidSignature = Class.new(Error)
5
8
  end
6
9
  end
@@ -1,22 +1,50 @@
1
1
  module Mailgun
2
2
  module Tracking
3
+ # Represents a mechanism for event listeners to subscribe to events and for event broadcasts.
3
4
  class Listener
5
+ # List of subscribers.
6
+ #
7
+ # @return [Array<Mailgun::Tracking::Subscriber::AllMessages, Mailgun::Tracking::Subscriber::Evented>]
4
8
  attr_reader :subscribers
5
9
 
10
+ # Initializes a new Listener object.
11
+ #
12
+ # @return [Mailgun::Tracking::Listener]
6
13
  def initialize
7
14
  @subscribers = []
8
15
  end
9
16
 
17
+ # Adds a subscriber to the list of subscribers for the specified event.
18
+ #
19
+ # @param event [Symbol, String] The name of event.
20
+ # @param callable [Proc, Class] The listener of event.
21
+ # The callable objects should respond to call.
22
+ #
23
+ # @return [Array<Mailgun::Tracking::Subscriber::AllMessages, Mailgun::Tracking::Subscriber::Evented>]
24
+ # The list of subscribers.
10
25
  def add_subscriber(event, callable)
11
26
  @subscribers << Subscriber.for(event, callable)
12
27
  end
13
28
 
29
+ # Broadcasts an event to the subscribers.
30
+ #
31
+ # @param event [String] The name of event.
32
+ # @param payload [Hash] The response parameters.
33
+ #
34
+ # @return [Array<Mailgun::Tracking::Subscriber::AllMessages, Mailgun::Tracking::Subscriber::Evented>]
35
+ # The list of subscribers.
14
36
  def broadcast(event, payload)
15
37
  subscribers_for(event).each { |subscriber| subscriber.call(payload) }
16
38
  end
17
39
 
18
40
  private
19
41
 
42
+ # Selects the subscribers for the specified event.
43
+ #
44
+ # @param event [String] The name of event.
45
+ #
46
+ # @return [Array<Mailgun::Tracking::Subscriber::AllMessages, Mailgun::Tracking::Subscriber::Evented>]
47
+ # The list of subscribers.
20
48
  def subscribers_for(event)
21
49
  @subscribers.select { |subscriber| subscriber.subscribed_to?(event) }
22
50
  end
@@ -1,16 +1,25 @@
1
1
  module Mailgun
2
2
  module Tracking
3
+ # Rack-based middleware to handle event notifications.
3
4
  class Middleware
5
+ # Initializes a new Middleware object.
6
+ #
7
+ # @param app the next Rack middleware in the stack.
4
8
  def initialize(app)
5
9
  @app = app
6
10
  end
7
11
 
12
+ # Responds to Rack requests.
13
+ #
14
+ # @param env [Hash] Environment hash.
15
+ #
16
+ # @return [Array(Numeric,Hash,Array)] The Rack-style response.
8
17
  def call(env)
9
18
  @env = env
10
19
 
11
20
  if mailgun_tracking_request?
12
21
  Mailgun::Tracking.notifier.broadcast(request.params.fetch('event'), request.params)
13
- [200, {}, []]
22
+ null_response
14
23
  else
15
24
  @app.call(env)
16
25
  end
@@ -18,15 +27,26 @@ module Mailgun
18
27
 
19
28
  private
20
29
 
30
+ # Interface to a Rack environment.
31
+ #
32
+ # @return [Rack::Request]
21
33
  def request
22
34
  ::Rack::Request.new(@env)
23
35
  end
24
36
 
37
+ # Checks if the path of the request is equal to the endpoint.
38
+ #
39
+ # @return [Boolean]
25
40
  def mailgun_tracking_request?
26
41
  return false unless request.post?
27
42
  return false unless request.path == Mailgun::Tracking.endpoint
28
43
  true
29
44
  end
45
+
46
+ # @return [Array(Numeric,Hash,Array)] The Rack-style response.
47
+ def null_response
48
+ [200, {}, []]
49
+ end
30
50
  end
31
51
  end
32
52
  end
@@ -1,18 +1,43 @@
1
1
  module Mailgun
2
2
  module Tracking
3
+ # Wraps the {Listener} which gives a friendlier way to subscribe or broadcast.
3
4
  class Notifier
5
+ # Initializes a new Notifier object.
6
+ #
7
+ # @param listener [Mailgun::Tracking::Listener] Event listener.
8
+ #
9
+ # @return [Mailgun::Tracking::Notifier]
4
10
  def initialize(listener = Listener.new)
5
11
  @listener ||= listener
6
12
  end
7
13
 
14
+ # Adds subscriber for the specified event.
15
+ #
16
+ # @param event [Symbol, String] The name of event.
17
+ # @param callable [Proc, Class] The listener of event.
18
+ # The callable objects should respond to call.
19
+ #
20
+ # @return [NilClass]
8
21
  def subscribe(event, callable = Proc.new)
9
22
  listener.add_subscriber(event, callable)
10
23
  end
11
24
 
25
+ # Adds a subscriber for all events.
26
+ #
27
+ # @param callable [Proc, Class] The listener of event.
28
+ # The callable objects should respond to call.
29
+ #
30
+ # @return [NilClass]
12
31
  def all(callable = Proc.new)
13
32
  listener.add_subscriber(nil, callable)
14
33
  end
15
34
 
35
+ # Broadcasts parameters to event subscribers.
36
+ #
37
+ # @param event [String] The name of event.
38
+ # @param payload [Hash] The response parameters.
39
+ #
40
+ # @return [NilClass]
16
41
  def broadcast(event, payload)
17
42
  Signature.verify!(payload)
18
43
  listener.broadcast(event, payload)
@@ -20,6 +45,7 @@ module Mailgun
20
45
 
21
46
  private
22
47
 
48
+ # @return [Mailgun::Tracking::Listener]
23
49
  attr_reader :listener
24
50
  end
25
51
  end
@@ -1,5 +1,6 @@
1
1
  module Mailgun
2
2
  module Tracking
3
+ # Mailgun Tracking Railtie.
3
4
  class Railtie < Rails::Railtie
4
5
  initializer 'mailgun-tracking.insert_middleware' do |app|
5
6
  app.config.middleware.use(Mailgun::Tracking::Middleware)
@@ -2,29 +2,47 @@ require 'openssl'
2
2
 
3
3
  module Mailgun
4
4
  module Tracking
5
+ # A Mailgun::Tracking::Signature object is used to verify the signature.
5
6
  class Signature
7
+ # Verify the signature of the response parameters.
8
+ #
9
+ # @param payload [Hash]
10
+ # @raise [InvalidSignature] Error raised when signature is invalid.
11
+ #
12
+ # @return [Boolean]
13
+ # Always returns true.
6
14
  def self.verify!(payload)
7
15
  signature = new(payload)
8
16
  raise InvalidSignature unless signature.valid?
9
17
  true
10
18
  end
11
19
 
20
+ # Initializes a new Signature object.
21
+ #
22
+ # @param payload [Hash]
23
+ #
24
+ # @return [Mailgun::Tracking::Signature]
12
25
  def initialize(payload)
13
26
  @token = payload.fetch('token')
14
27
  @timestamp = payload.fetch('timestamp')
15
28
  @signature = payload.fetch('signature')
16
29
  end
17
30
 
31
+ # @return [Boolean]
18
32
  def valid?
19
33
  @signature == OpenSSL::HMAC.hexdigest(digest, Mailgun::Tracking.api_key, data)
20
34
  end
21
35
 
22
36
  private
23
37
 
38
+ # @return [OpenSSL::Digest::SHA256]
24
39
  def digest
25
40
  OpenSSL::Digest::SHA256.new
26
41
  end
27
42
 
43
+ # Joins the timestamp and the response token.
44
+ #
45
+ # @return [String]
28
46
  def data
29
47
  [@timestamp, @token].join
30
48
  end
@@ -3,7 +3,15 @@ require 'mailgun/tracking/subscriber/evented'
3
3
 
4
4
  module Mailgun
5
5
  module Tracking
6
+ # Namespace for classes that wraps subscribers.
6
7
  module Subscriber
8
+ # Determines the type of subscription.
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, Mailgun::Tracking::Subscriber::AllMessages]
7
15
  def self.for(name, callable)
8
16
  if name
9
17
  Evented.new(name, callable)
@@ -1,15 +1,31 @@
1
1
  module Mailgun
2
2
  module Tracking
3
3
  module Subscriber
4
+ # Wraps the subscriber for events.
4
5
  class AllMessages
6
+ # Initializes a new AllMessages object.
7
+ #
8
+ # @param callable [Proc, Class] The callable object.
9
+ # The callable objects should respond to call.
10
+ #
11
+ # @return [Mailgun::Tracking::Subscriber::AllMessages]
5
12
  def initialize(callable)
6
13
  @callable = callable
7
14
  end
8
15
 
16
+ # Invokes the callable object.
17
+ #
18
+ # @param payload [Hash] The response parameters.
19
+ #
20
+ # @return [void]
9
21
  def call(payload)
10
22
  @callable.call(payload)
11
23
  end
12
24
 
25
+ # Checks if a callable object is a subscribed to the specified event.
26
+ #
27
+ # @return [Boolean]
28
+ # Always returns true.
13
29
  def subscribed_to?(*)
14
30
  true
15
31
  end
@@ -1,16 +1,34 @@
1
1
  module Mailgun
2
2
  module Tracking
3
3
  module Subscriber
4
+ # Wraps the evented subscriber.
4
5
  class Evented
6
+ # Initializes a new Evented object.
7
+ #
8
+ # @param name [Symbol, String] The name of event.
9
+ # @param callable [Proc, Class] The callable object.
10
+ # The callable objects should respond to call.
11
+ #
12
+ # @return [Mailgun::Tracking::Subscriber::Evented]
5
13
  def initialize(name, callable)
6
14
  @name = name.to_sym
7
15
  @callable = callable
8
16
  end
9
17
 
18
+ # Invokes the callable object.
19
+ #
20
+ # @param payload [Hash] The response parameters.
21
+ #
22
+ # @return [void]
10
23
  def call(payload)
11
24
  @callable.call(payload)
12
25
  end
13
26
 
27
+ # Checks if a callable object is a subscribed to the specified event.
28
+ #
29
+ # @param name [String] The name of event.
30
+ #
31
+ # @return [Boolean]
14
32
  def subscribed_to?(name)
15
33
  @name == name.to_sym
16
34
  end
@@ -1,5 +1,5 @@
1
1
  module Mailgun
2
2
  module Tracking
3
- VERSION = '0.2.0'.freeze
3
+ VERSION = '0.2.1'.freeze
4
4
  end
5
5
  end
@@ -11,7 +11,7 @@ RSpec.describe Mailgun::Tracking::Signature do
11
11
  end
12
12
 
13
13
  context 'when the signature comparison is unsuccessful' do
14
- before { payload.merge!('timestamp' => '') }
14
+ before { payload['timestamp'] = '' }
15
15
 
16
16
  it { expect { described_class.verify!(payload) }.to raise_error(Mailgun::Tracking::InvalidSignature) }
17
17
  end
@@ -23,7 +23,7 @@ RSpec.describe Mailgun::Tracking::Signature do
23
23
  end
24
24
 
25
25
  context 'when the signature comparison is unsuccessful' do
26
- before { payload.merge!('timestamp' => '') }
26
+ before { payload['timestamp'] = '' }
27
27
 
28
28
  it { is_expected.not_to be_valid }
29
29
  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: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Artem Chubchenko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-07-16 00:00:00.000000000 Z
11
+ date: 2017-07-23 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Integration with Mailgun Webhooks
14
14
  email:
@@ -17,6 +17,8 @@ executables: []
17
17
  extensions: []
18
18
  extra_rdoc_files: []
19
19
  files:
20
+ - lib/generators/mailgun/tracking/install_generator.rb
21
+ - lib/generators/mailgun/tracking/templates/mailgun_tracking.rb.erb
20
22
  - lib/mailgun/tracking.rb
21
23
  - lib/mailgun/tracking/exceptions.rb
22
24
  - lib/mailgun/tracking/listener.rb
@@ -66,16 +68,16 @@ signing_key:
66
68
  specification_version: 4
67
69
  summary: Integration with Mailgun Webhooks
68
70
  test_files:
69
- - spec/fixtures/delivered.json
70
- - spec/spec_helper.rb
71
- - spec/mailgun/tracking_spec.rb
72
- - spec/mailgun/tracking/listener_spec.rb
73
- - spec/mailgun/tracking/middleware_spec.rb
74
- - spec/mailgun/tracking/notifier_spec.rb
75
- - spec/mailgun/tracking/subscriber_spec.rb
76
71
  - spec/mailgun/tracking/signature_spec.rb
77
- - spec/mailgun/tracking/subscriber/evented_spec.rb
78
72
  - spec/mailgun/tracking/subscriber/all_messages_spec.rb
79
- - spec/support/fixture.rb
73
+ - spec/mailgun/tracking/subscriber/evented_spec.rb
74
+ - spec/mailgun/tracking/subscriber_spec.rb
75
+ - spec/mailgun/tracking/listener_spec.rb
76
+ - spec/mailgun/tracking/notifier_spec.rb
77
+ - spec/mailgun/tracking/middleware_spec.rb
78
+ - spec/mailgun/tracking_spec.rb
80
79
  - spec/support/rack_helpers.rb
80
+ - spec/support/fixture.rb
81
81
  - spec/support/shared_examples/subscriber.rb
82
+ - spec/fixtures/delivered.json
83
+ - spec/spec_helper.rb