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