shoryuken 1.0.0 → 2.0.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.hound.yml +6 -2
  4. data/.rubocop.yml +50 -0
  5. data/CHANGELOG.md +11 -0
  6. data/Gemfile +4 -1
  7. data/README.md +6 -52
  8. data/examples/bootstrap_queues.rb +1 -1
  9. data/lib/shoryuken/cli.rb +14 -14
  10. data/lib/shoryuken/client.rb +4 -7
  11. data/lib/shoryuken/default_worker_registry.rb +1 -1
  12. data/lib/shoryuken/environment_loader.rb +34 -16
  13. data/lib/shoryuken/extensions/active_job_adapter.rb +0 -1
  14. data/lib/shoryuken/fetcher.rb +8 -6
  15. data/lib/shoryuken/launcher.rb +1 -1
  16. data/lib/shoryuken/logging.rb +4 -6
  17. data/lib/shoryuken/manager.rb +13 -13
  18. data/lib/shoryuken/message.rb +70 -0
  19. data/lib/shoryuken/middleware/chain.rb +2 -2
  20. data/lib/shoryuken/middleware/server/exponential_backoff_retry.rb +50 -0
  21. data/lib/shoryuken/middleware/server/timing.rb +4 -4
  22. data/lib/shoryuken/processor.rb +28 -14
  23. data/lib/shoryuken/queue.rb +84 -0
  24. data/lib/shoryuken/sns_arn.rb +3 -3
  25. data/lib/shoryuken/util.rb +5 -6
  26. data/lib/shoryuken/version.rb +1 -1
  27. data/lib/shoryuken/worker.rb +1 -2
  28. data/lib/shoryuken.rb +29 -7
  29. data/shoryuken.gemspec +15 -16
  30. data/spec/shoryuken/client_spec.rb +45 -4
  31. data/spec/shoryuken/default_worker_registry_spec.rb +1 -1
  32. data/spec/shoryuken/fetcher_spec.rb +6 -6
  33. data/spec/shoryuken/middleware/chain_spec.rb +3 -1
  34. data/spec/shoryuken/middleware/server/auto_delete_spec.rb +2 -2
  35. data/spec/shoryuken/middleware/server/exponential_backoff_retry_spec.rb +77 -0
  36. data/spec/shoryuken/middleware/server/timing_spec.rb +23 -9
  37. data/spec/shoryuken/processor_spec.rb +39 -13
  38. data/spec/shoryuken/queue_spec.rb +100 -0
  39. data/spec/shoryuken/util_spec.rb +1 -1
  40. data/spec/shoryuken/worker_spec.rb +0 -13
  41. data/spec/shoryuken_endpoint.yml +6 -0
  42. data/spec/spec_helper.rb +4 -4
  43. metadata +36 -39
@@ -0,0 +1,70 @@
1
+ module Shoryuken
2
+ class Message
3
+ attr_accessor :client, :queue_url, :queue_name, :data
4
+
5
+ def initialize(client, queue, data)
6
+ self.client = client
7
+ self.data = data
8
+
9
+ if queue.is_a?(Shoryuken::Queue)
10
+ self.queue_url = queue.url
11
+ self.queue_name = queue.name
12
+ else
13
+ # TODO: Remove next major release
14
+ Shoryuken.loggger.warn do
15
+ '[DEPRECATION] Passing a queue url into Shoryuken::Message is deprecated, please pass the queue itself'
16
+ end
17
+ self.queue_url = queue
18
+ end
19
+ end
20
+
21
+ def delete
22
+ client.delete_message(
23
+ queue_url: queue_url,
24
+ receipt_handle: data.receipt_handle
25
+ )
26
+ end
27
+
28
+ def change_visibility(options)
29
+ client.change_message_visibility(
30
+ options.merge(queue_url: queue_url, receipt_handle: data.receipt_handle)
31
+ )
32
+ end
33
+
34
+ def visibility_timeout=(timeout)
35
+ client.change_message_visibility(
36
+ queue_url: queue_url,
37
+ receipt_handle: data.receipt_handle,
38
+ visibility_timeout: timeout
39
+ )
40
+ end
41
+
42
+ def message_id
43
+ data.message_id
44
+ end
45
+
46
+ def receipt_handle
47
+ data.receipt_handle
48
+ end
49
+
50
+ def md5_of_body
51
+ data.md5_of_body
52
+ end
53
+
54
+ def body
55
+ data.body
56
+ end
57
+
58
+ def attributes
59
+ data.attributes
60
+ end
61
+
62
+ def md5_of_message_attributes
63
+ data.md5_of_message_attributes
64
+ end
65
+
66
+ def message_attributes
67
+ data.message_attributes
68
+ end
69
+ end
70
+ end
@@ -72,7 +72,7 @@ module Shoryuken
72
72
  i = entries.index { |entry| entry.klass == newklass }
