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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 07b71e3cd5e4d50cdbadff4bfc6aeb90cffa749c
4
- data.tar.gz: 541920f865408bedb92d9b9bd0b0490adf7f13d1
3
+ metadata.gz: 20ba1b2e3b2fc1f89b3d25af6d5087e894ce4f87
4
+ data.tar.gz: bab54a9008cf00eb37f8c682a5c32ea199017f0c
5
5
  SHA512:
6
- metadata.gz: 656c84442869c91309fcbb93c1303b527c4b03b107d53cc4354154928e7a0a0273e9e9ad5bdbeafc0b841141d8f7c94e40e5adb0b7cc43952db1a832cc8ade44
7
- data.tar.gz: 943010a912bd147ca77ec5d5c6658b89688c74d2c4b194a9eee2bcbbd5777bbcf935e6cc19234a28c6ca7937dc4e500420b13891b59296371a0fb5b2adc17b53
6
+ metadata.gz: 1219d927b814c1ac6fdc88937d03082b757457c1a68bc454184d8adfb4c9c245b214d4faf886992a5a6fefd98dc1abcb7a7dc1613ca2344dcf0f3c1150119574
7
+ data.tar.gz: 7a303f0e8e01d9891811e8ad06dba142d0dcc4074c8e6c880627d0d313a0956f6122e2d4379433fc2b92532d9f7192388cbf2b52c548938c03dba8e14bc1e088
@@ -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
@@ -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
- to_hash
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
@@ -12,8 +12,14 @@ module Banter
12
12
  @@publisher
13
13
  end
14
14
 
15
- def initialize(exchange = nil)
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.topic(@exchange, :durable => true, :auto_delete => false)
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
- private
72
- def execute_publish(routing_key, envelope)
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
- @publisher.publish(envelope.to_json, :persistent => true, :mandatory => true, :timestamp => envelope[:ts], :content_type => "application/json", :routing_key => routing_key)
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
@@ -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.to_s].map{|x| utf(x)}
40
- logger.tagged(tags) { logger.send(@@log_map[log_level], utf(message.as_json.to_s) ) }
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, request_key, queue_name, durable = true, pool_size = 100, topic = nil)
14
- @topic = topic || Banter::Configuration.topic_prefix
15
- @request_key = request_key
13
+ def initialize(worker_class)
16
14
  @worker_class = worker_class
17
- @queue_name = queue_name
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
- def initialize(routing_key, queue_name, ttl, durable = true, pool_size = 100, topic = Banter::Configuration.topic_prefix)
13
- @initial_key = routing_key
14
- @durable = durable
15
- @topic = topic
16
- @ttl = ttl
17
- @pool_size = pool_size
18
-
19
- if @initial_key.present?
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
- else
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
- @exchange = @channel.topic(@topic, :durable => @durable, :auto_delete => false)
44
+
45
+ @exchange = @channel.topic(@exchange_name, :durable => @durable, :auto_delete => false)
40
46
 
41
47
  queue_arguments = {}
42
- queue_arguments["x-dead-letter-exchange"] = Configuration.dead_letter_queue if Configuration.dead_letter_queue.present?
43
- queue_arguments["x-message-ttl"] = ttl * 1000
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 workers"
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(subscriber)
98
- application_name = Banter::Configuration.application_name
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
@@ -8,8 +8,8 @@ module Banter
8
8
  class Subscriber
9
9
  @@registered_subscribers = []
10
10
 
11
- class_attribute :payload_validators, :error_handlers, :subscribed_key, :subscribed_queue, :queue_ttl, :application_name, :durable
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] :queue_ttl Time, in seconds, that the message lives on the queue before being either consumer by a subscriber or being discarded.
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
- options.assert_valid_keys(:durable, :queue_ttl)
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
- self.subscribed_key = routing_key_name
31
- self.application_name = generated_application_name(options[:on])
32
- self.subscribed_queue = generated_queue_name(routing_key_name, self.application_name)
33
- self.queue_ttl = options[:queue_ttl] || Banter::Configuration.default_queue_ttl
34
- self.durable = options.key?(:durable) ? options[:durable] : true
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
- def initialize(delivery_routing_data, delivery_properties, context)
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
@@ -1,3 +1,3 @@
1
1
  module Banter
2
- VERSION = "1.2.4"
2
+ VERSION = "1.3.0"
3
3
  end
@@ -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
- it "should not fail" do
41
- expect{ result }.not_to raise_error()
42
- end
40
+ context "standard message" do
41
+ it "should not fail" do
42
+ expect{ result }.not_to raise_error()
43
+ end
43
44
 
44
- it "should have all the hash items" do
45
- expect(result[:ts]).not_to be_nil
46
- expect(result[:v]).not_to be_nil
47
- expect(result[:pub]).not_to be_nil
48
- expect(result[:payload]).not_to be_nil
49
- end
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
- it "should have the data in the payload" do
52
- expect(result.payload.data).to eq('found')
53
- end
54
+ it "should have the data in the payload" do
55
+ expect(result.payload.data).to eq('found')
56
+ end
54
57
 
55
- it "should have the attributes readable" do
56
- result
57
- expect(subject.ts).not_to be_nil
58
- expect(subject.version).not_to be_nil
59
- expect(subject.pub).not_to be_nil
60
- expect(subject.payload).not_to be_nil
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(:queue_listener) { Banter::Server::ClientQueueListener.new(Klass, routing_key, 'queue') }
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 { should_not raise_exception }
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
- it { should_not raise_exception }
116
- it { expect(klass_instance).to have_received(:perform!).with(payload) }
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 { should_not raise_exception }
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(:subscriber){ Banter::Server::RabbitMQSubscriber.new( routing_key, "queue_name", 100 ) }
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, "foo.bar.one", "foo.bar.one_#{application_name}", durable, 100)
24
- expect(Banter::Server::ClientQueueListener).to have_received(:new).with(MyTestSubscriber2, "foo.bar.two", "foo.bar.two_#{application_name}", durable, 100)
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, "foo.bar.one", "foo.bar.one_#{application_name}", durable, 200)
43
- expect(Banter::Server::ClientQueueListener).to have_received(:new).with(MyTestSubscriber2, "foo.bar.two", "foo.bar.two_#{application_name}", durable, 200)
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.subscribed_queue).to eq('a.b.c_app_name')
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.subscribed_queue).to eq('a.b.c_app_name')
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.subscribed_queue).to eq('a_b.c_app_name')
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", queue_ttl: 4 }
255
+ before { Klass.subscribe_to "a.b", ttl: 4 }
256
256
  it 'sets the ttl' do
257
- expect(Klass.queue_ttl).to eq(4)
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.queue_ttl).to eq(Banter::Configuration.default_queue_ttl)
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.2.4
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-09 00:00:00.000000000 Z
14
+ date: 2014-10-20 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: bundler