banter 1.2.4 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/banter/configuration.rb +9 -4
- data/lib/banter/message.rb +16 -1
- data/lib/banter/publisher.rb +25 -5
- data/lib/banter/rabbit_logger.rb +2 -2
- data/lib/banter/server/client_queue_listener.rb +12 -6
- data/lib/banter/server/rabbit_mq_subscriber.rb +21 -15
- data/lib/banter/server/subscriber_server.rb +3 -10
- data/lib/banter/subscriber.rb +50 -12
- data/lib/banter/version.rb +1 -1
- data/spec/banter/message_spec.rb +58 -18
- data/spec/banter/server/client_queue_listener_spec.rb +90 -10
- data/spec/banter/server/rabbitmq_subscriber_spec.rb +12 -1
- data/spec/banter/server/subscriber_server_spec.rb +4 -4
- data/spec/banter/subscriber_spec.rb +13 -13
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 20ba1b2e3b2fc1f89b3d25af6d5087e894ce4f87
|
4
|
+
data.tar.gz: bab54a9008cf00eb37f8c682a5c32ea199017f0c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1219d927b814c1ac6fdc88937d03082b757457c1a68bc454184d8adfb4c9c245b214d4faf886992a5a6fefd98dc1abcb7a7dc1613ca2344dcf0f3c1150119574
|
7
|
+
data.tar.gz: 7a303f0e8e01d9891811e8ad06dba142d0dcc4074c8e6c880627d0d313a0956f6122e2d4379433fc2b92532d9f7192388cbf2b52c548938c03dba8e14bc1e088
|
data/lib/banter/configuration.rb
CHANGED
@@ -13,11 +13,20 @@ module Banter
|
|
13
13
|
mattr_accessor :exchange_name
|
14
14
|
@@exchange_name = "banter"
|
15
15
|
|
16
|
+
mattr_accessor :dead_letter_exchange_name
|
17
|
+
@@dead_letter_exchange_name = "banter-dlx"
|
18
|
+
|
19
|
+
mattr_accessor :dead_letter_queue_name
|
20
|
+
@@dead_letter_queue_name = "deadletter"
|
21
|
+
|
16
22
|
# The topic prefix for RabbitMQ
|
17
23
|
# @default banter
|
18
24
|
mattr_accessor :topic_prefix
|
19
25
|
@@topic_prefix = "banter"
|
20
26
|
|
27
|
+
mattr_accessor :durable_queues
|
28
|
+
@@durable_queues = true
|
29
|
+
|
21
30
|
mattr_accessor :logger
|
22
31
|
|
23
32
|
# RabbitMQ push and subscribe logging toggle
|
@@ -35,10 +44,6 @@ module Banter
|
|
35
44
|
mattr_accessor :batch_messages
|
36
45
|
@@batch_messages = false
|
37
46
|
|
38
|
-
# Dead letter queue name
|
39
|
-
# @default nil
|
40
|
-
mattr_accessor :dead_letter_queue
|
41
|
-
|
42
47
|
# Enable web monitoring
|
43
48
|
# @default false
|
44
49
|
mattr_accessor :web_enabled
|
data/lib/banter/message.rb
CHANGED
@@ -10,6 +10,8 @@ module Banter
|
|
10
10
|
attr_reader :version
|
11
11
|
attr_reader :payload
|
12
12
|
attr_reader :context
|
13
|
+
attr_reader :dead_letter
|
14
|
+
attr_reader :envelope
|
13
15
|
|
14
16
|
def initialize
|
15
17
|
end
|
@@ -21,19 +23,26 @@ module Banter
|
|
21
23
|
@version = @@message_version
|
22
24
|
@payload = payload
|
23
25
|
@context = context
|
26
|
+
@dead_letter = nil
|
24
27
|
to_hash
|
25
28
|
end
|
26
29
|
|
27
30
|
def parse(envelope)
|
28
31
|
contents = Hashie::Mash.new(::JSON.parse(envelope))
|
32
|
+
from_hash(contents)
|
33
|
+
to_hash
|
34
|
+
end
|
35
|
+
|
36
|
+
def from_hash(contents)
|
29
37
|
@ts = contents.ts
|
30
38
|
@pub = contents.pub
|
31
39
|
# Version used for messaging can get updated if we need to add extra header information, and need
|
32
40
|
# to move each section of the library separately.
|
33
41
|
@version = contents.v
|
34
42
|
@payload = contents.payload
|
43
|
+
@dead_letter = contents.dead_letter
|
35
44
|
@context = ::Banter::Context.from_json(contents[:context])
|
36
|
-
|
45
|
+
self
|
37
46
|
end
|
38
47
|
|
39
48
|
def to_hash
|
@@ -42,8 +51,14 @@ module Banter
|
|
42
51
|
pub: @pub,
|
43
52
|
v: @@message_version,
|
44
53
|
payload: @payload,
|
54
|
+
dead_letter: @dead_letter,
|
45
55
|
context: @context.as_json
|
46
56
|
})
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
def retry_completed?
|
61
|
+
@dead_letter.present? && @dead_letter.completed.present? && @dead_letter.completed > 0
|
47
62
|
end
|
48
63
|
|
49
64
|
end
|
data/lib/banter/publisher.rb
CHANGED
@@ -12,8 +12,14 @@ module Banter
|
|
12
12
|
@@publisher
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
15
|
+
def self.teardown
|
16
|
+
@@publisher.teardown if @publisher
|
17
|
+
@@publisher = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(exchange = nil, exchange_type = :topic)
|
16
21
|
@exchange = exchange || Banter::Configuration.exchange_name
|
22
|
+
@exchange_type = exchange_type
|
17
23
|
@disabled = false
|
18
24
|
@batch_messages = false
|
19
25
|
@stack_depth = 0
|
@@ -43,7 +49,7 @@ module Banter
|
|
43
49
|
@connection.start
|
44
50
|
|
45
51
|
@channel = @connection.create_channel
|
46
|
-
@publisher = @channel.
|
52
|
+
@publisher = @channel.send(@exchange_type, @exchange, :durable => true, :auto_delete => false)
|
47
53
|
|
48
54
|
rescue => e
|
49
55
|
Airbrake.notify(e, parameters: { message: e.message }, environment_name: ENV['RAILS_ENV'])
|
@@ -68,8 +74,20 @@ module Banter
|
|
68
74
|
end
|
69
75
|
end
|
70
76
|
|
71
|
-
|
72
|
-
|
77
|
+
# Special method to publish to specific queues. This is useful for deadletter queue processing, where the dead letter will want to
|
78
|
+
# fire a message to a specific queue, as well at the consumer will want to inform the dead letter queue that it is finished processing
|
79
|
+
# a retry
|
80
|
+
# @param queue_name [String] name to publish to (can be retrieved from headers[:x_death][:queue] from dead letter exchange processor)
|
81
|
+
# @param envelope [Hash] hash of contents that the amqp server returns
|
82
|
+
def publish_to_queue(queue_name, envelope)
|
83
|
+
execute_publish(queue_name, envelope, true)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Calls the ampq server with normalized data
|
87
|
+
# @param routing_key [String] - routing_key that amqp server uses
|
88
|
+
# @param envelope [Hash] - normalized data to publish
|
89
|
+
def execute_publish(routing_key, envelope, use_default_exchange=false)
|
90
|
+
|
73
91
|
|
74
92
|
if @publisher.nil?
|
75
93
|
start
|
@@ -80,7 +98,8 @@ module Banter
|
|
80
98
|
else
|
81
99
|
tries = 2
|
82
100
|
begin
|
83
|
-
|
101
|
+
instance = use_default_exchange ? @channel.default_exchange : @publisher
|
102
|
+
instance.publish(envelope.to_json, :persistent => true, :mandatory => true, :timestamp => envelope[:ts], :content_type => "application/json", :routing_key => routing_key)
|
84
103
|
Banter::RabbitLogger.log_publish(routing_key, envelope)
|
85
104
|
# FIX!!! -thl
|
86
105
|
# What kind of errors could be fired from a failure to publish?
|
@@ -103,6 +122,7 @@ module Banter
|
|
103
122
|
|
104
123
|
end
|
105
124
|
|
125
|
+
private
|
106
126
|
def teardown
|
107
127
|
begin
|
108
128
|
@connection.close
|
data/lib/banter/rabbit_logger.rb
CHANGED
@@ -36,8 +36,8 @@ module Banter
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def self.log(log_level, message)
|
39
|
-
tags = ["BANTER LOG_LEVEL:#{@@log_map[log_level].capitalize.to_s}", Process::pid
|
40
|
-
logger.tagged(tags) { logger.send(@@log_map[log_level],
|
39
|
+
tags = ["BANTER LOG_LEVEL:#{@@log_map[log_level].capitalize.to_s}", Process::pid]
|
40
|
+
logger.tagged(tags) { logger.send(@@log_map[log_level], message.as_json ) }
|
41
41
|
end
|
42
42
|
|
43
43
|
def self.internal_log(log_level, log_type, envelope, routing_key, payload)
|
@@ -10,12 +10,9 @@ module Banter
|
|
10
10
|
|
11
11
|
attr_reader :worker_class
|
12
12
|
|
13
|
-
def initialize(worker_class
|
14
|
-
@topic = topic || Banter::Configuration.topic_prefix
|
15
|
-
@request_key = request_key
|
13
|
+
def initialize(worker_class)
|
16
14
|
@worker_class = worker_class
|
17
|
-
@
|
18
|
-
@subscriber = ::Banter::Server::RabbitMQSubscriber.new(@request_key, @queue_name, worker_class.queue_ttl, durable, pool_size, @topic)
|
15
|
+
@subscriber = RabbitMQSubscriber.new(worker_class.config)
|
19
16
|
end
|
20
17
|
|
21
18
|
def start
|
@@ -31,11 +28,12 @@ module Banter
|
|
31
28
|
|
32
29
|
def message_received(delivery_info, properties, envelope)
|
33
30
|
Banter::RabbitLogger.log(::Logger::DEBUG, "Message received by listener on #{worker_class.name} with payload: #{envelope[:payload]}")
|
34
|
-
worker = worker_class.new(delivery_info, properties, envelope[:context])
|
31
|
+
worker = worker_class.new(delivery_info, properties, envelope[:context], envelope)
|
35
32
|
payload = envelope[:payload]
|
36
33
|
track_message_received(worker, payload)
|
37
34
|
worker.perform!(payload)
|
38
35
|
track_message_successful
|
36
|
+
clear_dead_letter(worker_class.config.dead_letter_queue_name, envelope)
|
39
37
|
rescue ::Banter::PayloadValidationError => banter_error
|
40
38
|
track_message_failed(banter_error, false)
|
41
39
|
Banter::RabbitLogger.log(::Logger::ERROR, { worker: worker_class.name, message: banter_error.message, envelope: envelope })
|
@@ -47,6 +45,7 @@ module Banter
|
|
47
45
|
payload: envelope[:payload],
|
48
46
|
message: "Validation error for #{worker_class.name}"
|
49
47
|
})
|
48
|
+
raise banter_error
|
50
49
|
rescue => e
|
51
50
|
track_message_failed(e, true)
|
52
51
|
Banter::RabbitLogger.log(::Logger::DEBUG, "Failed message for #{worker_class.name} #{e.message}")
|
@@ -58,7 +57,14 @@ module Banter
|
|
58
57
|
payload: envelope[:payload],
|
59
58
|
message: "Failed perform for #{worker_class.name}"
|
60
59
|
})
|
60
|
+
raise e
|
61
|
+
end
|
61
62
|
|
63
|
+
def clear_dead_letter(dead_letter_queue, envelope)
|
64
|
+
if !dead_letter_queue.blank? && !envelope[:dead_letter].blank?
|
65
|
+
envelope.dead_letter.completed = 1
|
66
|
+
Banter::Publisher.instance.publish_to_queue(dead_letter_queue, envelope)
|
67
|
+
end
|
62
68
|
end
|
63
69
|
|
64
70
|
protected
|
@@ -8,21 +8,26 @@ module Banter
|
|
8
8
|
attr_reader :exchange
|
9
9
|
attr_reader :channel
|
10
10
|
attr_reader :ttl
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
@topic = topic
|
16
|
-
@
|
17
|
-
@
|
18
|
-
|
19
|
-
|
11
|
+
attr_reader :routing_key
|
12
|
+
attr_reader :topic
|
13
|
+
|
14
|
+
def initialize(config)
|
15
|
+
@topic = config.topic
|
16
|
+
@exchange_name = config.exchange
|
17
|
+
@dead_letter_exchange_name = config.dead_letter_exchange
|
18
|
+
@initial_key = config.routing_key
|
19
|
+
@queue_name = config.queue_name
|
20
|
+
@ttl = config.ttl
|
21
|
+
@durable = config.durable
|
22
|
+
@pool_size = config.pool_size
|
23
|
+
if @topic.blank?
|
24
|
+
@routing_key = "#"
|
25
|
+
elsif @initial_key.present?
|
20
26
|
@routing_key = "#{@topic}.#{@initial_key}.#"
|
21
|
-
|
27
|
+
elsif @topic.present?
|
22
28
|
@routing_key = "#{@topic}.#"
|
23
29
|
end
|
24
30
|
|
25
|
-
@queue_name = queue_name
|
26
31
|
end
|
27
32
|
|
28
33
|
def start(&block)
|
@@ -36,11 +41,13 @@ module Banter
|
|
36
41
|
end
|
37
42
|
|
38
43
|
@channel = @connection.create_channel
|
39
|
-
|
44
|
+
|
45
|
+
@exchange = @channel.topic(@exchange_name, :durable => @durable, :auto_delete => false)
|
40
46
|
|
41
47
|
queue_arguments = {}
|
42
|
-
|
43
|
-
queue_arguments["x-
|
48
|
+
|
49
|
+
queue_arguments["x-dead-letter-exchange"] = @dead_letter_exchange_name if @dead_letter_exchange_name.present?
|
50
|
+
queue_arguments["x-message-ttl"] = ttl * 1000 if ttl > 0
|
44
51
|
|
45
52
|
@channel.basic_qos(@pool_size) if @pool_size != 0
|
46
53
|
|
@@ -76,7 +83,6 @@ module Banter
|
|
76
83
|
Banter::RabbitLogger.log(Logger::WARN, "Error in message: #{e}")
|
77
84
|
e.backtrace.each{|line| Banter::RabbitLogger.log(Logger::WARN, "-- #{line}") }
|
78
85
|
Banter::RabbitLogger.log(Logger::WARN, "contents: #{contents}")
|
79
|
-
Banter::RabbitLogger.log(Logger::WARN, { routing_key: delivery_info[:routing_key], delivery_info: delivery_info, properties: properties, envelope: envelope} )
|
80
86
|
Banter::RabbitLogger.log_subscriber_failed(delivery_info[:routing_key], delivery_info, properties, envelope)
|
81
87
|
|
82
88
|
# Does not get put back on the queue, and instead, will need to be processed either by the log parser
|
@@ -79,7 +79,7 @@ module Banter
|
|
79
79
|
|
80
80
|
def start_workers
|
81
81
|
workers.each do |worker|
|
82
|
-
puts "starting
|
82
|
+
puts "starting worker #{worker.inspect}"
|
83
83
|
worker.start
|
84
84
|
end
|
85
85
|
end
|
@@ -94,15 +94,8 @@ module Banter
|
|
94
94
|
@hostname = `hostname -f`.chomp
|
95
95
|
end
|
96
96
|
|
97
|
-
def create_queue_listeners(
|
98
|
-
|
99
|
-
routing_key = subscriber.subscribed_key
|
100
|
-
durable = subscriber.durable
|
101
|
-
queue_name = subscriber.generated_queue_name(routing_key, application_name)
|
102
|
-
pool_size = Banter::Configuration.configuration[:pool_size] || 100
|
103
|
-
|
104
|
-
STDOUT.puts "Setting up listener for request_key: #{routing_key} and queue:#{application_name} with qos setting: #{pool_size}"
|
105
|
-
ClientQueueListener.new(subscriber, routing_key, queue_name, durable, pool_size)
|
97
|
+
def create_queue_listeners(subscriber_class)
|
98
|
+
ClientQueueListener.new(subscriber_class)
|
106
99
|
end
|
107
100
|
end
|
108
101
|
end
|
data/lib/banter/subscriber.rb
CHANGED
@@ -8,8 +8,8 @@ module Banter
|
|
8
8
|
class Subscriber
|
9
9
|
@@registered_subscribers = []
|
10
10
|
|
11
|
-
class_attribute :payload_validators, :error_handlers, :
|
12
|
-
attr_accessor :delivery_routing_data, :delivery_properties, :context
|
11
|
+
class_attribute :payload_validators, :error_handlers, :config, :subscribed_key, :subscribed_queue
|
12
|
+
attr_accessor :delivery_routing_data, :delivery_properties, :context, :envelope
|
13
13
|
|
14
14
|
def self.inherited(klass)
|
15
15
|
@@registered_subscribers << klass
|
@@ -19,19 +19,55 @@ module Banter
|
|
19
19
|
# @param [String] routing_key_name The routing key to subscribe to. Must be characters only separated by periods (.)
|
20
20
|
# @param [Hash] options subscription options
|
21
21
|
# @option [String] :durable sets the subscriber to be a durable subscriber (one that survives reboots). This currently defaults to true.
|
22
|
-
# @option [Integer] :
|
22
|
+
# @option [Integer] :ttl Time, in seconds, that the message lives on the queue before being either consumer by a subscriber or being discarded.
|
23
23
|
# If not specified, then Banter::Configuration.default_queue_ttl is used
|
24
|
-
|
24
|
+
# @option [String] :exchange_name sets to exchange to the specified value, or if it is not used, will default to Banter.Configuration.exchange_name
|
25
|
+
# @option [String] :dead_letter_exchange_name name of queue to subscribe to.
|
26
|
+
# @option [String] :queue_name name of queue to subscribe to.
|
25
27
|
def self.subscribe_to(routing_key_name, options = {})
|
26
|
-
|
28
|
+
|
27
29
|
unless validate_routing_key_name(routing_key_name)
|
28
30
|
raise ArgumentError.new("#{routing_key_name} is not supported. Only lower case characters separated by periods are allowed.")
|
29
31
|
end
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
32
|
+
|
33
|
+
options.assert_valid_keys(
|
34
|
+
:topic_prefix,
|
35
|
+
:exchange_name,
|
36
|
+
:dead_letter_exchange_name,
|
37
|
+
:dead_letter_queue_name,
|
38
|
+
:queue_name,
|
39
|
+
:ttl,
|
40
|
+
:durable,
|
41
|
+
:pool_size)
|
42
|
+
|
43
|
+
config = Hashie::Mash.new
|
44
|
+
config[:topic] = get_config_value(options, :topic_prefix)
|
45
|
+
config[:exchange] = get_config_value(options, :exchange_name)
|
46
|
+
config[:dead_letter_exchange] = get_config_value(options, :dead_letter_exchange_name)
|
47
|
+
config[:dead_letter_queue_name] = get_config_value(options, :dead_letter_queue_name)
|
48
|
+
config[:routing_key] = self.subscribed_key = routing_key_name
|
49
|
+
config[:queue_name] = self.subscribed_queue = options[:queue_name] || generated_queue_name(routing_key_name, Banter::Configuration.application_name)
|
50
|
+
config[:ttl] = get_config_value(options, :ttl, :default_queue_ttl)
|
51
|
+
config[:durable] = get_config_value(options, :durable, :durable_queues)
|
52
|
+
config[:pool_size] = get_config_value(options, :pool_size)
|
53
|
+
|
54
|
+
self.config = config
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.get_config_value(options, options_key, config_key = nil)
|
59
|
+
if options.key?(options_key)
|
60
|
+
options[options_key]
|
61
|
+
else
|
62
|
+
config_key = options_key if config_key.nil?
|
63
|
+
configure_obj = [Banter::Configuration.configuration, Banter::Configuration].find do |obj|
|
64
|
+
obj.respond_to?(config_key)
|
65
|
+
end
|
66
|
+
configure_obj.nil? ? nil : configure_obj.send(config_key)
|
67
|
+
end
|
68
|
+
rescue => e
|
69
|
+
puts "Banter Configuration does not exist #{config_key}:#{e.message}"
|
70
|
+
raise e
|
35
71
|
end
|
36
72
|
|
37
73
|
# Sets the validator for payload
|
@@ -49,13 +85,15 @@ module Banter
|
|
49
85
|
error_handler = handler
|
50
86
|
end
|
51
87
|
|
52
|
-
# @param [Hash] context Context from invocation
|
53
88
|
# @param [Object] delivery_routing_data Contains routing information like originator and routing key
|
54
89
|
# @param [Object] delivery_properties
|
55
|
-
|
90
|
+
# @param [Hash] context
|
91
|
+
# @param [Hash] envelope
|
92
|
+
def initialize(delivery_routing_data, delivery_properties, context, envelope)
|
56
93
|
@delivery_routing_data = delivery_routing_data
|
57
94
|
@delivery_properties = delivery_properties
|
58
95
|
@context = context
|
96
|
+
@envelope = envelope
|
59
97
|
end
|
60
98
|
|
61
99
|
# Performs validation if validates_payload_with is defined and then calls the perform method
|
data/lib/banter/version.rb
CHANGED
data/spec/banter/message_spec.rb
CHANGED
@@ -37,29 +37,69 @@ describe Banter::Message do
|
|
37
37
|
let(:payload) { subject.serialize(context, routing_key, original).to_json }
|
38
38
|
let(:result) { subject.parse(payload) }
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
context "standard message" do
|
41
|
+
it "should not fail" do
|
42
|
+
expect{ result }.not_to raise_error()
|
43
|
+
end
|
43
44
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
45
|
+
it "should have all the hash items" do
|
46
|
+
expect(result[:ts]).not_to be_nil
|
47
|
+
expect(result[:v]).not_to be_nil
|
48
|
+
expect(result[:pub]).not_to be_nil
|
49
|
+
expect(result[:payload]).not_to be_nil
|
50
|
+
expect(result[:dead_letter]).to be_nil
|
51
|
+
expect(subject.retry_completed?).to eq(false)
|
52
|
+
end
|
50
53
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
+
it "should have the data in the payload" do
|
55
|
+
expect(result.payload.data).to eq('found')
|
56
|
+
end
|
54
57
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
58
|
+
it "should have the attributes readable" do
|
59
|
+
result
|
60
|
+
expect(subject.ts).not_to be_nil
|
61
|
+
expect(subject.version).not_to be_nil
|
62
|
+
expect(subject.pub).not_to be_nil
|
63
|
+
expect(subject.payload).not_to be_nil
|
64
|
+
expect(subject.dead_letter).to be_nil
|
65
|
+
end
|
61
66
|
end
|
62
67
|
|
68
|
+
context "dead lettered message" do
|
69
|
+
let(:complete_val) { 1 }
|
70
|
+
let(:payload) do
|
71
|
+
obj = subject.serialize(context, routing_key, original)
|
72
|
+
obj.dead_letter = {"completed"=> complete_val, "id"=> 5}
|
73
|
+
obj.to_json
|
74
|
+
end
|
75
|
+
|
76
|
+
before do
|
77
|
+
result
|
78
|
+
end
|
79
|
+
|
80
|
+
context "completed message" do
|
81
|
+
it "should have all the hash items" do
|
82
|
+
expect(result[:ts]).not_to be_nil
|
83
|
+
expect(result[:v]).not_to be_nil
|
84
|
+
expect(result[:pub]).not_to be_nil
|
85
|
+
expect(result[:payload]).not_to be_nil
|
86
|
+
expect(result[:dead_letter]).not_to be_nil
|
87
|
+
expect(subject.retry_completed?).to eq(true)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "failed message" do
|
92
|
+
let(:complete_val) { 0 }
|
93
|
+
it "should have all the hash items" do
|
94
|
+
expect(result[:ts]).not_to be_nil
|
95
|
+
expect(result[:v]).not_to be_nil
|
96
|
+
expect(result[:pub]).not_to be_nil
|
97
|
+
expect(result[:payload]).not_to be_nil
|
98
|
+
expect(result[:dead_letter]).not_to be_nil
|
99
|
+
expect(subject.retry_completed?).to eq(false)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
63
103
|
end
|
64
104
|
|
65
105
|
end
|
@@ -1,10 +1,26 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
+
|
4
|
+
class TestSubscriber < Banter::Subscriber
|
5
|
+
subscribe_to "some.test.key"
|
6
|
+
end
|
7
|
+
|
3
8
|
describe Banter::Server::ClientQueueListener do
|
4
9
|
describe "#message_received" do
|
5
10
|
|
6
11
|
subject { -> { queue_listener.message_received({}, { routing_key: routing_key }, { context: context, payload: payload }) } }
|
7
|
-
let(:
|
12
|
+
let(:subscriber_config) { Hashie::Mash.new(
|
13
|
+
topic: "banter",
|
14
|
+
exchange: "banter",
|
15
|
+
dead_letter_exchange: "banter-dlx",
|
16
|
+
routing_key: routing_key,
|
17
|
+
queue_name: "test_queue",
|
18
|
+
ttl: 100,
|
19
|
+
durable: true,
|
20
|
+
pool_size: 100
|
21
|
+
)}
|
22
|
+
|
23
|
+
let(:queue_listener) { Banter::Server::ClientQueueListener.new(Klass) }
|
8
24
|
let(:routing_key) { 'some.test.key' }
|
9
25
|
let(:context) { { app: 'test' } }
|
10
26
|
let(:payload) { { hello: 'moto' } }
|
@@ -17,6 +33,7 @@ describe Banter::Server::ClientQueueListener do
|
|
17
33
|
}
|
18
34
|
let!(:application_name) { "testapplication" }
|
19
35
|
let(:banter_message) { BanterMessage.last }
|
36
|
+
let(:subscriber_options) {{}}
|
20
37
|
|
21
38
|
after(:each) { Banter::Configuration.web_enabled = false }
|
22
39
|
|
@@ -46,25 +63,36 @@ describe Banter::Server::ClientQueueListener do
|
|
46
63
|
validators.each do |validator|
|
47
64
|
Klass.validates_payload_with validator
|
48
65
|
end
|
49
|
-
Klass.subscribe_to "test.foo"
|
66
|
+
Klass.subscribe_to "test.foo", subscriber_options
|
50
67
|
|
51
68
|
klass_instance
|
52
69
|
|
53
70
|
allow(Klass).to receive(:new).and_return(klass_instance)
|
54
|
-
subject.call unless respond_to? :no_call
|
55
71
|
end
|
56
72
|
|
57
73
|
after do
|
58
74
|
Object.send :remove_const, :Klass
|
59
75
|
end
|
60
76
|
|
77
|
+
def safe_call
|
78
|
+
subject.call
|
79
|
+
rescue=>e
|
80
|
+
@raised_error = e
|
81
|
+
nil
|
82
|
+
end
|
83
|
+
|
61
84
|
context "Normal execution" do
|
62
85
|
let!(:klass_instance) {
|
63
|
-
instance = Klass.new({}, {}, {})
|
86
|
+
instance = Klass.new({}, {}, {}, {})
|
64
87
|
allow(instance).to receive(:perform!)
|
65
88
|
instance
|
66
89
|
}
|
67
90
|
|
91
|
+
|
92
|
+
before do
|
93
|
+
subject.call unless respond_to? :no_call
|
94
|
+
end
|
95
|
+
|
68
96
|
context "banter web disabled" do
|
69
97
|
context "test exception" do
|
70
98
|
let(:no_call) { true }
|
@@ -94,7 +122,7 @@ describe Banter::Server::ClientQueueListener do
|
|
94
122
|
|
95
123
|
context "Exception during execution" do
|
96
124
|
let!(:klass_instance) {
|
97
|
-
instance = Klass.new({}, {}, {})
|
125
|
+
instance = Klass.new({}, {}, {}, {})
|
98
126
|
allow(instance).to receive(:perform!) {
|
99
127
|
raise StandardError.new("test exception")
|
100
128
|
}
|
@@ -105,29 +133,42 @@ describe Banter::Server::ClientQueueListener do
|
|
105
133
|
before do
|
106
134
|
allow(Banter::RabbitLogger).to receive(:log)
|
107
135
|
end
|
108
|
-
it {
|
136
|
+
it { should raise_exception }
|
109
137
|
it { expect(BanterMessage.count).to eq(0) }
|
110
138
|
end
|
111
139
|
|
112
140
|
context "banter web enabled" do
|
113
141
|
let(:context_before) { enable_banter_web }
|
114
142
|
|
115
|
-
|
116
|
-
|
143
|
+
before do
|
144
|
+
safe_call
|
145
|
+
end
|
146
|
+
|
147
|
+
it { should raise_exception }
|
148
|
+
it "should have called perform" do
|
149
|
+
expect(klass_instance).to have_received(:perform!).with(payload)
|
150
|
+
end
|
117
151
|
banter_message_fields_are_correct(BanterMessage::STATUS_ERROR, 'test exception')
|
118
152
|
end
|
119
153
|
end
|
120
154
|
|
121
155
|
context "Payload error" do
|
122
156
|
let!(:klass_instance) {
|
123
|
-
instance = Klass.new({}, {}, {})
|
157
|
+
instance = Klass.new({}, {}, {},{})
|
124
158
|
allow(instance).to receive(:perform!).and_raise(Banter::PayloadValidationError.new("validation failed"))
|
125
159
|
instance
|
126
160
|
}
|
127
161
|
|
162
|
+
let(:result) { subject.call }
|
163
|
+
|
164
|
+
before do
|
165
|
+
safe_call
|
166
|
+
end
|
167
|
+
|
128
168
|
let!(:validators) { [lambda { |payload| false }] }
|
129
169
|
context "web disabled" do
|
130
170
|
let(:context_before) { allow(Airbrake).to receive(:notify) }
|
171
|
+
it { should raise_exception }
|
131
172
|
it { expect(Airbrake).to have_received(:notify) }
|
132
173
|
end
|
133
174
|
|
@@ -138,10 +179,49 @@ describe Banter::Server::ClientQueueListener do
|
|
138
179
|
end
|
139
180
|
|
140
181
|
it { expect(Airbrake).to have_received(:notify) }
|
141
|
-
it {
|
182
|
+
it { should raise_exception }
|
142
183
|
it { expect(klass_instance).to have_received(:perform!).with(payload) }
|
143
184
|
banter_message_fields_are_correct(BanterMessage::STATUS_VALIDATION_FAILED, "validation failed")
|
144
185
|
end
|
145
186
|
end
|
187
|
+
|
188
|
+
context "reprocessing message" do
|
189
|
+
subject { -> { queue_listener.message_received({}, { routing_key: routing_key }, Hashie::Mash.new({ context: context, payload: payload, dead_letter: { retries: 1, id: 553} }) ) } }
|
190
|
+
let(:subscriber_options) {{ dead_letter_queue_name: "deadletter" }}
|
191
|
+
let(:test_method) { raise StandardError.new("test exception") }
|
192
|
+
let!(:klass_instance) {
|
193
|
+
instance = Klass.new({}, {}, {}, {})
|
194
|
+
allow(instance).to receive(:perform!) {
|
195
|
+
test_method
|
196
|
+
}
|
197
|
+
instance
|
198
|
+
}
|
199
|
+
|
200
|
+
before do
|
201
|
+
klass_instance
|
202
|
+
allow(Banter::Publisher.instance).to receive(:execute_publish)
|
203
|
+
safe_call
|
204
|
+
end
|
205
|
+
|
206
|
+
context "succeed in reprocess" do
|
207
|
+
let(:test_method) { {} }
|
208
|
+
|
209
|
+
it "should have called the execute_publish method of the queue publisher" do
|
210
|
+
# Marked the message with a completed flag and send to a queue for dead letter. also, the exchange is correct being empty.
|
211
|
+
hash_data = { context: {app:"test"}, dead_letter: { completed: 1, id: 553, retries: 1 }, payload: { hello: "moto"} }
|
212
|
+
val = Hashie::Mash.new(hash_data)
|
213
|
+
expect(Banter::Publisher.instance).to have_received(:execute_publish).with("deadletter", val, true )
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
217
|
+
|
218
|
+
context "failed to reprocess" do
|
219
|
+
it "should not have called the execute_publish method of the queue publisher" do
|
220
|
+
expect(Banter::Publisher.instance).not_to have_received(:execute_publish)
|
221
|
+
end
|
222
|
+
|
223
|
+
end
|
224
|
+
|
225
|
+
end
|
146
226
|
end
|
147
227
|
end
|
@@ -1,8 +1,19 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
+
|
3
4
|
describe Banter::Server::RabbitMQSubscriber do
|
4
5
|
let(:routing_key) { "fake.route"}
|
5
|
-
let(:
|
6
|
+
let(:subscriber_config) { Hashie::Mash.new(
|
7
|
+
topic: "banter",
|
8
|
+
exchange: "banter",
|
9
|
+
dead_letter_exchange: "banter-dlx",
|
10
|
+
routing_key: routing_key,
|
11
|
+
queue_name: "test_queue",
|
12
|
+
ttl: 100,
|
13
|
+
durable: true,
|
14
|
+
pool_size: 100
|
15
|
+
)}
|
16
|
+
let(:subscriber){ Banter::Server::RabbitMQSubscriber.new( subscriber_config ) }
|
6
17
|
let(:delivery_info) { Hashie::Mash.new({ :delivery_tag=>1, :routing_key=> routing_key}) }
|
7
18
|
let(:properties) { {} }
|
8
19
|
let(:contents) { File.read( File.join(File.dirname(__FILE__), "../../fixtures/utf_message.json")) }
|
@@ -20,8 +20,8 @@ describe Banter::Server::SubscriberServer do
|
|
20
20
|
}
|
21
21
|
|
22
22
|
it "creates queue listeners" do
|
23
|
-
expect(Banter::Server::ClientQueueListener).to have_received(:new).with(MyTestSubscriber1
|
24
|
-
expect(Banter::Server::ClientQueueListener).to have_received(:new).with(MyTestSubscriber2
|
23
|
+
expect(Banter::Server::ClientQueueListener).to have_received(:new).with(MyTestSubscriber1)
|
24
|
+
expect(Banter::Server::ClientQueueListener).to have_received(:new).with(MyTestSubscriber2)
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
@@ -39,8 +39,8 @@ describe Banter::Server::SubscriberServer do
|
|
39
39
|
}
|
40
40
|
|
41
41
|
it "creates queue listeners" do
|
42
|
-
expect(Banter::Server::ClientQueueListener).to have_received(:new).with(MyTestSubscriber1
|
43
|
-
expect(Banter::Server::ClientQueueListener).to have_received(:new).with(MyTestSubscriber2
|
42
|
+
expect(Banter::Server::ClientQueueListener).to have_received(:new).with(MyTestSubscriber1)
|
43
|
+
expect(Banter::Server::ClientQueueListener).to have_received(:new).with(MyTestSubscriber2)
|
44
44
|
end
|
45
45
|
|
46
46
|
end
|
@@ -10,7 +10,7 @@ describe Banter::Subscriber do
|
|
10
10
|
end
|
11
11
|
|
12
12
|
describe "#routing_key" do
|
13
|
-
subject { Banter::Subscriber.new(routing_data, properties, {}).routing_key }
|
13
|
+
subject { Banter::Subscriber.new(routing_data, properties, {}, {}).routing_key }
|
14
14
|
|
15
15
|
let(:properties) { {} }
|
16
16
|
|
@@ -22,7 +22,7 @@ describe Banter::Subscriber do
|
|
22
22
|
end
|
23
23
|
|
24
24
|
describe '#at' do
|
25
|
-
let(:subscriber) { Banter::Subscriber.new({}, {}, {}) }
|
25
|
+
let(:subscriber) { Banter::Subscriber.new({}, {}, {}, {}) }
|
26
26
|
context 'web not enabled' do
|
27
27
|
before do
|
28
28
|
allow(Banter::Configuration).to receive(:web_enabled).and_return(false)
|
@@ -49,7 +49,7 @@ describe Banter::Subscriber do
|
|
49
49
|
end
|
50
50
|
|
51
51
|
describe '#total' do
|
52
|
-
let(:subscriber) { Banter::Subscriber.new({}, {}, {}) }
|
52
|
+
let(:subscriber) { Banter::Subscriber.new({}, {}, {}, {}) }
|
53
53
|
context 'web not enabled' do
|
54
54
|
before do
|
55
55
|
allow(Banter::Configuration).to receive(:web_enabled).and_return(false)
|
@@ -76,7 +76,7 @@ describe Banter::Subscriber do
|
|
76
76
|
end
|
77
77
|
|
78
78
|
describe '#progress' do
|
79
|
-
let(:subscriber) { Banter::Subscriber.new({}, {}, {}) }
|
79
|
+
let(:subscriber) { Banter::Subscriber.new({}, {}, {}, {}) }
|
80
80
|
context 'web not enabled' do
|
81
81
|
before do
|
82
82
|
allow(Banter::Configuration).to receive(:web_enabled).and_return(false)
|
@@ -112,18 +112,18 @@ describe Banter::Subscriber do
|
|
112
112
|
context "One validator" do
|
113
113
|
let(:validators){ [lambda{|payload| true}] }
|
114
114
|
it {
|
115
|
-
expect(Klass.new({},{}, {}).payload_validators.length).to eq 1 }
|
115
|
+
expect(Klass.new({},{}, {}, {}).payload_validators.length).to eq 1 }
|
116
116
|
end
|
117
117
|
|
118
118
|
context "Two validators" do
|
119
119
|
let(:validators){ [lambda{|payload| true}, lambda{|payload| true}] }
|
120
|
-
it { expect(Klass.new({},{}, {}).payload_validators.length).to eq 2 }
|
120
|
+
it { expect(Klass.new({},{}, {}, {}).payload_validators.length).to eq 2 }
|
121
121
|
end
|
122
122
|
end
|
123
123
|
|
124
124
|
describe '#valid_payload?' do
|
125
125
|
context
|
126
|
-
subject { Klass.new({},{}, {}).valid_payload?({}) }
|
126
|
+
subject { Klass.new({},{}, {},{}).valid_payload?({}) }
|
127
127
|
|
128
128
|
before do
|
129
129
|
validators.each do |validator|
|
@@ -222,7 +222,7 @@ describe Banter::Subscriber do
|
|
222
222
|
before { Klass.subscribe_to 'a.b.c' }
|
223
223
|
it 'sets the routing key and queue name' do
|
224
224
|
expect(Klass.subscribed_key).to eq('a.b.c')
|
225
|
-
expect(Klass.
|
225
|
+
expect(Klass.config[:queue_name]).to eq('a.b.c_app_name')
|
226
226
|
end
|
227
227
|
end
|
228
228
|
|
@@ -230,7 +230,7 @@ describe Banter::Subscriber do
|
|
230
230
|
before { Klass.subscribe_to 'a.b.c' }
|
231
231
|
it 'sets the routing key and queue name' do
|
232
232
|
expect(Klass.subscribed_key).to eq('a.b.c')
|
233
|
-
expect(Klass.
|
233
|
+
expect(Klass.config[:queue_name]).to eq('a.b.c_app_name')
|
234
234
|
end
|
235
235
|
end
|
236
236
|
|
@@ -238,7 +238,7 @@ describe Banter::Subscriber do
|
|
238
238
|
before { Klass.subscribe_to 'a_b.c' }
|
239
239
|
it 'is allowed' do
|
240
240
|
expect(Klass.subscribed_key).to eq('a_b.c')
|
241
|
-
expect(Klass.
|
241
|
+
expect(Klass.config[:queue_name]).to eq('a_b.c_app_name')
|
242
242
|
end
|
243
243
|
end
|
244
244
|
|
@@ -252,16 +252,16 @@ describe Banter::Subscriber do
|
|
252
252
|
|
253
253
|
context "queue_ttl" do
|
254
254
|
context "option provided" do
|
255
|
-
before { Klass.subscribe_to "a.b",
|
255
|
+
before { Klass.subscribe_to "a.b", ttl: 4 }
|
256
256
|
it 'sets the ttl' do
|
257
|
-
expect(Klass.
|
257
|
+
expect(Klass.config[:ttl]).to eq(4)
|
258
258
|
end
|
259
259
|
end
|
260
260
|
|
261
261
|
context "option not provided" do
|
262
262
|
before { Klass.subscribe_to "a.b" }
|
263
263
|
it 'uses default' do
|
264
|
-
expect(Klass.
|
264
|
+
expect(Klass.config[:ttl]).to eq(Banter::Configuration.default_queue_ttl)
|
265
265
|
end
|
266
266
|
end
|
267
267
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: banter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- The Honest Company
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2014-10-
|
14
|
+
date: 2014-10-20 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: bundler
|