73
73
  new_entry = i.nil? ? Entry.new(newklass, *args) : entries.delete_at(i)
74
74
  i = entries.find_index { |entry| entry.klass == oldklass } || entries.count - 1
75
- entries.insert(i+1, new_entry)
75
+ entries.insert(i + 1, new_entry)
76
76
  end
77
77
 
78
78
  def exists?(klass)
@@ -117,7 +117,7 @@ module Shoryuken
117
117
 
118
118
  def patch_deprecated_middleware!(klass)
119
119
  if klass.instance_method(:call).arity == 3
120
- Shoryuken.logger.warn "[DEPRECATION] #{klass.name}#call(worker_instance, queue, sqs_msg) is deprecated. Please use #{klass.name}#call(worker_instance, queue, sqs_msg, body)"
120
+ Shoryuken.logger.warn { "[DEPRECATION] #{klass.name}#call(worker_instance, queue, sqs_msg) is deprecated. Please use #{klass.name}#call(worker_instance, queue, sqs_msg, body)" }
121
121
 
122
122
  klass.class_eval do
123
123
  alias_method :deprecated_call, :call
@@ -0,0 +1,50 @@
1
+ module Shoryuken
2
+ module Middleware
3
+ module Server
4
+ class ExponentialBackoffRetry
5
+ include Util
6
+
7
+ def call(worker, queue, sqs_msg, body)
8
+ started_at = Time.now
9
+ yield
10
+ rescue
11
+ retry_intervals = Array(worker.class.get_shoryuken_options['retry_intervals'])
12
+
13
+ if retry_intervals.empty? || !handle_failure(sqs_msg, started_at, retry_intervals)
14
+ # Re-raise the exception if the job is not going to be exponential backoff retried.
15
+ # This allows custom middleware (like exception notifiers) to be aware of the unhandled failure.
16
+ raise
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def handle_failure(sqs_msg, started_at, retry_intervals)
23
+ attempts = sqs_msg.attributes['ApproximateReceiveCount']
24
+
25
+ return unless attempts
26
+
27
+ attempts = attempts.to_i - 1
28
+
29
+ interval = if attempts < retry_intervals.size
30
+ retry_intervals[attempts]
31
+ else
32
+ retry_intervals.last
33
+ end
34
+
35
+ # Visibility timeouts are limited to a total 12 hours, starting from the receipt of the message.
36
+ # We calculate the maximum timeout by subtracting the amount of time since the receipt of the message.
37
+ #
38
+ # From the docs: "Amazon SQS restarts the timeout period using the new value."
39
+ # http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/AboutVT.html#AboutVT-extending-message-visibility-timeout
40
+ max_timeout = 43200 - (Time.now - started_at).ceil - 1
41
+ interval = max_timeout if interval > max_timeout
42
+
43
+ sqs_msg.change_visibility(visibility_timeout: interval.to_i)
44
+
45
+ logger.info { "Message #{sqs_msg.message_id} failed, will be retried in #{interval} seconds." }
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -9,19 +9,19 @@ module Shoryuken
9
9
  begin
10
10
  started_at = Time.now
