mailgun-tracking 0.2.0 → 0.2.1

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