banter 1.2.4 → 1.3.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.
- 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
|