11
11
 
12
- logger.info "started at #{started_at}"
12
+ logger.info { "started at #{started_at}" }
13
13
 
14
14
  yield
15
15
 
16
16
  total_time = elapsed(started_at)
17
17
 
18
18
  if (total_time / 1000.0) > (timeout = Shoryuken::Client.queues(queue).visibility_timeout)
19
- logger.warn "exceeded the queue visibility timeout by #{total_time - (timeout * 1000)} ms"
19
+ logger.warn { "exceeded the queue visibility timeout by #{total_time - (timeout * 1000)} ms" }
20
20
  end
21
21
 
22
- logger.info "completed in: #{total_time} ms"
22
+ logger.info { "completed in: #{total_time} ms" }
23
23
  rescue => e
24
- logger.info "failed in: #{elapsed(started_at)} ms"
24
+ logger.info { "failed in: #{elapsed(started_at)} ms" }
25
25
  raise e
26
26
  end
27
27
  end
@@ -19,12 +19,10 @@ module Shoryuken
19
19
  timer = auto_visibility_timeout(queue, sqs_msg, worker.class)
20
20
 
21
21
  begin
22
- defer do
23
- body = get_body(worker.class, sqs_msg)
22
+ body = get_body(worker.class, sqs_msg)
24
23
 
25
- worker.class.server_middleware.invoke(worker, queue, sqs_msg, body) do
26
- worker.perform(sqs_msg, body)
27
- end
24
+ worker.class.server_middleware.invoke(worker, queue, sqs_msg, body) do
25
+ worker.perform(sqs_msg, body)
28
26
  end
29
27
 
30
28
  @manager.async.processor_done(queue, current_actor)
@@ -35,22 +33,31 @@ module Shoryuken
35
33
 
36
34
  private
37
35
 
38
- def auto_visibility_timeout(queue, sqs_msg, worker_class)
39
- if worker_class.auto_visibility_timeout?
36
+ class MessageVisibilityExtender
37
+ include Celluloid
38
+ include Util
39
+
40
+ def auto_extend(queue, sqs_msg, worker_class)
40
41
  queue_visibility_timeout = Shoryuken::Client.queues(queue).visibility_timeout
41
42
 
42
- timer = every(queue_visibility_timeout - 5) do
43
+ every(queue_visibility_timeout - 5) do
43
44
  begin
44
- logger.debug "Extending message #{worker_name(worker_class, sqs_msg)}/#{queue}/#{sqs_msg.message_id} visibility timeout by #{queue_visibility_timeout}s."
45
+ logger.debug { "Extending message #{worker_name(worker_class, sqs_msg)}/#{queue}/#{sqs_msg.message_id} visibility timeout by #{queue_visibility_timeout}s." }
45
46
 
46
47
  sqs_msg.visibility_timeout = queue_visibility_timeout
47
48
  rescue => e
48
- logger.error "Could not auto extend the message #{worker_class}/#{queue}/#{sqs_msg.message_id} visibility timeout. Error: #{e.message}"
49
+ logger.error { "Could not auto extend the message #{worker_class}/#{queue}/#{sqs_msg.message_id} visibility timeout. Error: #{e.message}" }
49
50
  end
50
51
  end
51
52
  end
53
+ end
52
54
 
53
- timer
55
+ def auto_visibility_timeout(queue, sqs_msg, worker_class)
56
+ return unless worker_class.auto_visibility_timeout?
57
+
58
+ @visibility_extender ||= MessageVisibilityExtender.new_link
59
+
60
+ @visibility_extender.auto_extend(queue, sqs_msg, worker_class)
54
61
  end
55
62
 
56
63
  def get_body(worker_class, sqs_msg)
@@ -72,11 +79,18 @@ module Shoryuken
72
79
  when :text, nil
73
80
  sqs_msg.body
74
81
  else
