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 +4 -4
- data/lib/generators/mailgun/tracking/install_generator.rb +19 -0
- data/lib/generators/mailgun/tracking/templates/mailgun_tracking.rb.erb +15 -0
- data/lib/mailgun/tracking.rb +34 -2
- data/lib/mailgun/tracking/exceptions.rb +3 -0
- data/lib/mailgun/tracking/listener.rb +28 -0
- data/lib/mailgun/tracking/middleware.rb +21 -1
- data/lib/mailgun/tracking/notifier.rb +26 -0
- data/lib/mailgun/tracking/railtie.rb +1 -0
- data/lib/mailgun/tracking/signature.rb +18 -0
- data/lib/mailgun/tracking/subscriber.rb +8 -0
- data/lib/mailgun/tracking/subscriber/all_messages.rb +16 -0
- data/lib/mailgun/tracking/subscriber/evented.rb +18 -0
- data/lib/mailgun/tracking/version.rb +1 -1
- data/spec/mailgun/tracking/signature_spec.rb +2 -2
- metadata +13 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0f2b53add355033fc2a5b6cc4f93dc1818fdd4f5
|
4
|
+
data.tar.gz: 2abd9e0afdc9621f48584160547756b721ca0e10
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/lib/mailgun/tracking.rb
CHANGED
@@ -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
|
-
|
16
|
-
|
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,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
|
-
|
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
|
@@ -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
|
@@ -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
|
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
|
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.
|
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-
|
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/
|
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
|