pwwka 0.10.0 → 0.11.0.RC1
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/.ruby-version +1 -1
- data/Gemfile.lock +53 -46
- data/README.md +29 -0
- data/lib/pwwka/configuration.rb +17 -0
- data/lib/pwwka/publish_options.rb +30 -0
- data/lib/pwwka/send_message_async_job.rb +14 -2
- data/lib/pwwka/transmitter.rb +47 -22
- data/lib/pwwka/version.rb +1 -1
- data/pwwka.gemspec +1 -0
- data/spec/integration/send_and_receive_spec.rb +149 -51
- data/spec/integration/support/logging_receiver.rb +8 -1
- data/spec/spec_helper.rb +11 -1
- data/spec/support/test_configuration.rb +35 -1
- data/spec/unit/configuration_spec.rb +82 -0
- data/spec/unit/send_message_async_job_spec.rb +41 -0
- data/spec/unit/transmitter_spec.rb +219 -31
- metadata +24 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3694a0647439ed09e9d80f90aa35d3b16724ad9f
|
|
4
|
+
data.tar.gz: 0d6630ecdf0aa36954b24606378a618fa36f8e38
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2dcb9350aef4c3f9eaf4b5691d3d39216c0e08e4bd606060565c68cb9bc41ba14f21d8ac5c451950bec1656f0d664fc67cd417263fe88543e4cdef185018a8a7
|
|
7
|
+
data.tar.gz: b4b94ed53542512a8e74bb456c3a845013d94869143cef7fcfa8d883a2153cd057256115e938720c9f2a39694ba52638976db2324800b071abebc23d696a62e3
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.
|
|
1
|
+
2.4.1
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
pwwka (0.
|
|
4
|
+
pwwka (0.11.0.RC1)
|
|
5
5
|
activemodel
|
|
6
6
|
activesupport
|
|
7
7
|
bunny
|
|
@@ -10,32 +10,37 @@ PATH
|
|
|
10
10
|
GEM
|
|
11
11
|
remote: https://www.rubygems.org/
|
|
12
12
|
specs:
|
|
13
|
-
activemodel (5.
|
|
14
|
-
activesupport (= 5.
|
|
15
|
-
activesupport (5.
|
|
13
|
+
activemodel (5.1.1)
|
|
14
|
+
activesupport (= 5.1.1)
|
|
15
|
+
activesupport (5.1.1)
|
|
16
16
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
17
17
|
i18n (~> 0.7)
|
|
18
18
|
minitest (~> 5.1)
|
|
19
19
|
tzinfo (~> 1.1)
|
|
20
|
-
amq-protocol (2.
|
|
21
|
-
bunny (2.
|
|
22
|
-
amq-protocol (>= 2.0
|
|
23
|
-
concurrent-ruby (1.0.
|
|
24
|
-
diff-lcs (1.
|
|
20
|
+
amq-protocol (2.2.0)
|
|
21
|
+
bunny (2.7.0)
|
|
22
|
+
amq-protocol (>= 2.2.0)
|
|
23
|
+
concurrent-ruby (1.0.5)
|
|
24
|
+
diff-lcs (1.3)
|
|
25
25
|
docile (1.1.5)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
et-orbi (1.0.4)
|
|
27
|
+
tzinfo
|
|
28
|
+
i18n (0.8.1)
|
|
29
|
+
json (2.1.0)
|
|
30
|
+
minitest (5.10.2)
|
|
29
31
|
mono_logger (1.1.0)
|
|
30
|
-
multi_json (1.
|
|
31
|
-
|
|
32
|
-
rack
|
|
32
|
+
multi_json (1.12.1)
|
|
33
|
+
mustermann (1.0.0)
|
|
34
|
+
rack (2.0.3)
|
|
35
|
+
rack-protection (2.0.0)
|
|
33
36
|
rack
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
rainbow (2.2.2)
|
|
38
|
+
rake
|
|
39
|
+
rake (12.0.0)
|
|
40
|
+
redis (3.3.3)
|
|
41
|
+
redis-namespace (1.5.3)
|
|
37
42
|
redis (~> 3.0, >= 3.0.4)
|
|
38
|
-
resque (1.
|
|
43
|
+
resque (1.27.4)
|
|
39
44
|
mono_logger (~> 1.0)
|
|
40
45
|
multi_json (~> 1.0)
|
|
41
46
|
redis-namespace (~> 1.3)
|
|
@@ -44,40 +49,41 @@ GEM
|
|
|
44
49
|
resque-retry (1.5.0)
|
|
45
50
|
resque (~> 1.25)
|
|
46
51
|
resque-scheduler (~> 4.0)
|
|
47
|
-
resque-scheduler (4.
|
|
52
|
+
resque-scheduler (4.3.0)
|
|
48
53
|
mono_logger (~> 1.0)
|
|
49
|
-
redis (~> 3.
|
|
50
|
-
resque (~> 1.
|
|
54
|
+
redis (~> 3.3)
|
|
55
|
+
resque (~> 1.26)
|
|
51
56
|
rufus-scheduler (~> 3.2)
|
|
52
|
-
resqutils (1.2.
|
|
57
|
+
resqutils (1.2.1)
|
|
53
58
|
resque
|
|
54
|
-
rspec (3.
|
|
55
|
-
rspec-core (~> 3.
|
|
56
|
-
rspec-expectations (~> 3.
|
|
57
|
-
rspec-mocks (~> 3.
|
|
58
|
-
rspec-core (3.
|
|
59
|
-
rspec-support (~> 3.
|
|
60
|
-
rspec-expectations (3.
|
|
59
|
+
rspec (3.6.0)
|
|
60
|
+
rspec-core (~> 3.6.0)
|
|
61
|
+
rspec-expectations (~> 3.6.0)
|
|
62
|
+
rspec-mocks (~> 3.6.0)
|
|
63
|
+
rspec-core (3.6.0)
|
|
64
|
+
rspec-support (~> 3.6.0)
|
|
65
|
+
rspec-expectations (3.6.0)
|
|
61
66
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
62
|
-
rspec-support (~> 3.
|
|
63
|
-
rspec-mocks (3.
|
|
67
|
+
rspec-support (~> 3.6.0)
|
|
68
|
+
rspec-mocks (3.6.0)
|
|
64
69
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
65
|
-
rspec-support (~> 3.
|
|
66
|
-
rspec-support (3.
|
|
67
|
-
rufus-scheduler (3.
|
|
68
|
-
|
|
69
|
-
simplecov (0.
|
|
70
|
+
rspec-support (~> 3.6.0)
|
|
71
|
+
rspec-support (3.6.0)
|
|
72
|
+
rufus-scheduler (3.4.2)
|
|
73
|
+
et-orbi (~> 1.0)
|
|
74
|
+
simplecov (0.14.1)
|
|
70
75
|
docile (~> 1.1.0)
|
|
71
76
|
json (>= 1.8, < 3)
|
|
72
77
|
simplecov-html (~> 0.10.0)
|
|
73
|
-
simplecov-html (0.10.
|
|
74
|
-
sinatra (
|
|
75
|
-
|
|
76
|
-
rack
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
simplecov-html (0.10.1)
|
|
79
|
+
sinatra (2.0.0)
|
|
80
|
+
mustermann (~> 1.0)
|
|
81
|
+
rack (~> 2.0)
|
|
82
|
+
rack-protection (= 2.0.0)
|
|
83
|
+
tilt (~> 2.0)
|
|
84
|
+
thread_safe (0.3.6)
|
|
85
|
+
tilt (2.0.7)
|
|
86
|
+
tzinfo (1.2.3)
|
|
81
87
|
thread_safe (~> 0.1)
|
|
82
88
|
vegas (0.1.11)
|
|
83
89
|
rack (>= 1.0.0)
|
|
@@ -87,6 +93,7 @@ PLATFORMS
|
|
|
87
93
|
|
|
88
94
|
DEPENDENCIES
|
|
89
95
|
pwwka!
|
|
96
|
+
rainbow
|
|
90
97
|
rake
|
|
91
98
|
resque
|
|
92
99
|
resque-retry
|
|
@@ -95,4 +102,4 @@ DEPENDENCIES
|
|
|
95
102
|
simplecov
|
|
96
103
|
|
|
97
104
|
BUNDLED WITH
|
|
98
|
-
1.
|
|
105
|
+
1.14.6
|
data/README.md
CHANGED
|
@@ -91,6 +91,35 @@ Pwwka::Transmitter.send_message!(payload, routing_key)
|
|
|
91
91
|
|
|
92
92
|
The payload should be a simple hash containing primitives. Don't send objects because the payload will be converted to JSON for sending.
|
|
93
93
|
|
|
94
|
+
#### AMQP Attributes
|
|
95
|
+
|
|
96
|
+
By default, pwwka will set the following [AMQP Attributes](http://stackoverflow.com/questions/18403623/rabbitmq-amqp-basicproperties-builder-values/18447385#18447385):
|
|
97
|
+
|
|
98
|
+
* `message_id` - a GUID
|
|
99
|
+
* `timestamp` - The time the message is sent
|
|
100
|
+
* `app_id` - the name of your Rails app or, if you aren't using rails, the value of `app_id` given to the configuration
|
|
101
|
+
* `content_type` - `application/json; version=1`
|
|
102
|
+
|
|
103
|
+
You may optionally set the following when sending a message to set these additional attributes:
|
|
104
|
+
|
|
105
|
+
* `message_id` - to override the GUID. Generally don't do this.
|
|
106
|
+
* `type` - a String to define the data type you are sending. Useful for languages with static types to know how to
|
|
107
|
+
deserialize. You should ensure that the combo of `app_id` and `type` are unique to your entire ecosystem or consumers won't
|
|
108
|
+
know what they are receiving.
|
|
109
|
+
* `headers` - a hash of arbitrary headers.
|
|
110
|
+
|
|
111
|
+
A fuller example:
|
|
112
|
+
|
|
113
|
+
```ruby
|
|
114
|
+
Pwwka::Transmitter.send_message!(
|
|
115
|
+
{ "customer_id" => 12345, "active" => true },
|
|
116
|
+
"customers.customer.created",
|
|
117
|
+
type: "Customer",
|
|
118
|
+
headers: {
|
|
119
|
+
"RAILS_VERSION" => "5.1.1"
|
|
120
|
+
}
|
|
121
|
+
)
|
|
122
|
+
```
|
|
94
123
|
|
|
95
124
|
#### Error Handling
|
|
96
125
|
|
data/lib/pwwka/configuration.rb
CHANGED
|
@@ -12,6 +12,7 @@ module Pwwka
|
|
|
12
12
|
attr_accessor :async_job_klass
|
|
13
13
|
attr_accessor :send_message_resque_backoff_strategy
|
|
14
14
|
attr_accessor :requeue_on_error
|
|
15
|
+
attr_writer :app_id
|
|
15
16
|
attr_writer :keep_alive_on_handler_klass_exceptions
|
|
16
17
|
|
|
17
18
|
def initialize
|
|
@@ -32,6 +33,22 @@ module Pwwka
|
|
|
32
33
|
@keep_alive_on_handler_klass_exceptions
|
|
33
34
|
end
|
|
34
35
|
|
|
36
|
+
def app_id
|
|
37
|
+
if @app_id.to_s.strip == ""
|
|
38
|
+
if defined?(Rails)
|
|
39
|
+
if Rails.respond_to?(:application)
|
|
40
|
+
Rails.application.class.parent.name
|
|
41
|
+
else
|
|
42
|
+
raise "'Rails' is defined, but it doesn't respond to #application, so could not derive the app_id; you must explicitly set it"
|
|
43
|
+
end
|
|
44
|
+
else
|
|
45
|
+
raise "Could not derive the app_id; you must explicitly set it"
|
|
46
|
+
end
|
|
47
|
+
else
|
|
48
|
+
@app_id
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
35
52
|
def payload_logging
|
|
36
53
|
@payload_logging || :info
|
|
37
54
|
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require "securerandom"
|
|
2
|
+
module Pwwka
|
|
3
|
+
# Encaspulates the options we pass to `topic_exchange.publish` as well
|
|
4
|
+
# as the various defaults and auto-generated values.
|
|
5
|
+
class PublishOptions
|
|
6
|
+
def initialize(routing_key: ,
|
|
7
|
+
message_id: :auto_generate,
|
|
8
|
+
type: ,
|
|
9
|
+
headers:,
|
|
10
|
+
expiration: nil)
|
|
11
|
+
@options_hash = {
|
|
12
|
+
routing_key: routing_key,
|
|
13
|
+
message_id: message_id.to_s == "auto_generate" ? SecureRandom.uuid : message_id,
|
|
14
|
+
content_type: "application/json; version=1",
|
|
15
|
+
persistent: true,
|
|
16
|
+
app_id: Pwwka.configuration.app_id
|
|
17
|
+
}
|
|
18
|
+
@options_hash[:type] = type unless type.nil?
|
|
19
|
+
@options_hash[:headers] = headers unless headers.nil?
|
|
20
|
+
@options_hash[:expiration] = expiration unless expiration.nil?
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def message_id
|
|
24
|
+
@options_hash[:message_id]
|
|
25
|
+
end
|
|
26
|
+
def to_h
|
|
27
|
+
@options_hash.merge(timestamp: Time.now.to_i)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -8,9 +8,21 @@ module Pwwka
|
|
|
8
8
|
extend Resque::Plugins::ExponentialBackoff rescue nil # Optional
|
|
9
9
|
@backoff_strategy = Pwwka.configuration.send_message_resque_backoff_strategy
|
|
10
10
|
|
|
11
|
-
def self.perform(payload, routing_key)
|
|
11
|
+
def self.perform(payload, routing_key, options = {})
|
|
12
|
+
|
|
13
|
+
type = options["type"]
|
|
14
|
+
message_id = options["message_id"] || "auto_generate"
|
|
15
|
+
headers = options["headers"]
|
|
16
|
+
|
|
12
17
|
info("Sending message async #{routing_key}, #{payload}")
|
|
13
|
-
|
|
18
|
+
message_id = message_id.to_sym if message_id == "auto_generate"
|
|
19
|
+
Pwwka::Transmitter.send_message!(
|
|
20
|
+
payload,
|
|
21
|
+
routing_key,
|
|
22
|
+
type: type,
|
|
23
|
+
message_id: message_id,
|
|
24
|
+
headers: headers,
|
|
25
|
+
on_error: :raise)
|
|
14
26
|
end
|
|
15
27
|
end
|
|
16
28
|
end
|
data/lib/pwwka/transmitter.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require_relative "publish_options"
|
|
2
|
+
|
|
1
3
|
begin # optional dependency
|
|
2
4
|
require 'resque'
|
|
3
5
|
require 'resque-retry'
|
|
@@ -34,6 +36,9 @@ module Pwwka
|
|
|
34
36
|
# routing_key:: String routing key for the message
|
|
35
37
|
# delayed:: Boolean send this message later
|
|
36
38
|
# delay_by:: Integer milliseconds to delay the message
|
|
39
|
+
# type:: A string describing the type. This + your configured app_id should be unique to your entire ecosystem.
|
|
40
|
+
# message_id:: If specified (which generally you should not do), sets the id of the message. If omitted, a GUID is used.
|
|
41
|
+
# headers:: A hash of arbitrary headers to include in the AMQP attributes
|
|
37
42
|
# on_error:: What is the behavior of
|
|
38
43
|
# - :ignore (aka as send_message_safely)
|
|
39
44
|
# - :raise
|
|
@@ -42,12 +47,17 @@ module Pwwka
|
|
|
42
47
|
# Returns true
|
|
43
48
|
#
|
|
44
49
|
# Raises any exception generated by the innerworkings of this library.
|
|
45
|
-
def self.send_message!(payload, routing_key,
|
|
46
|
-
|
|
50
|
+
def self.send_message!(payload, routing_key,
|
|
51
|
+
on_error: :raise,
|
|
52
|
+
delayed: false,
|
|
53
|
+
delay_by: nil,
|
|
54
|
+
type: nil,
|
|
55
|
+
message_id: :auto_generate,
|
|
56
|
+
headers: nil)
|
|
47
57
|
if delayed
|
|
48
|
-
new.send_delayed_message!(*[payload, routing_key, delay_by].compact)
|
|
58
|
+
new.send_delayed_message!(*[payload, routing_key, delay_by].compact, type: type, headers: headers, message_id: message_id)
|
|
49
59
|
else
|
|
50
|
-
new.send_message!(payload, routing_key)
|
|
60
|
+
new.send_message!(payload, routing_key, type: type, headers: headers, message_id: message_id)
|
|
51
61
|
end
|
|
52
62
|
logf "AFTER Transmitting Message on %{routing_key} -> %{payload}",routing_key: routing_key, payload: payload
|
|
53
63
|
true
|
|
@@ -74,9 +84,18 @@ module Pwwka
|
|
|
74
84
|
|
|
75
85
|
# Use Resque to enqueue the message.
|
|
76
86
|
# - :delay_by_ms:: Integer milliseconds to delay the message. Default is 0.
|
|
77
|
-
def self.send_message_async(payload, routing_key,
|
|
87
|
+
def self.send_message_async(payload, routing_key,
|
|
88
|
+
delay_by_ms: 0,
|
|
89
|
+
type: nil,
|
|
90
|
+
message_id: :auto_generate,
|
|
91
|
+
headers: nil)
|
|
78
92
|
job = Pwwka.configuration.async_job_klass
|
|
79
|
-
|
|
93
|
+
# Be perhaps too carefully making sure we queue jobs in the legacy way
|
|
94
|
+
if type == nil && message_id == :auto_generate && headers == nil
|
|
95
|
+
Resque.enqueue_in(delay_by_ms/1000, job, payload, routing_key)
|
|
96
|
+
else
|
|
97
|
+
Resque.enqueue_in(delay_by_ms/1000, job, payload, routing_key, type: type, message_id: message_id, headers: headers)
|
|
98
|
+
end
|
|
80
99
|
end
|
|
81
100
|
|
|
82
101
|
# Send a less important message that doesn't have to go through. This eats
|
|
@@ -89,35 +108,41 @@ module Pwwka
|
|
|
89
108
|
#
|
|
90
109
|
# Returns true if the message was sent, false otherwise
|
|
91
110
|
# @deprecated This is ignoring a message. ::send_message supports this explicitly.
|
|
92
|
-
def self.send_message_safely(payload, routing_key, delayed: false, delay_by: nil)
|
|
111
|
+
def self.send_message_safely(payload, routing_key, delayed: false, delay_by: nil, message_id: message_id)
|
|
93
112
|
send_message!(payload, routing_key, delayed: delayed, delay_by: delay_by, on_error: :ignore)
|
|
94
113
|
end
|
|
95
114
|
|
|
96
|
-
def send_message!(payload, routing_key)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
115
|
+
def send_message!(payload, routing_key, type: nil, headers: nil, message_id: :auto_generate)
|
|
116
|
+
publish_options = Pwwka::PublishOptions.new(
|
|
117
|
+
routing_key: routing_key,
|
|
118
|
+
message_id: message_id,
|
|
119
|
+
type: type,
|
|
120
|
+
headers: headers
|
|
121
|
+
)
|
|
122
|
+
logf "START Transmitting Message on id[%{id}] %{routing_key} -> %{payload}", id: publish_options.message_id, routing_key: routing_key, payload: payload
|
|
123
|
+
channel_connector.topic_exchange.publish(payload.to_json, publish_options.to_h)
|
|
102
124
|
channel_connector.connection_close
|
|
103
125
|
# if it gets this far it has succeeded
|
|
104
|
-
logf "END Transmitting Message on %{routing_key} -> %{payload}", routing_key: routing_key, payload: payload
|
|
126
|
+
logf "END Transmitting Message on id[%{id}] %{routing_key} -> %{payload}", id: publish_options.message_id, routing_key: routing_key, payload: payload
|
|
105
127
|
true
|
|
106
128
|
end
|
|
107
129
|
|
|
108
130
|
|
|
109
|
-
def send_delayed_message!(payload, routing_key, delay_by = DEFAULT_DELAY_BY_MS)
|
|
131
|
+
def send_delayed_message!(payload, routing_key, delay_by = DEFAULT_DELAY_BY_MS, type: nil, headers: nil, message_id: :auto_generate)
|
|
110
132
|
channel_connector.raise_if_delayed_not_allowed
|
|
111
|
-
|
|
133
|
+
publish_options = Pwwka::PublishOptions.new(
|
|
134
|
+
routing_key: routing_key,
|
|
135
|
+
message_id: message_id,
|
|
136
|
+
type: type,
|
|
137
|
+
headers: headers,
|
|
138
|
+
expiration: delay_by
|
|
139
|
+
)
|
|
140
|
+
logf "START Transmitting Delayed Message on id[%{id}] %{routing_key} -> %{payload}", id: publish_options.message_id, routing_key: routing_key, payload: payload
|
|
112
141
|
channel_connector.create_delayed_queue
|
|
113
|
-
channel_connector.delayed_exchange.publish(
|
|
114
|
-
payload.to_json,
|
|
115
|
-
routing_key: routing_key,
|
|
116
|
-
expiration: delay_by,
|
|
117
|
-
persistent: true)
|
|
142
|
+
channel_connector.delayed_exchange.publish(payload.to_json,publish_options.to_h)
|
|
118
143
|
channel_connector.connection_close
|
|
119
144
|
# if it gets this far it has succeeded
|
|
120
|
-
logf "END Transmitting Delayed Message on %{routing_key} -> %{payload}", routing_key: routing_key, payload: payload
|
|
145
|
+
logf "END Transmitting Delayed Message on id[%{id}] %{routing_key} -> %{payload}", id: publish_options.message_id, routing_key: routing_key, payload: payload
|
|
121
146
|
true
|
|
122
147
|
end
|
|
123
148
|
|
data/lib/pwwka/version.rb
CHANGED
data/pwwka.gemspec
CHANGED
|
@@ -31,79 +31,177 @@ describe "sending and receiving messages", :integration do
|
|
|
31
31
|
@testing_setup.kill_threads_and_clear_queues
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
34
|
+
context "routing" do
|
|
35
|
+
it "can send a message that gets routed to all receivers" do
|
|
36
|
+
Pwwka::Transmitter.send_message!({ sample: "payload", has: { deeply: true, nested: 4 }},
|
|
37
|
+
"pwwka.testing.foo")
|
|
38
|
+
allow_receivers_to_process_queues
|
|
39
|
+
|
|
40
|
+
expect(AllReceiver.messages_received.size).to eq(1)
|
|
41
|
+
expect(FooReceiver.messages_received.size).to eq(1)
|
|
42
|
+
expect(OtherFooReceiver.messages_received.size).to eq(1)
|
|
43
|
+
@testing_setup.queues.each do |queue|
|
|
44
|
+
expect(queue.message_count).to eq(0)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
it "can send a message that is only delivered to some handlers based on routing key" do
|
|
48
|
+
Pwwka::Transmitter.send_message!({ sample: "payload", has: { deeply: true, nested: 4 }},
|
|
49
|
+
"pwwka.testing.bar")
|
|
50
|
+
allow_receivers_to_process_queues
|
|
51
|
+
|
|
52
|
+
expect(AllReceiver.messages_received.size).to eq(1)
|
|
53
|
+
expect(FooReceiver.messages_received.size).to eq(0)
|
|
54
|
+
expect(OtherFooReceiver.messages_received.size).to eq(0)
|
|
55
|
+
@testing_setup.queues.each do |queue|
|
|
56
|
+
expect(queue.message_count).to eq(0)
|
|
57
|
+
end
|
|
44
58
|
end
|
|
45
59
|
end
|
|
46
60
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
allow_receivers_to_process_queues(1_000)
|
|
61
|
+
context "metadata" do
|
|
62
|
+
it "can access standard metadata" do
|
|
63
|
+
Pwwka::Transmitter.send_message!({ sample: "payload", has: { deeply: true, nested: 4 }},
|
|
64
|
+
"pwwka.testing.foo")
|
|
65
|
+
allow_receivers_to_process_queues
|
|
53
66
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
67
|
+
expect(AllReceiver.metadata[0].message_id).not_to be_nil
|
|
68
|
+
expect(AllReceiver.metadata[0].timestamp).to be_within(2.minutes).of(Time.now)
|
|
69
|
+
expect(AllReceiver.metadata[0].content_type).to eq("application/json; version=1")
|
|
70
|
+
expect(AllReceiver.metadata[0].app_id).to eq("MyAwesomeApp")
|
|
71
|
+
end
|
|
57
72
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
73
|
+
it "can access standard metadata on delayed jobs" do
|
|
74
|
+
Pwwka::Transmitter.send_message!({ sample: "payload", has: { deeply: true, nested: 4 }},
|
|
75
|
+
"pwwka.testing.foo",
|
|
76
|
+
delayed: true,
|
|
77
|
+
delay_by: 100)
|
|
78
|
+
allow_receivers_to_process_queues(200)
|
|
79
|
+
|
|
80
|
+
expect(AllReceiver.metadata[0].message_id).not_to be_nil
|
|
81
|
+
expect(AllReceiver.metadata[0].timestamp).to be_within(2.minutes).of(Time.now)
|
|
82
|
+
expect(AllReceiver.metadata[0].content_type).to eq("application/json; version=1")
|
|
83
|
+
expect(AllReceiver.metadata[0].app_id).to eq("MyAwesomeApp")
|
|
84
|
+
end
|
|
63
85
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
86
|
+
it "can access explicitly-provided metadata" do
|
|
87
|
+
Pwwka::Transmitter.send_message!({ sample: "payload", has: { deeply: true, nested: 4 }},
|
|
88
|
+
"pwwka.testing.foo",
|
|
89
|
+
type: "Customer",
|
|
90
|
+
headers: {
|
|
91
|
+
foo: "bar",
|
|
92
|
+
blah: 42,
|
|
93
|
+
})
|
|
94
|
+
allow_receivers_to_process_queues
|
|
95
|
+
|
|
96
|
+
expect(AllReceiver.metadata[0].message_id).not_to be_nil
|
|
97
|
+
expect(AllReceiver.metadata[0].timestamp).to be_within(2.minutes).of(Time.now)
|
|
98
|
+
expect(AllReceiver.metadata[0].content_type).to eq("application/json; version=1")
|
|
99
|
+
expect(AllReceiver.metadata[0].app_id).to eq("MyAwesomeApp")
|
|
100
|
+
expect(AllReceiver.metadata[0].type).to eq("Customer")
|
|
101
|
+
expect(AllReceiver.metadata[0].headers["foo"]).to eq("bar")
|
|
102
|
+
expect(AllReceiver.metadata[0].headers["blah"]).to eq(42)
|
|
103
|
+
end
|
|
68
104
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
105
|
+
it "can access explicitly-provided metadata on delayed jobs" do
|
|
106
|
+
Pwwka::Transmitter.send_message!({ sample: "payload", has: { deeply: true, nested: 4 }},
|
|
107
|
+
"pwwka.testing.foo",
|
|
108
|
+
type: "Customer",
|
|
109
|
+
headers: {
|
|
110
|
+
foo: "bar",
|
|
111
|
+
blah: 42,
|
|
112
|
+
},
|
|
113
|
+
delayed: true,
|
|
114
|
+
delay_by: 100)
|
|
115
|
+
allow_receivers_to_process_queues(200)
|
|
116
|
+
|
|
117
|
+
expect(AllReceiver.metadata[0].message_id).not_to be_nil
|
|
118
|
+
expect(AllReceiver.metadata[0].timestamp).to be_within(2.minutes).of(Time.now)
|
|
119
|
+
expect(AllReceiver.metadata[0].content_type).to eq("application/json; version=1")
|
|
120
|
+
expect(AllReceiver.metadata[0].type).to eq("Customer")
|
|
121
|
+
expect(AllReceiver.metadata[0].app_id).to eq("MyAwesomeApp")
|
|
122
|
+
expect(AllReceiver.metadata[0].headers["foo"]).to eq("bar")
|
|
123
|
+
expect(AllReceiver.metadata[0].headers["blah"]).to eq(42)
|
|
74
124
|
end
|
|
75
125
|
end
|
|
76
126
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
127
|
+
context "sending messages from a background job" do
|
|
128
|
+
it "can queue a job to send a message from a Resque job" do
|
|
129
|
+
Pwwka::Transmitter.send_message_async({ sample: "payload", has: { deeply: true, nested: 4 }},
|
|
130
|
+
"pwwka.testing.bar")
|
|
80
131
|
|
|
81
|
-
|
|
132
|
+
allow_receivers_to_process_queues # not expecting anything to be processed
|
|
82
133
|
|
|
83
|
-
|
|
134
|
+
expect(AllReceiver.messages_received.size).to eq(0)
|
|
84
135
|
|
|
85
|
-
|
|
136
|
+
process_resque_job(Pwwka::SendMessageAsyncJob,:delayed)
|
|
86
137
|
|
|
87
|
-
|
|
138
|
+
allow_receivers_to_process_queues
|
|
88
139
|
|
|
89
|
-
|
|
90
|
-
|
|
140
|
+
expect(AllReceiver.messages_received.size).to eq(1)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it "can queue a job with optional arguments to send a message from a Resque job" do
|
|
144
|
+
Pwwka::Transmitter.send_message_async(
|
|
145
|
+
{ sample: "payload", has: { deeply: true, nested: 4 }},
|
|
146
|
+
"pwwka.testing.bar",
|
|
147
|
+
message_id: "setting this is a bad idea, but you can do it",
|
|
148
|
+
headers: {
|
|
149
|
+
"FOO" => "bar"
|
|
150
|
+
},
|
|
151
|
+
type: "Customer"
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
allow_receivers_to_process_queues # not expecting anything to be processed
|
|
155
|
+
|
|
156
|
+
expect(AllReceiver.messages_received.size).to eq(0)
|
|
157
|
+
|
|
158
|
+
process_resque_job(Pwwka::SendMessageAsyncJob,:delayed)
|
|
159
|
+
|
|
160
|
+
allow_receivers_to_process_queues
|
|
91
161
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
162
|
+
expect(AllReceiver.messages_received.size).to eq(1)
|
|
163
|
+
expect(AllReceiver.metadata[0].message_id).to eq("setting this is a bad idea, but you can do it")
|
|
164
|
+
expect(AllReceiver.metadata[0].timestamp).to be_within(2.minutes).of(Time.now)
|
|
165
|
+
expect(AllReceiver.metadata[0].content_type).to eq("application/json; version=1")
|
|
166
|
+
expect(AllReceiver.metadata[0].type).to eq("Customer")
|
|
167
|
+
expect(AllReceiver.metadata[0].app_id).to eq("MyAwesomeApp")
|
|
168
|
+
expect(AllReceiver.metadata[0].headers["FOO"]).to eq("bar")
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
it "can queue a job to send a message to a specified Resque job queue" do
|
|
172
|
+
async_job_klass = double(:async_job_klass)
|
|
173
|
+
configuration = Pwwka::Configuration.new
|
|
174
|
+
configuration.async_job_klass = async_job_klass
|
|
175
|
+
|
|
176
|
+
allow(Pwwka).to receive(:configuration).and_return(configuration)
|
|
177
|
+
|
|
178
|
+
allow(Resque).to receive(:enqueue_in)
|
|
96
179
|
|
|
97
|
-
|
|
180
|
+
Pwwka::Transmitter.send_message_async({ sample: "payload", has: { deeply: true, nested: 4 }},
|
|
181
|
+
"pwwka.testing.bar")
|
|
182
|
+
|
|
183
|
+
expect(Resque).to have_received(:enqueue_in).with(anything, async_job_klass, anything, anything)
|
|
184
|
+
end
|
|
185
|
+
end
|
|
98
186
|
|
|
99
|
-
|
|
187
|
+
it "can send a message delayed" do
|
|
188
|
+
Pwwka::Transmitter.send_message!({ sample: "payload", has: { deeply: true, nested: 4 }},
|
|
189
|
+
"pwwka.testing.foo",
|
|
190
|
+
delayed: true,
|
|
191
|
+
delay_by: 5_000)
|
|
192
|
+
allow_receivers_to_process_queues(1_000)
|
|
100
193
|
|
|
101
|
-
|
|
102
|
-
|
|
194
|
+
expect(AllReceiver.messages_received.size).to eq(0)
|
|
195
|
+
expect(FooReceiver.messages_received.size).to eq(0)
|
|
196
|
+
expect(OtherFooReceiver.messages_received.size).to eq(0)
|
|
103
197
|
|
|
104
|
-
|
|
198
|
+
allow_receivers_to_process_queues(5_000)
|
|
199
|
+
expect(AllReceiver.messages_received.size).to eq(1)
|
|
200
|
+
expect(FooReceiver.messages_received.size).to eq(1)
|
|
201
|
+
expect(OtherFooReceiver.messages_received.size).to eq(1)
|
|
105
202
|
end
|
|
106
203
|
|
|
204
|
+
|
|
107
205
|
class AllReceiver < LoggingReceiver
|
|
108
206
|
end
|
|
109
207
|
class FooReceiver < AllReceiver
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
class LoggingReceiver
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
def self.reset!
|
|
4
|
+
@messages_received = []
|
|
5
|
+
@metadata = []
|
|
6
|
+
end
|
|
7
|
+
|
|
3
8
|
def self.messages_received; @messages_received ||= []; end
|
|
9
|
+
def self.metadata; @metadata ||= []; end
|
|
4
10
|
|
|
5
11
|
reset!
|
|
6
12
|
|
|
7
13
|
def self.handle!(delivery_info,properties,payload)
|
|
8
14
|
messages_received << [ delivery_info,properties,payload ]
|
|
15
|
+
metadata << properties
|
|
9
16
|
end
|
|
10
17
|
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -34,6 +34,7 @@ RSpec.configure do |config|
|
|
|
34
34
|
c.options[:allow_delayed] = true
|
|
35
35
|
c.requeue_on_error = false
|
|
36
36
|
c.rabbit_mq_host = "amqp://guest:guest@localhost:#{test_configuration.rabbit_port}"
|
|
37
|
+
c.app_id = "MyAwesomeApp"
|
|
37
38
|
|
|
38
39
|
unless ENV["SHOW_PWWKA_LOG"] == "true"
|
|
39
40
|
c.logger = MonoLogger.new("/dev/null")
|
|
@@ -41,7 +42,16 @@ RSpec.configure do |config|
|
|
|
41
42
|
end
|
|
42
43
|
Resque.redis = Redis.new(port: test_configuration.resque_redis_port)
|
|
43
44
|
end
|
|
44
|
-
|
|
45
|
+
config.around(:each) do |example|
|
|
46
|
+
if example.metadata[:integration]
|
|
47
|
+
result = test_configuration.check_services
|
|
48
|
+
unless result.up?
|
|
49
|
+
puts "\n\n" + Rainbow(result.error).yellow.bright + "\n\n"
|
|
50
|
+
exit 1
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
example.run
|
|
54
|
+
end
|
|
45
55
|
config.order = :random
|
|
46
56
|
config.filter_run_excluding :legacy
|
|
47
57
|
end
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "yaml"
|
|
2
|
+
require "socket"
|
|
3
|
+
require "timeout"
|
|
4
|
+
require "rainbow"
|
|
5
|
+
|
|
2
6
|
class TestConfiguration
|
|
3
7
|
attr_reader :resque_redis_port, :rabbit_port
|
|
4
8
|
def initialize(docker_compose_file)
|
|
@@ -7,4 +11,34 @@ class TestConfiguration
|
|
|
7
11
|
@resque_redis_port = (ENV["PWWKA_RESQUE_REDIS_PORT"] || yaml["services"]["resque"]["ports"].first.split(/:/)[0]).to_i
|
|
8
12
|
@rabbit_port = (ENV["PWWKA_RABBIT_PORT"] || yaml["services"]["rabbit"]["ports"].first.split(/:/)[0]).to_i
|
|
9
13
|
end
|
|
14
|
+
|
|
15
|
+
def check_services
|
|
16
|
+
redis_running = is_port_open?("localhost",@resque_redis_port)
|
|
17
|
+
rabbit_running = is_port_open?("localhost",@rabbit_port)
|
|
18
|
+
if !(redis_running && rabbit_port)
|
|
19
|
+
OpenStruct.new(error: "Rabbit and/or Redis is not running - you need to run `docker-compose up` in the root dir",
|
|
20
|
+
up?: false)
|
|
21
|
+
else
|
|
22
|
+
OpenStruct.new(up?: true)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def is_port_open?(ip, port)
|
|
29
|
+
begin
|
|
30
|
+
Timeout::timeout(1) do
|
|
31
|
+
begin
|
|
32
|
+
s = TCPSocket.new(ip, port)
|
|
33
|
+
s.close
|
|
34
|
+
return true
|
|
35
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
|
|
36
|
+
return false
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
rescue Timeout::Error
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
return false
|
|
43
|
+
end
|
|
10
44
|
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
require 'spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
module MyAmazingApp
|
|
4
|
+
class Application
|
|
5
|
+
end
|
|
6
|
+
end
|
|
7
|
+
describe Pwwka::Configuration do
|
|
8
|
+
|
|
9
|
+
subject(:configuration) { described_class.new }
|
|
10
|
+
before do
|
|
11
|
+
@env = ENV["RAILS_ENV"]
|
|
12
|
+
ENV["RAILS_ENV"] = "production"
|
|
13
|
+
end
|
|
14
|
+
after do
|
|
15
|
+
ENV["RAILS_ENV"] = @env
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
describe "#topic_exchange_name" do
|
|
19
|
+
it "is based on the Pwwka.environment" do
|
|
20
|
+
expect(configuration.topic_exchange_name).to eq("pwwka.topics.production")
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
describe "#delayed_exchange_name" do
|
|
24
|
+
it "is based on the Pwwka.environment" do
|
|
25
|
+
expect(configuration.delayed_exchange_name).to eq("pwwka.delayed.production")
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe "#payload_logging" do
|
|
30
|
+
it "is info by default" do
|
|
31
|
+
expect(configuration.payload_logging).to eq(:info)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "can be overridden" do
|
|
35
|
+
configuration.payload_logging = :debug
|
|
36
|
+
expect(configuration.payload_logging).to eq(:debug)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
describe "#app_id" do
|
|
41
|
+
it "returns the value set explicitly" do
|
|
42
|
+
configuration.app_id = "MyApp"
|
|
43
|
+
expect(configuration.app_id).to eq("MyApp")
|
|
44
|
+
end
|
|
45
|
+
it "blows up when not set" do
|
|
46
|
+
expect {
|
|
47
|
+
configuration.app_id
|
|
48
|
+
}.to raise_error(/Could not derive the app_id; you must explicitly set it/)
|
|
49
|
+
end
|
|
50
|
+
context "when inside a Rails app" do
|
|
51
|
+
before do
|
|
52
|
+
rails = Class.new do
|
|
53
|
+
def self.application
|
|
54
|
+
MyAmazingApp::Application.new
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
Object.const_set("Rails",rails)
|
|
58
|
+
end
|
|
59
|
+
after do
|
|
60
|
+
Object.send(:remove_const,"Rails")
|
|
61
|
+
end
|
|
62
|
+
it "uses the Rails app name" do
|
|
63
|
+
expect(configuration.app_id).to eq("MyAmazingApp")
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
context "when Rails is defined, but not how we expect" do
|
|
68
|
+
before do
|
|
69
|
+
rails = Class.new
|
|
70
|
+
Object.const_set("Rails",rails)
|
|
71
|
+
end
|
|
72
|
+
after do
|
|
73
|
+
Object.send(:remove_const,"Rails")
|
|
74
|
+
end
|
|
75
|
+
it "blows up when not set" do
|
|
76
|
+
expect {
|
|
77
|
+
configuration.app_id
|
|
78
|
+
}.to raise_error(/'Rails' is defined, but it doesn't respond to #application, so could not derive the app_id; you must explicitly set it/)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require 'spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
describe Pwwka::SendMessageAsyncJob do
|
|
4
|
+
describe "::perform" do
|
|
5
|
+
before do
|
|
6
|
+
allow(Pwwka::Transmitter).to receive(:send_message!)
|
|
7
|
+
end
|
|
8
|
+
context "with just two arguments" do
|
|
9
|
+
it "calls through to Pwwka::Transmitter, setting error handling to 'raise'" do
|
|
10
|
+
described_class.perform({ "foo" => "bar"} , "some.routing.key")
|
|
11
|
+
expect(Pwwka::Transmitter).to have_received(:send_message!).with(
|
|
12
|
+
{ "foo" => "bar" },
|
|
13
|
+
"some.routing.key",
|
|
14
|
+
type: nil,
|
|
15
|
+
message_id: :auto_generate,
|
|
16
|
+
headers: nil,
|
|
17
|
+
on_error: :raise
|
|
18
|
+
)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
context "with optional values" do
|
|
22
|
+
it "passes them through to Pwwka::Transmitter" do
|
|
23
|
+
described_class.perform(
|
|
24
|
+
{ "foo" => "bar"},
|
|
25
|
+
"some.routing.key",
|
|
26
|
+
"type" => "Customer",
|
|
27
|
+
"message_id" => "foobar",
|
|
28
|
+
"headers" => { "x" => "y" }
|
|
29
|
+
)
|
|
30
|
+
expect(Pwwka::Transmitter).to have_received(:send_message!).with(
|
|
31
|
+
{ "foo" => "bar" },
|
|
32
|
+
"some.routing.key",
|
|
33
|
+
type: "Customer",
|
|
34
|
+
message_id: "foobar",
|
|
35
|
+
headers: { "x" => "y" },
|
|
36
|
+
on_error: :raise
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -36,18 +36,46 @@ describe Pwwka::Transmitter do
|
|
|
36
36
|
before do
|
|
37
37
|
allow(Resque).to receive(:enqueue_in)
|
|
38
38
|
end
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
context "with only basic required arguments" do
|
|
40
|
+
it "queues a Resque job with no extra args" do
|
|
41
|
+
delay_by_ms = 3_000
|
|
42
|
+
described_class.send_message_async(payload,routing_key,delay_by_ms: delay_by_ms)
|
|
43
|
+
expect(Resque).to have_received(:enqueue_in).with(delay_by_ms/1_000,Pwwka::SendMessageAsyncJob,payload,routing_key)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
context "with everything overridden" do
|
|
47
|
+
it "queues a Resque job with the various arguments" do
|
|
48
|
+
delay_by_ms = 3_000
|
|
49
|
+
described_class.send_message_async(
|
|
50
|
+
payload,routing_key,
|
|
51
|
+
delay_by_ms: delay_by_ms,
|
|
52
|
+
message_id: "snowflake id that is likely a bad idea, but if you must",
|
|
53
|
+
type: "Customer",
|
|
54
|
+
headers: {
|
|
55
|
+
"custom" => "value",
|
|
56
|
+
"other_custom" => "other_value",
|
|
57
|
+
}
|
|
58
|
+
)
|
|
59
|
+
expect(Resque).to have_received(:enqueue_in).with(
|
|
60
|
+
delay_by_ms/1_000,
|
|
61
|
+
Pwwka::SendMessageAsyncJob,
|
|
62
|
+
payload,
|
|
63
|
+
routing_key,
|
|
64
|
+
message_id: "snowflake id that is likely a bad idea, but if you must",
|
|
65
|
+
type: "Customer",
|
|
66
|
+
headers: {
|
|
67
|
+
"custom" => "value",
|
|
68
|
+
"other_custom" => "other_value",
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
end
|
|
44
72
|
end
|
|
45
73
|
end
|
|
46
74
|
|
|
47
75
|
shared_examples "it passes through to an instance" do
|
|
48
76
|
context "not using delayed flag" do
|
|
49
77
|
it "calls through to send_message!" do
|
|
50
|
-
expect_any_instance_of(described_class).to receive(:send_message!).with(payload,routing_key)
|
|
78
|
+
expect_any_instance_of(described_class).to receive(:send_message!).with(payload,routing_key, type: nil, headers: nil, message_id: :auto_generate)
|
|
51
79
|
described_class.send(method,payload,routing_key)
|
|
52
80
|
end
|
|
53
81
|
it "logs after sending" do
|
|
@@ -66,19 +94,140 @@ describe Pwwka::Transmitter do
|
|
|
66
94
|
context "explicitly setting delay time" do
|
|
67
95
|
it "calls through to send_delayed_message! using the given delay time" do
|
|
68
96
|
delay_by = 1_000
|
|
69
|
-
expect_any_instance_of(described_class).to receive(:send_delayed_message!).with(payload,routing_key,delay_by)
|
|
97
|
+
expect_any_instance_of(described_class).to receive(:send_delayed_message!).with(payload,routing_key,delay_by, type: nil, headers: nil, message_id: :auto_generate)
|
|
70
98
|
described_class.send(method,payload,routing_key,delayed: true, delay_by: delay_by)
|
|
71
99
|
end
|
|
72
100
|
end
|
|
73
101
|
context "using the default delay time" do
|
|
74
102
|
it "calls through to send_delayed_message! using its default delay time" do
|
|
75
|
-
expect_any_instance_of(described_class).to receive(:send_delayed_message!).with(payload,routing_key)
|
|
103
|
+
expect_any_instance_of(described_class).to receive(:send_delayed_message!).with(payload,routing_key, type: nil, headers: nil, message_id: :auto_generate)
|
|
76
104
|
described_class.send(method,payload,routing_key,delayed: true)
|
|
77
105
|
end
|
|
78
106
|
end
|
|
79
107
|
end
|
|
80
108
|
end
|
|
81
109
|
|
|
110
|
+
shared_examples "it sends standard and overridden data to the exchange" do
|
|
111
|
+
it "publishes to the topic exchange" do
|
|
112
|
+
expect(exchange).to have_received(:publish).with(payload.to_json, kind_of(Hash))
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it "passes the routing key" do
|
|
116
|
+
expect(exchange).to have_received(:publish).with(
|
|
117
|
+
payload.to_json,
|
|
118
|
+
hash_including(routing_key: routing_key))
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it "sets the type" do
|
|
122
|
+
expect(exchange).to have_received(:publish).with(
|
|
123
|
+
payload.to_json,
|
|
124
|
+
hash_including(type: "Customer"))
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it "sets the headers" do
|
|
128
|
+
expect(exchange).to have_received(:publish).with(
|
|
129
|
+
payload.to_json,
|
|
130
|
+
hash_including(headers: { "custom" => "value", "other_custom" => "other_value" }))
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it "uses the overridden message id" do
|
|
134
|
+
expect(exchange).to have_received(:publish).with(
|
|
135
|
+
payload.to_json,
|
|
136
|
+
hash_including(message_id: "snowflake id that is likely a bad idea, but if you must"))
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it "sets the timestamp to now" do
|
|
140
|
+
expect(exchange).to have_received(:publish).with(
|
|
141
|
+
payload.to_json,
|
|
142
|
+
hash_including(timestamp: a_timestamp_about_now))
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it "sets the app id to what's configured" do
|
|
146
|
+
expect(exchange).to have_received(:publish).with(
|
|
147
|
+
payload.to_json,
|
|
148
|
+
hash_including(app_id: "MyAwesomeApp"))
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
it "sets the content type to JSON with a version" do
|
|
152
|
+
expect(exchange).to have_received(:publish).with(
|
|
153
|
+
payload.to_json,
|
|
154
|
+
hash_including(content_type: "application/json; version=1"))
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
it "sets persistent true" do
|
|
158
|
+
expect(exchange).to have_received(:publish).with(
|
|
159
|
+
payload.to_json,
|
|
160
|
+
hash_including(persistent: true))
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
shared_examples "it sends standard attributes and the payload to the exchange" do
|
|
165
|
+
it "publishes to the topic exchange" do
|
|
166
|
+
expect(exchange).to have_received(:publish).with(payload.to_json, kind_of(Hash))
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
it "passes the routing key" do
|
|
170
|
+
expect(exchange).to have_received(:publish).with(
|
|
171
|
+
payload.to_json,
|
|
172
|
+
hash_including(routing_key: routing_key))
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
it "sets a default message id" do
|
|
176
|
+
expect(exchange).to have_received(:publish).with(
|
|
177
|
+
payload.to_json,
|
|
178
|
+
hash_including(message_id: a_uuid))
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
it "sets the timestamp to now" do
|
|
182
|
+
expect(exchange).to have_received(:publish).with(
|
|
183
|
+
payload.to_json,
|
|
184
|
+
hash_including(timestamp: a_timestamp_about_now))
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
it "sets the app id to what's configured" do
|
|
188
|
+
expect(exchange).to have_received(:publish).with(
|
|
189
|
+
payload.to_json,
|
|
190
|
+
hash_including(app_id: "MyAwesomeApp"))
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
it "sets the content type to JSON with a version" do
|
|
194
|
+
expect(exchange).to have_received(:publish).with(
|
|
195
|
+
payload.to_json,
|
|
196
|
+
hash_including(content_type: "application/json; version=1"))
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
it "sets persistent true" do
|
|
200
|
+
expect(exchange).to have_received(:publish).with(
|
|
201
|
+
payload.to_json,
|
|
202
|
+
hash_including(persistent: true))
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
it "does not set the type" do
|
|
206
|
+
expect(exchange).to have_received(:publish).with(
|
|
207
|
+
payload.to_json,
|
|
208
|
+
hash_excluding(:type))
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
it "does not set headers" do
|
|
212
|
+
expect(exchange).to have_received(:publish).with(
|
|
213
|
+
payload.to_json,
|
|
214
|
+
hash_excluding(:headers))
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
RSpec::Matchers.define :a_uuid do |x|
|
|
219
|
+
match { |actual|
|
|
220
|
+
actual =~ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
|
|
221
|
+
}
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
RSpec::Matchers.define :a_timestamp_about_now do |x|
|
|
225
|
+
match { |actual|
|
|
226
|
+
(actual - Time.now.to_i).abs < 1000
|
|
227
|
+
}
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
|
|
82
231
|
describe ".send_message!" do
|
|
83
232
|
context "no errors" do
|
|
84
233
|
it_behaves_like "it passes through to an instance" do
|
|
@@ -165,21 +314,45 @@ describe Pwwka::Transmitter do
|
|
|
165
314
|
expect(transmitter.send_message!(payload,routing_key)).to eq(true)
|
|
166
315
|
end
|
|
167
316
|
|
|
168
|
-
it "publishes the message" do
|
|
169
|
-
transmitter.send_message!(payload,routing_key)
|
|
170
|
-
expect(topic_exchange).to have_received(:publish).with(payload.to_json,routing_key: routing_key, persistent: true)
|
|
171
|
-
end
|
|
172
|
-
|
|
173
317
|
it "logs the start and end of the transmission" do
|
|
174
318
|
transmitter.send_message!(payload,routing_key)
|
|
175
|
-
expect(logger).to have_received(:info).with(/START Transmitting Message on #{routing_key} ->/)
|
|
176
|
-
expect(logger).to have_received(:info).with(/END Transmitting Message on #{routing_key} ->/)
|
|
319
|
+
expect(logger).to have_received(:info).with(/START Transmitting Message on id\[[\w\-\d]+\] #{routing_key} ->/)
|
|
320
|
+
expect(logger).to have_received(:info).with(/END Transmitting Message on id\[[\w\-\d]+\] #{routing_key} ->/)
|
|
177
321
|
end
|
|
178
322
|
|
|
179
323
|
it "closes the channel connector" do
|
|
180
324
|
transmitter.send_message!(payload,routing_key)
|
|
181
325
|
expect(channel_connector).to have_received(:connection_close)
|
|
182
326
|
end
|
|
327
|
+
|
|
328
|
+
context "with only basic required arguments" do
|
|
329
|
+
|
|
330
|
+
before do
|
|
331
|
+
transmitter.send_message!(payload,routing_key)
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
it_behaves_like "it sends standard attributes and the payload to the exchange" do
|
|
335
|
+
let(:exchange) { topic_exchange }
|
|
336
|
+
end
|
|
337
|
+
end
|
|
338
|
+
context "with everything overridden" do
|
|
339
|
+
before do
|
|
340
|
+
transmitter.send_message!(
|
|
341
|
+
payload,
|
|
342
|
+
routing_key,
|
|
343
|
+
message_id: "snowflake id that is likely a bad idea, but if you must",
|
|
344
|
+
type: "Customer",
|
|
345
|
+
headers: {
|
|
346
|
+
"custom" => "value",
|
|
347
|
+
"other_custom" => "other_value",
|
|
348
|
+
}
|
|
349
|
+
)
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
it_behaves_like "it sends standard and overridden data to the exchange" do
|
|
353
|
+
let(:exchange) { topic_exchange }
|
|
354
|
+
end
|
|
355
|
+
end
|
|
183
356
|
end
|
|
184
357
|
|
|
185
358
|
describe "#send_delayed_message!" do
|
|
@@ -189,29 +362,44 @@ describe Pwwka::Transmitter do
|
|
|
189
362
|
allow(channel_connector).to receive(:create_delayed_queue)
|
|
190
363
|
end
|
|
191
364
|
|
|
192
|
-
it "returns true" do
|
|
193
|
-
expect(transmitter.send_delayed_message!(payload,routing_key)).to eq(true)
|
|
194
|
-
end
|
|
195
|
-
|
|
196
365
|
it "creates the delayed queue" do
|
|
197
366
|
transmitter.send_delayed_message!(payload,routing_key)
|
|
198
367
|
expect(channel_connector).to have_received(:create_delayed_queue)
|
|
199
368
|
end
|
|
200
369
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
end
|
|
370
|
+
context "with only basic required arguments" do
|
|
371
|
+
before do
|
|
372
|
+
transmitter.send_delayed_message!(payload,routing_key,5_000)
|
|
373
|
+
end
|
|
206
374
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
375
|
+
it_behaves_like "it sends standard attributes and the payload to the exchange" do
|
|
376
|
+
let(:exchange) { delayed_exchange }
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
it "passes an expiration value" do
|
|
380
|
+
expect(delayed_exchange).to have_received(:publish).with(
|
|
381
|
+
payload.to_json,
|
|
382
|
+
hash_including(expiration: 5_000))
|
|
383
|
+
end
|
|
211
384
|
end
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
385
|
+
|
|
386
|
+
context "with everything overridden" do
|
|
387
|
+
before do
|
|
388
|
+
transmitter.send_delayed_message!(
|
|
389
|
+
payload,
|
|
390
|
+
routing_key,
|
|
391
|
+
message_id: "snowflake id that is likely a bad idea, but if you must",
|
|
392
|
+
type: "Customer",
|
|
393
|
+
headers: {
|
|
394
|
+
"custom" => "value",
|
|
395
|
+
"other_custom" => "other_value",
|
|
396
|
+
}
|
|
397
|
+
)
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
it_behaves_like "it sends standard and overridden data to the exchange" do
|
|
401
|
+
let(:exchange) { delayed_exchange }
|
|
402
|
+
end
|
|
215
403
|
end
|
|
216
404
|
end
|
|
217
405
|
context "delayed queue not configured" do
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pwwka
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.11.0.RC1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stitch Fix Engineering
|
|
@@ -15,7 +15,7 @@ authors:
|
|
|
15
15
|
autorequire:
|
|
16
16
|
bindir: bin
|
|
17
17
|
cert_chain: []
|
|
18
|
-
date: 2017-
|
|
18
|
+
date: 2017-06-08 00:00:00.000000000 Z
|
|
19
19
|
dependencies:
|
|
20
20
|
- !ruby/object:Gem::Dependency
|
|
21
21
|
name: bunny
|
|
@@ -157,6 +157,20 @@ dependencies:
|
|
|
157
157
|
- - ">="
|
|
158
158
|
- !ruby/object:Gem::Version
|
|
159
159
|
version: '0'
|
|
160
|
+
- !ruby/object:Gem::Dependency
|
|
161
|
+
name: rainbow
|
|
162
|
+
requirement: !ruby/object:Gem::Requirement
|
|
163
|
+
requirements:
|
|
164
|
+
- - ">="
|
|
165
|
+
- !ruby/object:Gem::Version
|
|
166
|
+
version: '0'
|
|
167
|
+
type: :development
|
|
168
|
+
prerelease: false
|
|
169
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
170
|
+
requirements:
|
|
171
|
+
- - ">="
|
|
172
|
+
- !ruby/object:Gem::Version
|
|
173
|
+
version: '0'
|
|
160
174
|
description: |-
|
|
161
175
|
The purpose of this gem is to normalise the sending and
|
|
162
176
|
receiving of messages between Rails apps using the shared RabbitMQ
|
|
@@ -195,6 +209,7 @@ files:
|
|
|
195
209
|
- lib/pwwka/handling.rb
|
|
196
210
|
- lib/pwwka/logging.rb
|
|
197
211
|
- lib/pwwka/message_queuer.rb
|
|
212
|
+
- lib/pwwka/publish_options.rb
|
|
198
213
|
- lib/pwwka/queue_resque_job_handler.rb
|
|
199
214
|
- lib/pwwka/receiver.rb
|
|
200
215
|
- lib/pwwka/send_message_async_job.rb
|
|
@@ -217,9 +232,11 @@ files:
|
|
|
217
232
|
- spec/spec_helper.rb
|
|
218
233
|
- spec/support/test_configuration.rb
|
|
219
234
|
- spec/unit/channel_connector_spec.rb
|
|
235
|
+
- spec/unit/configuration_spec.rb
|
|
220
236
|
- spec/unit/logging_spec.rb
|
|
221
237
|
- spec/unit/message_queuer_spec.rb
|
|
222
238
|
- spec/unit/queue_resque_job_handler_spec.rb
|
|
239
|
+
- spec/unit/send_message_async_job_spec.rb
|
|
223
240
|
- spec/unit/test_handler_message_spec.rb
|
|
224
241
|
- spec/unit/transmitter_spec.rb
|
|
225
242
|
homepage: https://github.com/stitchfix/pwwka
|
|
@@ -237,12 +254,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
237
254
|
version: '0'
|
|
238
255
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
239
256
|
requirements:
|
|
240
|
-
- - "
|
|
257
|
+
- - ">"
|
|
241
258
|
- !ruby/object:Gem::Version
|
|
242
|
-
version:
|
|
259
|
+
version: 1.3.1
|
|
243
260
|
requirements: []
|
|
244
261
|
rubyforge_project:
|
|
245
|
-
rubygems_version: 2.
|
|
262
|
+
rubygems_version: 2.6.11
|
|
246
263
|
signing_key:
|
|
247
264
|
specification_version: 4
|
|
248
265
|
summary: Send and receive messages via RabbitMQ
|
|
@@ -261,8 +278,10 @@ test_files:
|
|
|
261
278
|
- spec/spec_helper.rb
|
|
262
279
|
- spec/support/test_configuration.rb
|
|
263
280
|
- spec/unit/channel_connector_spec.rb
|
|
281
|
+
- spec/unit/configuration_spec.rb
|
|
264
282
|
- spec/unit/logging_spec.rb
|
|
265
283
|
- spec/unit/message_queuer_spec.rb
|
|
266
284
|
- spec/unit/queue_resque_job_handler_spec.rb
|
|
285
|
+
- spec/unit/send_message_async_job_spec.rb
|
|
267
286
|
- spec/unit/test_handler_message_spec.rb
|
|
268
287
|
- spec/unit/transmitter_spec.rb
|