75
- body_parser.parse(sqs_msg.body) if body_parser.respond_to?(:parse) # i.e. JSON.parse(...)
82
+ if body_parser.respond_to?(:parse)
83
+ # JSON.parse
84
+ body_parser.parse(sqs_msg.body)
85
+ elsif body_parser.respond_to?(:load)
86
+ # see https://github.com/phstc/shoryuken/pull/91
87
+ # JSON.load
88
+ body_parser.load(sqs_msg.body)
89
+ end
76
90
  end
77
91
  rescue => e
78
- logger.error "Error parsing the message body: #{e.message}\nbody_parser: #{body_parser}\nsqs_msg.body: #{sqs_msg.body}"
79
- nil
92
+ logger.error { "Error parsing the message body: #{e.message}\nbody_parser: #{body_parser}\nsqs_msg.body: #{sqs_msg.body}" }
93
+ raise
80
94
  end
81
95
  end
82
96
  end
@@ -0,0 +1,84 @@
1
+ module Shoryuken
2
+ class Queue
3
+ attr_accessor :name, :client, :url
4
+
5
+ def initialize(client, name)
6
+ self.name = name
7
+ self.client = client
8
+ self.url = client.get_queue_url(queue_name: name).queue_url
9
+ end
10
+
11
+ def visibility_timeout
12
+ client.get_queue_attributes(
13
+ queue_url: url,
14
+ attribute_names: ['VisibilityTimeout']
15
+ ).attributes['VisibilityTimeout'].to_i
16
+ end
17
+
18
+ def delete_messages(options)
19
+ client.delete_message_batch(options.merge(queue_url: url))
20
+ end
21
+
22
+ def send_message(options)
23
+ options = sanitize_message!(options).merge(queue_url: url)
24
+
25
+ Shoryuken.client_middleware.invoke(options) do
26
+ client.send_message(options)
27
+ end
28
+ end
29
+
30
+ def send_messages(options)
31
+ client.send_message_batch(sanitize_messages!(options).merge(queue_url: url))
32
+ end
33
+
34
+ def receive_messages(options)
35
+ client.receive_message(options.merge(queue_url: url)).
36
+ messages.
37
+ map { |m| Message.new(client, self, m) }
38
+ end
39
+
40
+ private
41
+
42
+ def sanitize_messages!(options)
43
+ options = case
44
+ when options.is_a?(Array)
45
+ { entries: options.map.with_index { |m, index| { id: index.to_s, message_body: m } } }
46
+ when options.is_a?(Hash)
47
+ options
48
+ end
49
+
50
+ validate_messages!(options)
51
+
52
+ options
53
+ end
54
+
55
+ def sanitize_message!(options)
56
+ options = case
57
+ when options.is_a?(String)
58
+ # send_message('message')
59
+ { message_body: options }
60
+ when options.is_a?(Hash)
61
+ options
62
+ end
63
+
64
+ validate_message!(options)
65
+
66
+ options
67
+ end
68
+
69
+ def validate_messages!(options)
70
+ options[:entries].map { |m| validate_message!(m) }
71
+ end
72
+
73
+ def validate_message!(options)
74
+ body = options[:message_body]
75
+ if body.is_a?(Hash)
76
+ options[:message_body] = JSON.dump(body)
77
+ elsif !body.is_a?(String)
78
+ fail ArgumentError, "The message body must be a String and you passed a #{body.class}"
79
+ end
80
+
81
+ options
82
+ end
83
+ end
84
+ end
@@ -1,6 +1,6 @@
1
1
  module Shoryuken
2
2
  class SnsArn
3
- def initialize topic
3
+ def initialize(topic)
4
4
  @topic = topic
5
5
  end
6
6
 
@@ -13,14 +13,14 @@ module Shoryuken
13
13
  def account_id
14
14
  Shoryuken::Client.account_id.tap do |account_id|
15
15
  if account_id.to_s.empty?
16
- fail "To generate SNS ARNs, you must assign an :account_id in your Shoryuken::Client."
16
+ fail 'To generate SNS ARNs, you must assign an :account_id in your Shoryuken::Client.'
17
17
  end
18
18
  end
19
19
  end
20
20
 
21
21
  def region
22
22
  Aws.config.fetch(:region) do
23
- fail "To generate SNS ARNs, you must include a :region in your AWS config."
23
+ fail 'To generate SNS ARNs, you must include a :region in your AWS config.'
24
24
  end
25
25
  end
26
26
  end
@@ -3,9 +3,9 @@ module Shoryuken
3
3
  def watchdog(last_words)
4
4
  yield
5
5
  rescue => ex
6
- logger.error last_words
7
- logger.error ex
8
- logger.error ex.backtrace.join("\n")
6
+ logger.error { last_words }
7
+ logger.error { ex }
8
+ logger.error { ex.backtrace.join("\n") }
9
9
  end
10
10
 
11
11
  def logger
@@ -18,9 +18,8 @@ module Shoryuken
18
18
  end
19
19
 
20
20
  def unparse_queues(queues)
21
- queues.inject({}) do |queue_and_weights, name|
21
+ queues.each_with_object({}) do |name, queue_and_weights|
22
22
  queue_and_weights[name] = queue_and_weights[name].to_i + 1
23
- queue_and_weights
24
23
  end.to_a
25
24
  end
26
25
 
@@ -31,7 +30,7 @@ module Shoryuken
31
30
  && sqs_msg.message_attributes['shoryuken_class'] \
32
31
  && sqs_msg.message_attributes['shoryuken_class'][:string_value] == ActiveJob::QueueAdapters::ShoryukenAdapter::JobWrapper.to_s
33
32
 
34
- "ActiveJob/#{body['job_class'].constantize}"
33
+ "ActiveJob/#{body['job_class']}"
35
34
  else
36
35
  worker_class.to_s
37
36
  end
@@ -1,3 +1,3 @@
1
1
  module Shoryuken
2
- VERSION = '1.0.0'
2
+ VERSION = '2.0.0'
3
3
  end
@@ -13,7 +13,6 @@ module Shoryuken
13
13
  data_type: 'String'
14
14
  }
15
15
 
16
- body = JSON.dump(body) if body.is_a?(Hash)
17
16
  options[:message_body] = body
18
17
 
19
18
  Shoryuken::Client.queues(get_shoryuken_options['queue']).send_message(options)
@@ -40,7 +39,7 @@ module Shoryuken
40
39
  end
41
40
 
42
41
  def shoryuken_options(opts = {})
43
- @shoryuken_options = get_shoryuken_options.merge(stringify_keys(Hash(opts)))
42
+ @shoryuken_options = get_shoryuken_options.merge(stringify_keys(opts || {}))
44
43
  queue = @shoryuken_options['queue']
45
44
  if queue.respond_to? :call
46
45
  queue = queue.call
data/lib/shoryuken.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  require 'yaml'
2
2
  require 'aws-sdk-core'
3
- require 'aws-sdk-resources'
4
3
  require 'time'
5
4
 
6
5
  require 'shoryuken/version'
@@ -8,12 +7,15 @@ require 'shoryuken/core_ext'
8
7
  require 'shoryuken/util'
9
8
  require 'shoryuken/logging'
10
9
  require 'shoryuken/environment_loader'
10
+ require 'shoryuken/queue'
11
+ require 'shoryuken/message'
11
12
  require 'shoryuken/client'
12
13
  require 'shoryuken/worker'
13
14
  require 'shoryuken/worker_registry'
14
15
  require 'shoryuken/default_worker_registry'
15
16
  require 'shoryuken/middleware/chain'
16
17
  require 'shoryuken/middleware/server/auto_delete'
18
+ require 'shoryuken/middleware/server/exponential_backoff_retry'
17
19
  require 'shoryuken/middleware/server/timing'
18
20
  require 'shoryuken/sns_arn'
19
21
  require 'shoryuken/topic'
@@ -27,8 +29,9 @@ module Shoryuken
27
29
  timeout: 8
28
30
  }
29
31
 
30
- @@queues = []
32
+ @@queues = []
31
33
  @@worker_registry = DefaultWorkerRegistry.new
34
+ @@active_job_queue_name_prefixing = false
32
35
 
33
36
  class << self
34
37
  def options
@@ -55,11 +58,14 @@ module Shoryuken
55
58
  @@worker_registry
56
59
  end
57
60
 
58
- # Shoryuken.configure_server do |config|
59
- # config.server_middleware do |chain|
60
- # chain.add MyServerHook
61
- # end
62
- # end
61
+ def active_job_queue_name_prefixing
62
+ @@active_job_queue_name_prefixing
63
+ end
64
+
65
+ def active_job_queue_name_prefixing=(prefixing)
66
+ @@active_job_queue_name_prefixing = prefixing
67
+ end
68
+
63
69
  def configure_server
64
70
  yield self if server?
65
71
  end
@@ -70,12 +76,23 @@ module Shoryuken
70
76
  @server_chain
71
77
  end
72
78
 
79
+ def configure_client
80
+ yield self
81
+ end
82
+
83
+ def client_middleware
84
+ @client_chain ||= default_client_middleware
85
+ yield @client_chain if block_given?
86
+ @client_chain
87
+ end
88
+
73
89
  def default_worker_options
74
90
  @@default_worker_options ||= {
75
91
  'queue' => 'default',
76
92
  'delete' => false,
77
93
  'auto_delete' => false,
78
94
  'auto_visibility_timeout' => false,
95
+ 'retry_intervals' => nil,
79
96
  'batch' => false }
80
97
  end
81
98
 
@@ -104,6 +121,7 @@ module Shoryuken
104
121
  def default_server_middleware
105
122
  Middleware::Chain.new do |m|
106
123
  m.add Middleware::Server::Timing
124
+ m.add Middleware::Server::ExponentialBackoffRetry
107
125
  m.add Middleware::Server::AutoDelete
108
126
  if defined?(::ActiveRecord::Base)
109
127
  require 'shoryuken/middleware/server/active_record'
@@ -112,6 +130,10 @@ module Shoryuken
112
130
  end
113
131
  end
114
132
 
133
+ def default_client_middleware
134
+ Middleware::Chain.new
135
+ end
136
+
115
137
  def server?
116
138
  defined?(Shoryuken::CLI)
117
139
  end
data/shoryuken.gemspec CHANGED
@@ -4,27 +4,26 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'shoryuken/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "shoryuken"
7
+ spec.name = 'shoryuken'
8
8
  spec.version = Shoryuken::VERSION
9
- spec.authors = ["Pablo Cantero"]
10
- spec.email = ["pablo@pablocantero.com"]
11
- spec.description = spec.summary = %q{Shoryuken is a super efficient AWS SQS thread based message processor}
12
- spec.homepage = "https://github.com/phstc/shoryuken"
13
- spec.license = "LGPL-3.0"
9
+ spec.authors = ['Pablo Cantero']
10
+ spec.email = ['pablo@pablocantero.com']
11
+ spec.description = spec.summary = %q(Shoryuken is a super efficient AWS SQS thread based message processor)
12
+ spec.homepage = 'https://github.com/phstc/shoryuken'
13
+ spec.license = 'LGPL-3.0'
14
14
 
15
15
  spec.files = `git ls-files -z`.split("\x0")
16
16
  spec.executables = %w[shoryuken]
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
- spec.require_paths = ["lib"]
18
+ spec.require_paths = ['lib']
19
19
 
20
- spec.add_development_dependency "bundler", "~> 1.6"
21
- spec.add_development_dependency "rake"
22
- spec.add_development_dependency "rspec"
23
- spec.add_development_dependency "pry-byebug"
24
- spec.add_development_dependency "nokogiri"
25
- spec.add_development_dependency "dotenv"
20
+ spec.add_development_dependency 'bundler', '~> 1.6'
21
+ spec.add_development_dependency 'rake'
22
+ spec.add_development_dependency 'rspec'
23
+ spec.add_development_dependency 'pry-byebug'
24
+ spec.add_development_dependency 'nokogiri'
25
+ spec.add_development_dependency 'dotenv'
26
26
 
27
- spec.add_dependency "aws-sdk-core", "2.0.21"
28
- spec.add_dependency "aws-sdk-resources", "2.0.21.pre"
29
- spec.add_dependency "celluloid", "~> 0.16.0"
27
+ spec.add_dependency 'aws-sdk-core', '~> 2.0.21'
28
+ spec.add_dependency 'celluloid', '~> 0.16.0'
30
29
  end
@@ -5,12 +5,13 @@ describe Shoryuken::Client do
5
5
  let(:sqs) { Aws::SQS::Client.new(stub_responses: true, credentials: credentials) }
6
6
  let(:queue_name) { 'shoryuken' }
7
7
  let(:queue_url) { 'https://eu-west-1.amazonaws.com:6059/123456789012/shoryuken' }
8
-
9
- before do
10
- described_class.sqs = sqs
11
- end
8
+ let(:sqs_endpoint) { 'http://localhost:4568' }
9
+ let(:sns_endpoint) { 'http://0.0.0.0:4568' }
12
10
 
13
11
  describe '.queue' do
12
+ before do
13
+ described_class.sqs = sqs
14
+ end
14
15
  it 'memoizes queues' do
15
16
  sqs.stub_responses(:get_queue_url, { queue_url: queue_url }, { queue_url: 'xyz' })
16
17
 
@@ -18,4 +19,44 @@ describe Shoryuken::Client do
18
19
  expect(Shoryuken::Client.queues(queue_name).url).to eq queue_url
19
20
  end
20
21
  end
22
+
23
+ describe 'environment variable endpoints' do
24
+ before do
25
+ ENV['AWS_SQS_ENDPOINT'] = sqs_endpoint
26
+ ENV['AWS_SNS_ENDPOINT'] = sns_endpoint
27
+ ENV['AWS_REGION'] = 'us-east-1'
28
+ Shoryuken.options[:aws] = {}
29
+ end
30
+
31
+ it 'will use config file settings if set' do
32
+ load_config_file_by_file_name('shoryuken_endpoint.yml')
33
+ expect(described_class.sqs.config.endpoint.to_s).to eql('https://github.com/phstc/shoryuken:4568')
34
+ expect(described_class.sns.config.endpoint.to_s).to eq('http://127.0.0.1:4568')
35
+ end
36
+
37
+ it 'should fallback to environment variable if config file not found or set' do
38
+ load_config_file_by_file_name(nil)
39
+ expect(described_class.sqs.config.endpoint.to_s).to eql(sqs_endpoint)
40
+ expect(described_class.sns.config.endpoint.to_s).to eq(sns_endpoint)
41
+ end
42
+
43
+ it 'should fallback to environment variable if config file found but settings not set' do
44
+ load_config_file_by_file_name('shoryuken.yml')
45
+ expect(described_class.sqs.config.endpoint.to_s).to eql(sqs_endpoint)
46
+ expect(described_class.sns.config.endpoint.to_s).to eq(sns_endpoint)
47
+ end
48
+
49
+ it 'will fallback to default settings if no config file settings or environment variables found' do
50
+ ENV['AWS_SQS_ENDPOINT'] = nil
51
+ ENV['AWS_SNS_ENDPOINT'] = nil
52
+ load_config_file_by_file_name('shoryuken.yml')
53
+ expect(described_class.sqs.config.endpoint.to_s).to eql('https://sqs.us-east-1.amazonaws.com')
54
+ expect(described_class.sns.config.endpoint.to_s).to eq('https://sns.us-east-1.amazonaws.com')
55
+ end
56
+ end
57
+
58
+ def load_config_file_by_file_name(file_name)
59
+ path_name = file_name ? File.join(File.expand_path('../../..', __FILE__), 'spec', file_name) : nil
60
+ Shoryuken::EnvironmentLoader.load(config_file: path_name)
61
+ end
21
62
  end
@@ -48,7 +48,7 @@ describe Shoryuken::DefaultWorkerRegistry do
48
48
  string_value: explicit_worker.to_s,
49
49
  data_type: 'String' } if explicit_worker
50
50
 
51
- double Aws::SQS::Message,
51
+ double Shoryuken::Message,
52
52
  body: 'test',
53
53
  message_attributes: attributes,
54
54
  message_id: SecureRandom.uuid
@@ -4,11 +4,11 @@ require 'shoryuken/fetcher'
4
4
 
5
5
  describe Shoryuken::Fetcher do
6
6
  let(:manager) { double Shoryuken::Manager }
7
- let(:queue) { double Aws::SQS::Queue }
7
+ let(:queue) { double Shoryuken::Queue }
8
8
  let(:queue_name) { 'default' }
9
9
 
10
10
  let(:sqs_msg) do
11
- double Aws::SQS::Message,
11
+ double Shoryuken::Message,
12
12
  queue_url: queue_name,
13
13
  body: 'test',
14
14
  message_id: 'fc754df7-9cc2-4c41-96ca-5996a44b771e'
@@ -24,7 +24,7 @@ describe Shoryuken::Fetcher do
24
24
 
25
25
  describe '#fetch' do
26
26
  it 'calls pause when no message' do
27
- allow(queue).to receive(:receive_messages).with(max_number_of_messages: 1, message_attribute_names: ['All']).and_return([])
27
+ allow(queue).to receive(:receive_messages).with(max_number_of_messages: 1, attribute_names: ['All'], message_attribute_names: ['All']).and_return([])
28
28
 
29
29
  expect(manager).to receive(:pause_queue!).with(queue_name)
30
30
  expect(manager).to receive(:dispatch)
@@ -33,7 +33,7 @@ describe Shoryuken::Fetcher do
33
33
  end
34
34
 
35
35
  it 'assigns messages' do
36
- allow(queue).to receive(:receive_messages).with(max_number_of_messages: 5, message_attribute_names: ['All']).and_return(sqs_msg)
36
+ allow(queue).to receive(:receive_messages).with(max_number_of_messages: 5, attribute_names: ['All'], message_attribute_names: ['All']).and_return(sqs_msg)
37
37
 
38
38
  expect(manager).to receive(:rebalance_queue_weight!).with(queue_name)
39
39
  expect(manager).to receive(:assign).with(queue_name, sqs_msg)
@@ -45,7 +45,7 @@ describe Shoryuken::Fetcher do
45
45
  it 'assigns messages in batch' do
46
46
  TestWorker.get_shoryuken_options['batch'] = true
47
47
 
48
- allow(queue).to receive(:receive_messages).with(max_number_of_messages: described_class::FETCH_LIMIT, message_attribute_names: ['All']).and_return(sqs_msg)
48
+ allow(queue).to receive(:receive_messages).with(max_number_of_messages: described_class::FETCH_LIMIT, attribute_names: ['All'], message_attribute_names: ['All']).and_return(sqs_msg)
49
49
 
50
50
  expect(manager).to receive(:rebalance_queue_weight!).with(queue_name)
51
51
  expect(manager).to receive(:assign).with(queue_name, [sqs_msg])
@@ -58,7 +58,7 @@ describe Shoryuken::Fetcher do
58
58
  let(:queue_name) { 'notfound' }
59
59
 
60
60
  it 'ignores batch' do
61
- allow(queue).to receive(:receive_messages).with(max_number_of_messages: 5, message_attribute_names: ['All']).and_return(sqs_msg)
61
+ allow(queue).to receive(:receive_messages).with(max_number_of_messages: 5, attribute_names: ['All'], message_attribute_names: ['All']).and_return(sqs_msg)
62
62
 
63
63
  expect(manager).to receive(:rebalance_queue_weight!).with(queue_name)
64
64
  expect(manager).to receive(:assign).with(queue_name, sqs_msg)