message_bus 2.2.4 → 3.3.1
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.
Potentially problematic release.
This version of message_bus might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.rubocop.yml +9 -1
- data/.travis.yml +1 -1
- data/CHANGELOG +33 -0
- data/Gemfile +6 -3
- data/README.md +47 -11
- data/Rakefile +4 -0
- data/assets/message-bus.js +59 -77
- data/lib/message_bus.rb +33 -0
- data/lib/message_bus/backends/redis.rb +11 -8
- data/lib/message_bus/client.rb +36 -8
- data/lib/message_bus/diagnostics.rb +0 -1
- data/lib/message_bus/rack/diagnostics.rb +5 -4
- data/lib/message_bus/rack/middleware.rb +8 -4
- data/lib/message_bus/rails/railtie.rb +15 -13
- data/lib/message_bus/version.rb +1 -1
- data/package.json +20 -0
- data/spec/assets/message-bus.spec.js +0 -9
- data/spec/lib/message_bus/client_spec.rb +206 -23
- data/spec/lib/message_bus/rack/middleware_spec.rb +62 -0
- data/spec/lib/message_bus_spec.rb +32 -0
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 659722c1ea3a6a805dbe81099ee2848a11ab7ebffaf8612d119f5062fd740337
|
4
|
+
data.tar.gz: 4b187421f5fb4bdff177d117b059c05314305729885f5ec520127ad827db5369
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c345409c8c9c1624b079d28ca166b962f9d64bd79159fc085055df8cb760545372732fd5cf509061b55eb0a427e8ffd7018238a7b49c44f27f4e942d575f0504
|
7
|
+
data.tar.gz: d92fb533dea19da315c210f7ac96a1029cf2d1709fee3dd919032f221b557cff9ebb3d2556285e5e0c6018ebc4b227ed178fd8658711c5a9ffacf19aeb47edff
|
data/.rubocop.yml
CHANGED
data/.travis.yml
CHANGED
data/CHANGELOG
CHANGED
@@ -1,3 +1,36 @@
|
|
1
|
+
09-06-2020
|
2
|
+
|
3
|
+
- Version 3.3.1
|
4
|
+
|
5
|
+
- FIX: Disconnect Redis conn when rescuing errors in global subscribe.
|
6
|
+
- FIX: `MessageBus::Backends::Redis#global_subscribe` not closing Redis connections.
|
7
|
+
|
8
|
+
15-05-2020
|
9
|
+
|
10
|
+
- Version 3.3.0
|
11
|
+
|
12
|
+
- FEATURE: `MessageBus.base_route=` to alter the route that message bus will listen on.
|
13
|
+
|
14
|
+
07-05-2020
|
15
|
+
|
16
|
+
- Version 3.2.0
|
17
|
+
|
18
|
+
- FIX: compatability with Rails 6.0.3, note: apps without ActionDispatch::Flash may stop working after this upgrade
|
19
|
+
to correct this disable middleware injection with `config.skip_message_bus_middleware = true` and configure middleware by hand with `app.middleware.use(MessageBus::Rack::Middleware)`
|
20
|
+
|
21
|
+
28-04-2020
|
22
|
+
|
23
|
+
- Version 3.1.0
|
24
|
+
|
25
|
+
- FEATURE: `MessageBus#register_client_message_filter` to register a custom filter so that messages can be inspected and filtered away from clients.
|
26
|
+
|
27
|
+
27-04-2020
|
28
|
+
|
29
|
+
- Version 3.0.0
|
30
|
+
|
31
|
+
- Drop support for Ruby 2.3
|
32
|
+
- FIX: Don't publish message to intersection of `user_ids` and `group_ids` - instead use the union, this is a behavior change, hence a new major release.
|
33
|
+
|
1
34
|
26-03-2020
|
2
35
|
|
3
36
|
- Version 2.2.4
|
data/Gemfile
CHANGED
@@ -20,8 +20,11 @@ group :test, :development do
|
|
20
20
|
gem 'byebug'
|
21
21
|
end
|
22
22
|
|
23
|
+
group :development do
|
24
|
+
gem 'yard'
|
25
|
+
gem 'rubocop-discourse', require: false
|
26
|
+
gem 'rubocop-rspec', require: false
|
27
|
+
end
|
28
|
+
|
23
29
|
gem 'rack'
|
24
30
|
gem 'concurrent-ruby' # for distributed-cache
|
25
|
-
|
26
|
-
gem 'rubocop'
|
27
|
-
gem 'yard'
|
data/README.md
CHANGED
@@ -74,32 +74,40 @@ id = MessageBus.last_id("/channel")
|
|
74
74
|
MessageBus.backlog "/channel", id
|
75
75
|
```
|
76
76
|
|
77
|
-
|
78
|
-
|
79
|
-
|
77
|
+
### Targetted messages
|
78
|
+
|
79
|
+
Messages can be targetted to particular clients by supplying the `client_ids` option when publishing a message.
|
80
80
|
|
81
|
-
|
82
|
-
MessageBus.publish "/channel", "hello", client_ids: ["XXX","YYY"]
|
81
|
+
```ruby
|
82
|
+
MessageBus.publish "/channel", "hello", client_ids: ["XXX", "YYY"] # (using MessageBus.clientId)
|
83
|
+
```
|
83
84
|
|
84
|
-
|
85
|
+
By configuring the `user_id_lookup` and `group_ids_lookup` options with a Proc or Lambda which will be called with a [Rack specification environment](https://github.com/rack/rack/blob/master/SPEC.rdoc#the-environment-), messages can be targetted to particular clients users or groups by supplying either the `user_ids` or `group_ids` options when publishing a message.
|
85
86
|
|
87
|
+
```ruby
|
86
88
|
MessageBus.configure(user_id_lookup: proc do |env|
|
87
89
|
# this lookup occurs on JS-client poolings, so that server can retrieve backlog
|
88
90
|
# for the client considering/matching/filtering user_ids set on published messages
|
89
91
|
# if user_id is not set on publish time, any user_id returned here will receive the message
|
90
|
-
|
91
92
|
# return the user id here
|
92
93
|
end)
|
93
94
|
|
95
|
+
# Target user_ids when publishing a message
|
96
|
+
MessageBus.publish "/channel", "hello", user_ids: [1, 2, 3]
|
97
|
+
|
94
98
|
MessageBus.configure(group_ids_lookup: proc do |env|
|
95
99
|
# return the group ids the user belongs to
|
96
100
|
# can be nil or []
|
97
101
|
end)
|
98
102
|
|
99
|
-
#
|
103
|
+
# Target group_ids when publishing a message
|
104
|
+
MessageBus.publish "/channel", "hello", group_ids: [1, 2, 3]
|
105
|
+
|
106
|
+
# example of MessageBus to set user_ids from an initializer in Rails and Devise:
|
100
107
|
# config/inializers/message_bus.rb
|
101
108
|
MessageBus.user_id_lookup do |env|
|
102
109
|
req = Rack::Request.new(env)
|
110
|
+
|
103
111
|
if req.session && req.session["warden.user.user.key"] && req.session["warden.user.user.key"][0][0]
|
104
112
|
user = User.find(req.session["warden.user.user.key"][0][0])
|
105
113
|
user.id
|
@@ -107,6 +115,36 @@ MessageBus.user_id_lookup do |env|
|
|
107
115
|
end
|
108
116
|
```
|
109
117
|
|
118
|
+
If both `user_ids` and `group_ids` options are supplied when publishing a message, the message will be targetted at clients with lookup return values that matches on either the `user_ids` **or** the `group_ids` options.
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
MessageBus.publish "/channel", "hello", user_ids: [1, 2, 3], group_ids: [1, 2, 3]
|
122
|
+
```
|
123
|
+
|
124
|
+
If the `client_ids` option is supplied with either the `user_ids` or `group_ids` options when publising a message, the `client_ids` option will be applied unconditionally and messages will be filtered further using `user_id` or `group_id` clauses.
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
MessageBus.publish "/channel", "hello", client_ids: ["XXX", "YYY"], user_ids: [1, 2, 3], group_ids: [1, 2, 3]
|
128
|
+
```
|
129
|
+
|
130
|
+
Passing `nil` or `[]` to either `client_ids`, `user_ids` or `group_ids` is equivalent to allowing all values on each option.
|
131
|
+
|
132
|
+
### Filtering Client Messages
|
133
|
+
|
134
|
+
Custom client message filters can be registered via `MessageBus#register_client_message_filter`. This can be useful for filtering away messages from the client based on the message's payload.
|
135
|
+
|
136
|
+
For example, ensuring that only messages seen by the server in the last 20 seconds are published to the client:
|
137
|
+
|
138
|
+
```
|
139
|
+
MessageBus.register_client_message_filter('/test') do |message|
|
140
|
+
(Time.now.to_i - message.data[:published_at]) <= 20
|
141
|
+
end
|
142
|
+
|
143
|
+
MessageBus.publish('/test/5', { data: "somedata", published_at: Time.now.to_i })
|
144
|
+
```
|
145
|
+
|
146
|
+
### Error handling
|
147
|
+
|
110
148
|
```ruby
|
111
149
|
MessageBus.configure(on_middleware_error: proc do |env, e|
|
112
150
|
# If you wish to add special handling based on error
|
@@ -281,7 +319,7 @@ minPollInterval|100|When polling requests succeed, this is the minimum amount of
|
|
281
319
|
maxPollInterval|180000|If request to the server start failing, MessageBus will backoff, this is the upper limit of the backoff.
|
282
320
|
alwaysLongPoll|false|For debugging you may want to disable the "is browser in background" check and always long-poll
|
283
321
|
shouldLongPollCallback|undefined|A callback returning true or false that determines if we should long-poll or not, if unset ignore and simply depend on window visibility.
|
284
|
-
baseUrl|/|If message bus is mounted at a sub-path or different domain, you may configure it to perform requests there
|
322
|
+
baseUrl|/|If message bus is mounted at a sub-path or different domain, you may configure it to perform requests there. See `MessageBus.base_route=` on how to configure the MessageBus server to listen on a sub-path.
|
285
323
|
ajax|$.ajax falling back to XMLHttpRequest|MessageBus will first attempt to use jQuery and then fallback to a plain XMLHttpRequest version that's contained in the `message-bus-ajax.js` file. `message-bus-ajax.js` must be loaded after `message-bus.js` for it to be used. You may override this option with a function that implements an ajax request by some other means
|
286
324
|
headers|{}|Extra headers to be include with requests. Properties and values of object must be valid values for HTTP Headers, i.e. no spaces or control characters.
|
287
325
|
minHiddenPollInterval|1500|Time to wait between poll requests performed by background or hidden tabs and windows, shared state via localStorage
|
@@ -303,8 +341,6 @@ enableChunkedEncoding|true|Allows streaming of message bus data over the HTTP co
|
|
303
341
|
|
304
342
|
`MessageBus.status()` : Returns status (started, paused, stopped)
|
305
343
|
|
306
|
-
`MessageBus.noConflict()` : Removes MessageBus from the global namespace by replacing it with whatever was present before MessageBus was loaded. Returns a reference to the MessageBus object.
|
307
|
-
|
308
344
|
`MessageBus.diagnostics()` : Returns a log that may be used for diagnostics on the status of message bus.
|
309
345
|
|
310
346
|
#### Ruby
|
data/Rakefile
CHANGED
data/assets/message-bus.js
CHANGED
@@ -1,55 +1,52 @@
|
|
1
1
|
/*jshint bitwise: false*/
|
2
|
-
|
2
|
+
|
3
|
+
(function (root, factory) {
|
4
|
+
if (typeof define === 'function' && define.amd) {
|
5
|
+
// AMD. Register as an anonymous module.
|
6
|
+
define([], function (b) {
|
7
|
+
// Also create a global in case some scripts
|
8
|
+
// that are loaded still are looking for
|
9
|
+
// a global even when an AMD loader is in use.
|
10
|
+
return (root.MessageBus = factory());
|
11
|
+
});
|
12
|
+
} else {
|
13
|
+
// Browser globals
|
14
|
+
root.MessageBus = factory();
|
15
|
+
}
|
16
|
+
}(typeof self !== 'undefined' ? self : this, function () {
|
3
17
|
"use strict";
|
4
|
-
var previousMessageBus = global.MessageBus;
|
5
18
|
|
6
19
|
// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
|
7
|
-
var
|
8
|
-
clientId,
|
9
|
-
failCount,
|
10
|
-
shouldLongPoll,
|
11
|
-
queue,
|
12
|
-
responseCallbacks,
|
13
|
-
uniqueId,
|
14
|
-
baseUrl;
|
15
|
-
var me,
|
16
|
-
started,
|
17
|
-
stopped,
|
18
|
-
longPoller,
|
19
|
-
pollTimeout,
|
20
|
-
paused,
|
21
|
-
later,
|
22
|
-
jQuery,
|
23
|
-
interval,
|
24
|
-
chunkedBackoff;
|
25
|
-
var delayPollTimeout;
|
26
|
-
|
27
|
-
var ajaxInProgress = false;
|
28
|
-
|
29
|
-
uniqueId = function() {
|
20
|
+
var uniqueId = function() {
|
30
21
|
return "xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx".replace(/[xy]/g, function(c) {
|
31
|
-
var r
|
32
|
-
|
33
|
-
v = c === "x" ? r : (r & 0x3) | 0x8;
|
22
|
+
var r = (Math.random() * 16) | 0;
|
23
|
+
var v = c === "x" ? r : (r & 0x3) | 0x8;
|
34
24
|
return v.toString(16);
|
35
25
|
});
|
36
26
|
};
|
37
27
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
var
|
50
|
-
|
51
|
-
|
28
|
+
var me;
|
29
|
+
var delayPollTimeout;
|
30
|
+
var ajaxInProgress = false;
|
31
|
+
var started = false;
|
32
|
+
var clientId = uniqueId();
|
33
|
+
var callbacks = [];
|
34
|
+
var queue = [];
|
35
|
+
var interval = null;
|
36
|
+
var failCount = 0;
|
37
|
+
var baseUrl = "/";
|
38
|
+
var paused = false;
|
39
|
+
var later = [];
|
40
|
+
var chunkedBackoff = 0;
|
41
|
+
var stopped;
|
42
|
+
var pollTimeout = null;
|
43
|
+
var totalAjaxFailures = 0;
|
44
|
+
var totalAjaxCalls = 0;
|
45
|
+
var lastAjax;
|
46
|
+
|
47
|
+
var isHidden = (function() {
|
52
48
|
var prefixes = ["", "webkit", "ms", "moz"];
|
49
|
+
var hiddenProperty;
|
53
50
|
for (var i = 0; i < prefixes.length; i++) {
|
54
51
|
var prefix = prefixes[i];
|
55
52
|
var check = prefix + (prefix === "" ? "hidden" : "Hidden");
|
@@ -57,15 +54,15 @@
|
|
57
54
|
hiddenProperty = check;
|
58
55
|
}
|
59
56
|
}
|
60
|
-
})();
|
61
57
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
58
|
+
return function() {
|
59
|
+
if (hiddenProperty !== undefined) {
|
60
|
+
return document[hiddenProperty];
|
61
|
+
} else {
|
62
|
+
return !document.hasFocus;
|
63
|
+
}
|
64
|
+
};
|
65
|
+
})();
|
69
66
|
|
70
67
|
var hasLocalStorage = (function() {
|
71
68
|
try {
|
@@ -98,24 +95,19 @@
|
|
98
95
|
return me.enableChunkedEncoding && hasonprogress;
|
99
96
|
};
|
100
97
|
|
101
|
-
shouldLongPoll = function() {
|
98
|
+
var shouldLongPoll = function() {
|
102
99
|
return (
|
103
100
|
me.alwaysLongPoll ||
|
104
101
|
(me.shouldLongPollCallback ? me.shouldLongPollCallback() : !isHidden())
|
105
102
|
);
|
106
103
|
};
|
107
104
|
|
108
|
-
var totalAjaxFailures = 0;
|
109
|
-
var totalAjaxCalls = 0;
|
110
|
-
var lastAjax;
|
111
|
-
|
112
105
|
var processMessages = function(messages) {
|
113
106
|
var gotData = false;
|
114
|
-
if (!messages)
|
107
|
+
if ((!messages) || (messages.length === 0)) { return false; }
|
115
108
|
|
116
109
|
for (var i = 0; i < messages.length; i++) {
|
117
110
|
var message = messages[i];
|
118
|
-
gotData = true;
|
119
111
|
for (var j = 0; j < callbacks.length; j++) {
|
120
112
|
var callback = callbacks[j];
|
121
113
|
if (callback.channel === message.channel) {
|
@@ -141,7 +133,7 @@
|
|
141
133
|
}
|
142
134
|
}
|
143
135
|
|
144
|
-
return
|
136
|
+
return true;
|
145
137
|
};
|
146
138
|
|
147
139
|
var reqSuccess = function(messages) {
|
@@ -158,7 +150,7 @@
|
|
158
150
|
return false;
|
159
151
|
};
|
160
152
|
|
161
|
-
longPoller = function(poll, data) {
|
153
|
+
var longPoller = function(poll, data) {
|
162
154
|
if (ajaxInProgress) {
|
163
155
|
// never allow concurrent ajax reqs
|
164
156
|
return;
|
@@ -180,9 +172,7 @@
|
|
180
172
|
chunked = false;
|
181
173
|
}
|
182
174
|
|
183
|
-
var headers = {
|
184
|
-
"X-SILENCE-LOGGER": "true"
|
185
|
-
};
|
175
|
+
var headers = { "X-SILENCE-LOGGER": "true" };
|
186
176
|
for (var name in me.headers) {
|
187
177
|
headers[name] = me.headers[name];
|
188
178
|
}
|
@@ -383,11 +373,7 @@
|
|
383
373
|
shouldLongPollCallback: undefined,
|
384
374
|
baseUrl: baseUrl,
|
385
375
|
headers: {},
|
386
|
-
ajax: jQuery && jQuery.ajax,
|
387
|
-
noConflict: function() {
|
388
|
-
global.MessageBus = global.MessageBus.previousMessageBus;
|
389
|
-
return this;
|
390
|
-
},
|
376
|
+
ajax: typeof jQuery !== "undefined" && jQuery.ajax,
|
391
377
|
diagnostics: function() {
|
392
378
|
console.log("Stopped: " + stopped + " Started: " + started);
|
393
379
|
console.log("Current callbacks");
|
@@ -429,15 +415,11 @@
|
|
429
415
|
|
430
416
|
// Start polling
|
431
417
|
start: function() {
|
432
|
-
var poll;
|
433
|
-
|
434
418
|
if (started) return;
|
435
419
|
started = true;
|
436
420
|
stopped = false;
|
437
421
|
|
438
|
-
poll = function() {
|
439
|
-
var data;
|
440
|
-
|
422
|
+
var poll = function() {
|
441
423
|
if (stopped) {
|
442
424
|
return;
|
443
425
|
}
|
@@ -452,7 +434,7 @@
|
|
452
434
|
return;
|
453
435
|
}
|
454
436
|
|
455
|
-
data = {};
|
437
|
+
var data = {};
|
456
438
|
for (var i = 0; i < callbacks.length; i++) {
|
457
439
|
data[callbacks[i].channel] = callbacks[i].last_id;
|
458
440
|
}
|
@@ -466,7 +448,7 @@
|
|
466
448
|
|
467
449
|
// monitor visibility, issue a new long poll when the page shows
|
468
450
|
if (document.addEventListener && "hidden" in document) {
|
469
|
-
me.visibilityEvent =
|
451
|
+
me.visibilityEvent = document.addEventListener(
|
470
452
|
"visibilitychange",
|
471
453
|
function() {
|
472
454
|
if (!document.hidden && !me.longPoll && pollTimeout) {
|
@@ -532,7 +514,7 @@
|
|
532
514
|
// TODO allow for globbing in the middle of a channel name
|
533
515
|
// like /something/*/something
|
534
516
|
// at the moment we only support globbing /something/*
|
535
|
-
var glob;
|
517
|
+
var glob = false;
|
536
518
|
if (channel.indexOf("*", channel.length - 1) !== -1) {
|
537
519
|
channel = channel.substr(0, channel.length - 1);
|
538
520
|
glob = true;
|
@@ -567,5 +549,5 @@
|
|
567
549
|
return removed;
|
568
550
|
}
|
569
551
|
};
|
570
|
-
|
571
|
-
})
|
552
|
+
return me;
|
553
|
+
}));
|
data/lib/message_bus.rb
CHANGED
@@ -149,6 +149,19 @@ module MessageBus::Implementation
|
|
149
149
|
@config[:long_polling_interval] || 25 * 1000
|
150
150
|
end
|
151
151
|
|
152
|
+
# @param [String] route Message bus will listen to requests on this route.
|
153
|
+
# @return [void]
|
154
|
+
def base_route=(route)
|
155
|
+
configure(base_route: route.gsub(Regexp.new('\A(?!/)|(?<!/)\Z|//+'), "/"))
|
156
|
+
end
|
157
|
+
|
158
|
+
# @return [String] the route that message bus will respond to. If not
|
159
|
+
# explicitly set, defaults to "/". Requests to "#{base_route}message-bus/*" will be handled
|
160
|
+
# by the message bus server.
|
161
|
+
def base_route
|
162
|
+
@config[:base_route] || "/"
|
163
|
+
end
|
164
|
+
|
152
165
|
# @return [Boolean] whether the bus is disabled or not
|
153
166
|
def off?
|
154
167
|
@off
|
@@ -554,6 +567,26 @@ module MessageBus::Implementation
|
|
554
567
|
@config[:keepalive_interval] || 60
|
555
568
|
end
|
556
569
|
|
570
|
+
# Registers a client message filter that allows messages to be filtered from the client.
|
571
|
+
#
|
572
|
+
# @param [String,Regexp] channel_prefix channel prefix to match against a message's channel
|
573
|
+
#
|
574
|
+
# @yieldparam [MessageBus::Message] message published to the channel that matched the prefix provided
|
575
|
+
# @yieldreturn [Boolean] whether the message should be published to the client
|
576
|
+
# @return [void]
|
577
|
+
def register_client_message_filter(channel_prefix, &blk)
|
578
|
+
if blk
|
579
|
+
configure(client_message_filters: []) if !@config[:client_message_filters]
|
580
|
+
@config[:client_message_filters] << [channel_prefix, blk]
|
581
|
+
end
|
582
|
+
end
|
583
|
+
|
584
|
+
# @return [Array] returns a hash of message filters that have been registered
|
585
|
+
def client_message_filters
|
586
|
+
configure(client_message_filters: []) if !@config[:client_message_filters]
|
587
|
+
@config[:client_message_filters]
|
588
|
+
end
|
589
|
+
|
557
590
|
private
|
558
591
|
|
559
592
|
ENCODE_SITE_TOKEN = "$|$"
|
@@ -251,11 +251,11 @@ LUA
|
|
251
251
|
|
252
252
|
# (see Base#global_unsubscribe)
|
253
253
|
def global_unsubscribe
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
254
|
+
begin
|
255
|
+
new_redis = new_redis_connection
|
256
|
+
new_redis.publish(redis_channel_name, UNSUB_MESSAGE)
|
257
|
+
ensure
|
258
|
+
new_redis&.disconnect!
|
259
259
|
end
|
260
260
|
end
|
261
261
|
|
@@ -278,13 +278,13 @@ LUA
|
|
278
278
|
end
|
279
279
|
|
280
280
|
begin
|
281
|
-
|
281
|
+
global_redis = new_redis_connection
|
282
282
|
|
283
283
|
if highest_id
|
284
284
|
clear_backlog.call(&blk)
|
285
285
|
end
|
286
286
|
|
287
|
-
|
287
|
+
global_redis.subscribe(redis_channel_name) do |on|
|
288
288
|
on.subscribe do
|
289
289
|
if highest_id
|
290
290
|
clear_backlog.call(&blk)
|
@@ -298,7 +298,7 @@ LUA
|
|
298
298
|
|
299
299
|
on.message do |_c, m|
|
300
300
|
if m == UNSUB_MESSAGE
|
301
|
-
|
301
|
+
global_redis.unsubscribe
|
302
302
|
return
|
303
303
|
end
|
304
304
|
m = MessageBus::Message.decode m
|
@@ -320,7 +320,10 @@ LUA
|
|
320
320
|
rescue => error
|
321
321
|
@logger.warn "#{error} subscribe failed, reconnecting in 1 second. Call stack #{error.backtrace}"
|
322
322
|
sleep 1
|
323
|
+
global_redis&.disconnect!
|
323
324
|
retry
|
325
|
+
ensure
|
326
|
+
global_redis&.disconnect!
|
324
327
|
end
|
325
328
|
end
|
326
329
|
|
data/lib/message_bus/client.rb
CHANGED
@@ -128,15 +128,43 @@ class MessageBus::Client
|
|
128
128
|
# @return [Boolean] whether or not the client has permission to receive the
|
129
129
|
# passed message
|
130
130
|
def allowed?(msg)
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
131
|
+
client_allowed = !msg.client_ids || msg.client_ids.length == 0 || msg.client_ids.include?(self.client_id)
|
132
|
+
|
133
|
+
user_allowed = false
|
134
|
+
group_allowed = false
|
135
|
+
|
136
|
+
# this is an inconsistency we should fix anyway, publishing `user_ids: nil` should work same as groups
|
137
|
+
has_users = msg.user_ids && msg.user_ids.length > 0
|
138
|
+
has_groups = msg.group_ids && msg.group_ids.length > 0
|
139
|
+
|
140
|
+
if has_users
|
141
|
+
user_allowed = msg.user_ids.include?(self.user_id)
|
142
|
+
end
|
143
|
+
|
144
|
+
if has_groups
|
145
|
+
group_allowed = (
|
146
|
+
msg.group_ids - (self.group_ids || [])
|
138
147
|
).length < msg.group_ids.length
|
139
|
-
|
148
|
+
end
|
149
|
+
|
150
|
+
has_permission = client_allowed && (user_allowed || group_allowed || (!has_users && !has_groups))
|
151
|
+
|
152
|
+
return has_permission if !has_permission
|
153
|
+
|
154
|
+
filters_allowed = true
|
155
|
+
|
156
|
+
len = @bus.client_message_filters.length
|
157
|
+
while len > 0
|
158
|
+
len -= 1
|
159
|
+
channel_prefix, blk = @bus.client_message_filters[len]
|
160
|
+
|
161
|
+
if msg.channel.start_with?(channel_prefix)
|
162
|
+
filters_allowed = blk.call(msg)
|
163
|
+
break if !filters_allowed
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
filters_allowed
|
140
168
|
end
|
141
169
|
|
142
170
|
# @return [Array<MessageBus::Message>] the set of messages the client is due
|
@@ -16,9 +16,9 @@ class MessageBus::Rack::Diagnostics
|
|
16
16
|
# Process an HTTP request from a subscriber client
|
17
17
|
# @param [Rack::Request::Env] env the request environment
|
18
18
|
def call(env)
|
19
|
-
return @app.call(env) unless env['PATH_INFO'].start_with?
|
19
|
+
return @app.call(env) unless env['PATH_INFO'].start_with? "#{@bus.base_route}message-bus/_diagnostics"
|
20
20
|
|
21
|
-
route = env['PATH_INFO'].split(
|
21
|
+
route = env['PATH_INFO'].split("#{@bus.base_route}message-bus/_diagnostics")[1]
|
22
22
|
|
23
23
|
if @bus.is_admin_lookup.nil? || !@bus.is_admin_lookup.call(env)
|
24
24
|
return [403, {}, ['not allowed']]
|
@@ -45,7 +45,7 @@ class MessageBus::Rack::Diagnostics
|
|
45
45
|
return [200, { 'Content-Type' => 'application/javascript;charset=UTF-8' }, [content]]
|
46
46
|
end
|
47
47
|
|
48
|
-
|
48
|
+
[404, {}, ['not found']]
|
49
49
|
end
|
50
50
|
|
51
51
|
private
|
@@ -92,6 +92,7 @@ class MessageBus::Rack::Diagnostics
|
|
92
92
|
</body>
|
93
93
|
</html>
|
94
94
|
HTML
|
95
|
-
|
95
|
+
|
96
|
+
[200, { "content-type" => "text/html;" }, [html]]
|
96
97
|
end
|
97
98
|
end
|
@@ -39,6 +39,10 @@ class MessageBus::Rack::Middleware
|
|
39
39
|
@bus = config[:message_bus] || MessageBus
|
40
40
|
@connection_manager = MessageBus::ConnectionManager.new(@bus)
|
41
41
|
@started_listener = false
|
42
|
+
@base_route = "#{@bus.base_route}message-bus/"
|
43
|
+
@base_route_length = @base_route.length
|
44
|
+
@diagnostics_route = "#{@base_route}_diagnostics"
|
45
|
+
@broadcast_route = "#{@base_route}broadcast"
|
42
46
|
start_listener unless @bus.off?
|
43
47
|
end
|
44
48
|
|
@@ -54,7 +58,7 @@ class MessageBus::Rack::Middleware
|
|
54
58
|
# Process an HTTP request from a subscriber client
|
55
59
|
# @param [Rack::Request::Env] env the request environment
|
56
60
|
def call(env)
|
57
|
-
return @app.call(env) unless env['PATH_INFO']
|
61
|
+
return @app.call(env) unless env['PATH_INFO'].start_with? @base_route
|
58
62
|
|
59
63
|
handle_request(env)
|
60
64
|
end
|
@@ -63,18 +67,18 @@ class MessageBus::Rack::Middleware
|
|
63
67
|
|
64
68
|
def handle_request(env)
|
65
69
|
# special debug/test route
|
66
|
-
if @bus.allow_broadcast? && env['PATH_INFO'] ==
|
70
|
+
if @bus.allow_broadcast? && env['PATH_INFO'] == @broadcast_route
|
67
71
|
parsed = Rack::Request.new(env)
|
68
72
|
@bus.publish parsed["channel"], parsed["data"]
|
69
73
|
return [200, { "Content-Type" => "text/html" }, ["sent"]]
|
70
74
|
end
|
71
75
|
|
72
|
-
if env['PATH_INFO'].start_with?
|
76
|
+
if env['PATH_INFO'].start_with? @diagnostics_route
|
73
77
|
diags = MessageBus::Rack::Diagnostics.new(@app, message_bus: @bus)
|
74
78
|
return diags.call(env)
|
75
79
|
end
|
76
80
|
|
77
|
-
client_id = env['PATH_INFO'].split("/")[
|
81
|
+
client_id = env['PATH_INFO'][@base_route_length..-1].split("/")[0]
|
78
82
|
return [404, {}, ["not found"]] unless client_id
|
79
83
|
|
80
84
|
user_id = @bus.user_id_lookup.call(env) if @bus.user_id_lookup
|
@@ -11,27 +11,29 @@ class MessageBus::Rails::Railtie < ::Rails::Railtie
|
|
11
11
|
# the Rails app is configured that might be ActionDispatch::Session::CookieStore, or potentially
|
12
12
|
# ActionDispatch::Session::ActiveRecordStore.
|
13
13
|
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
14
|
+
# given https://github.com/rails/rails/commit/fedde239dcee256b417dc9bcfe5fef603bf0d952#diff-533a9a9cc17a8a899cb830626089e5f9
|
15
|
+
# there is no way of walking the stack for operations
|
16
|
+
if !skip_middleware?(app.config)
|
17
|
+
if api_only?(app.config)
|
18
|
+
app.middleware.use(MessageBus::Rack::Middleware)
|
19
|
+
else
|
20
|
+
app.middleware.insert_before(ActionDispatch::Flash, MessageBus::Rack::Middleware)
|
21
|
+
end
|
22
22
|
end
|
23
23
|
|
24
24
|
MessageBus.logger = Rails.logger
|
25
25
|
end
|
26
26
|
|
27
|
+
def skip_middleware?(config)
|
28
|
+
return false if !config.respond_to?(:skip_message_bus_middleware)
|
29
|
+
|
30
|
+
config.skip_message_bus_middleware
|
31
|
+
end
|
32
|
+
|
27
33
|
def api_only?(config)
|
28
|
-
return false
|
34
|
+
return false if !config.respond_to?(:api_only)
|
29
35
|
|
30
36
|
config.api_only
|
31
37
|
end
|
32
38
|
|
33
|
-
def flash_middleware_deleted?(middleware)
|
34
|
-
ops = middleware.instance_variable_get(:@operations)
|
35
|
-
ops.any? { |m| m[0] == :delete && m[1].include?(ActionDispatch::Flash) }
|
36
|
-
end
|
37
39
|
end
|
data/lib/message_bus/version.rb
CHANGED
data/package.json
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
{
|
2
|
+
"name": "message-bus-client",
|
3
|
+
"version": "3.3.0",
|
4
|
+
"description": "A message bus client in Javascript",
|
5
|
+
"main": "assets/message-bus.js",
|
6
|
+
"keywords": "es6, modules",
|
7
|
+
"files": ["assets/message-bus.js"],
|
8
|
+
"jsnext:main": "assets/message-bus.js",
|
9
|
+
"module": "assets/message-bus.js",
|
10
|
+
"repository": {
|
11
|
+
"type": "git",
|
12
|
+
"url": "git+https://github.com/discourse/message_bus.git"
|
13
|
+
},
|
14
|
+
"author": "Sam Saffron, Robin Ward",
|
15
|
+
"license": "MIT",
|
16
|
+
"bugs": {
|
17
|
+
"url": "https://github.com/discourse/message_bus/issues"
|
18
|
+
},
|
19
|
+
"homepage": "https://github.com/discourse/message_bus#readme"
|
20
|
+
}
|
@@ -75,15 +75,6 @@ describe("Messagebus", function() {
|
|
75
75
|
})
|
76
76
|
});
|
77
77
|
|
78
|
-
it('removes itself from root namespace when noConflict is called', function(){
|
79
|
-
expect(window.MessageBus).not.toBeUndefined();
|
80
|
-
var mb = window.MessageBus;
|
81
|
-
expect(mb).toEqual(window.MessageBus.noConflict());
|
82
|
-
expect(window.MessageBus).toBeUndefined();
|
83
|
-
// reset it so afterEach has something to work on
|
84
|
-
window.MessageBus = mb;
|
85
|
-
});
|
86
|
-
|
87
78
|
it('respects minPollInterval setting with defaults', function(){
|
88
79
|
expect(MessageBus.minPollInterval).toEqual(100);
|
89
80
|
MessageBus.minPollInterval = 1000;
|
@@ -169,37 +169,220 @@ describe MessageBus::Client do
|
|
169
169
|
log[0].data.must_equal 'world'
|
170
170
|
end
|
171
171
|
|
172
|
-
|
173
|
-
|
174
|
-
@message.client_ids = ["1", "2"]
|
175
|
-
@client.client_id = "2"
|
176
|
-
@client.allowed?(@message).must_equal true
|
177
|
-
|
178
|
-
@client.client_id = "3"
|
179
|
-
@client.allowed?(@message).must_equal false
|
180
|
-
end
|
181
|
-
|
182
|
-
describe "targetted at group" do
|
183
|
-
before do
|
172
|
+
describe '#allowed?' do
|
173
|
+
it "allows only client_id in list if message contains client_ids" do
|
184
174
|
@message = MessageBus::Message.new(1, 2, '/test', 'hello')
|
185
|
-
@message.
|
186
|
-
|
175
|
+
@message.client_ids = ["1", "2"]
|
176
|
+
@client.client_id = "2"
|
177
|
+
@client.allowed?(@message).must_equal true
|
187
178
|
|
188
|
-
|
189
|
-
@client.group_ids = [77, 0, 10]
|
179
|
+
@client.client_id = "3"
|
190
180
|
@client.allowed?(@message).must_equal false
|
191
|
-
end
|
192
181
|
|
193
|
-
|
194
|
-
|
182
|
+
@message.client_ids = []
|
183
|
+
|
184
|
+
@client.client_id = "3"
|
195
185
|
@client.allowed?(@message).must_equal true
|
196
|
-
end
|
197
186
|
|
198
|
-
|
199
|
-
|
200
|
-
@client.
|
187
|
+
@message.client_ids = nil
|
188
|
+
|
189
|
+
@client.client_id = "3"
|
201
190
|
@client.allowed?(@message).must_equal true
|
202
191
|
end
|
192
|
+
|
193
|
+
describe 'targetted at user' do
|
194
|
+
before do
|
195
|
+
@message = MessageBus::Message.new(1, 2, '/test', 'hello')
|
196
|
+
@message.user_ids = [1, 2, 3]
|
197
|
+
end
|
198
|
+
|
199
|
+
it "allows client with user_id that is included in message's user_ids" do
|
200
|
+
@client.user_id = 1
|
201
|
+
@client.allowed?(@message).must_equal(true)
|
202
|
+
end
|
203
|
+
|
204
|
+
it "denies client with user_id that is not included in message's user_ids" do
|
205
|
+
@client.user_id = 4
|
206
|
+
@client.allowed?(@message).must_equal(false)
|
207
|
+
end
|
208
|
+
|
209
|
+
it "denies client with nil user_id" do
|
210
|
+
@client.user_id = nil
|
211
|
+
|
212
|
+
@client.allowed?(@message).must_equal(false)
|
213
|
+
end
|
214
|
+
|
215
|
+
it "allows client if message's user_ids is not set" do
|
216
|
+
@message.user_ids = nil
|
217
|
+
@client.user_id = 4
|
218
|
+
@client.allowed?(@message).must_equal(true)
|
219
|
+
end
|
220
|
+
|
221
|
+
it "allows client if message's user_ids is empty" do
|
222
|
+
@message.user_ids = []
|
223
|
+
@client.user_id = 4
|
224
|
+
@client.allowed?(@message).must_equal(true)
|
225
|
+
end
|
226
|
+
|
227
|
+
it "allows client with client_id that is included in message's client_ids" do
|
228
|
+
@message.client_ids = ["1", "2"]
|
229
|
+
@client.client_id = "1"
|
230
|
+
@client.user_id = 1
|
231
|
+
|
232
|
+
@client.allowed?(@message).must_equal(true)
|
233
|
+
end
|
234
|
+
|
235
|
+
it "denies client with client_id that is not included in message's client_ids" do
|
236
|
+
@message.client_ids = ["1", "2"]
|
237
|
+
@client.client_id = "3"
|
238
|
+
@client.user_id = 1
|
239
|
+
|
240
|
+
@client.allowed?(@message).must_equal(false)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
describe "targetted at group" do
|
245
|
+
before do
|
246
|
+
@message = MessageBus::Message.new(1, 2, '/test', 'hello')
|
247
|
+
@message.group_ids = [1, 2, 3]
|
248
|
+
end
|
249
|
+
|
250
|
+
it "denies client that are not members of group" do
|
251
|
+
@client.group_ids = [77, 0, 10]
|
252
|
+
@client.allowed?(@message).must_equal false
|
253
|
+
end
|
254
|
+
|
255
|
+
it 'denies client with nil group_ids' do
|
256
|
+
@client.group_ids = nil
|
257
|
+
@client.allowed?(@message).must_equal false
|
258
|
+
end
|
259
|
+
|
260
|
+
it "allows client that are members of group" do
|
261
|
+
@client.group_ids = [1, 2, 3]
|
262
|
+
@client.allowed?(@message).must_equal true
|
263
|
+
end
|
264
|
+
|
265
|
+
it "allows any client if message's group_ids is not set" do
|
266
|
+
@message.group_ids = nil
|
267
|
+
@client.group_ids = [77, 0, 10]
|
268
|
+
@client.allowed?(@message).must_equal true
|
269
|
+
end
|
270
|
+
|
271
|
+
it "allows any client if message's group_ids is empty" do
|
272
|
+
@message.group_ids = []
|
273
|
+
@client.group_ids = [77, 0, 10]
|
274
|
+
@client.allowed?(@message).must_equal true
|
275
|
+
end
|
276
|
+
|
277
|
+
it "allows client with client_id that is included in message's client_ids" do
|
278
|
+
@message.client_ids = ["1", "2"]
|
279
|
+
@client.client_id = "1"
|
280
|
+
@client.group_ids = [1]
|
281
|
+
|
282
|
+
@client.allowed?(@message).must_equal(true)
|
283
|
+
end
|
284
|
+
|
285
|
+
it "denies client with client_id that is not included in message's client_ids" do
|
286
|
+
@message.client_ids = ["1", "2"]
|
287
|
+
@client.client_id = "3"
|
288
|
+
@client.group_ids = [1]
|
289
|
+
|
290
|
+
@client.allowed?(@message).must_equal(false)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
describe 'targetted at group and user' do
|
295
|
+
before do
|
296
|
+
@message = MessageBus::Message.new(1, 2, '/test', 'hello')
|
297
|
+
@message.group_ids = [1, 2, 3]
|
298
|
+
@message.user_ids = [4, 5, 6]
|
299
|
+
end
|
300
|
+
|
301
|
+
it "allows client with user_id that is included in message's user_ids" do
|
302
|
+
@client.user_id = 4
|
303
|
+
@client.allowed?(@message).must_equal(true)
|
304
|
+
end
|
305
|
+
|
306
|
+
it "denies client with user_id that is not included in message's user_ids" do
|
307
|
+
@client.user_id = 1
|
308
|
+
@client.allowed?(@message).must_equal(false)
|
309
|
+
end
|
310
|
+
|
311
|
+
it "allows client with group_ids that is included in message's group_ids" do
|
312
|
+
@client.group_ids = [1, 0, 3]
|
313
|
+
@client.allowed?(@message).must_equal(true)
|
314
|
+
end
|
315
|
+
|
316
|
+
it "denies client with group_ids that is not included in message's group_ids" do
|
317
|
+
@client.group_ids = [8, 9, 10]
|
318
|
+
@client.allowed?(@message).must_equal(false)
|
319
|
+
end
|
320
|
+
|
321
|
+
it "allows client with allowed client_id and user_id" do
|
322
|
+
@message.client_ids = ["1", "2"]
|
323
|
+
@client.user_id = 4
|
324
|
+
@client.client_id = "2"
|
325
|
+
|
326
|
+
@client.allowed?(@message).must_equal(true)
|
327
|
+
end
|
328
|
+
|
329
|
+
it "denies client with allowed client_id but disallowed user_id" do
|
330
|
+
@message.client_ids = ["1", "2"]
|
331
|
+
@client.user_id = 99
|
332
|
+
@client.client_id = "2"
|
333
|
+
|
334
|
+
@client.allowed?(@message).must_equal(false)
|
335
|
+
end
|
336
|
+
|
337
|
+
it "allows client with allowed client_id and group_id" do
|
338
|
+
@message.client_ids = ["1", "2"]
|
339
|
+
@client.group_ids = [1]
|
340
|
+
@client.client_id = "2"
|
341
|
+
|
342
|
+
@client.allowed?(@message).must_equal(true)
|
343
|
+
end
|
344
|
+
|
345
|
+
it "denies client with allowed client_id but disallowed group_id" do
|
346
|
+
@message.client_ids = ["1", "2"]
|
347
|
+
@client.group_ids = [99]
|
348
|
+
@client.client_id = "2"
|
349
|
+
|
350
|
+
@client.allowed?(@message).must_equal(false)
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
describe 'when MessageBus#client_message_filters has been configured' do
|
355
|
+
|
356
|
+
it 'filters messages correctly' do
|
357
|
+
message = MessageBus::Message.new(1, 2, '/test/5', 'hello')
|
358
|
+
@client.allowed?(message).must_equal(true)
|
359
|
+
|
360
|
+
@bus.register_client_message_filter('/test') do |m|
|
361
|
+
m.data != 'hello'
|
362
|
+
end
|
363
|
+
|
364
|
+
@client.allowed?(message).must_equal(false)
|
365
|
+
end
|
366
|
+
|
367
|
+
it 'filters messages correctly when multiple filters have been configured' do
|
368
|
+
|
369
|
+
bob_message = MessageBus::Message.new(1, 2, '/test/5', 'bob')
|
370
|
+
fred_message = MessageBus::Message.new(1, 2, '/test/5', 'fred')
|
371
|
+
random_message = MessageBus::Message.new(1, 2, '/test/77', 'random')
|
372
|
+
|
373
|
+
@bus.register_client_message_filter('/test') do |message|
|
374
|
+
message.data == 'bob' || message.data == 'fred'
|
375
|
+
end
|
376
|
+
|
377
|
+
@bus.register_client_message_filter('/test') do |message|
|
378
|
+
message.data == 'fred'
|
379
|
+
end
|
380
|
+
|
381
|
+
@client.allowed?(fred_message).must_equal(true)
|
382
|
+
@client.allowed?(bob_message).must_equal(false)
|
383
|
+
@client.allowed?(random_message).must_equal(false)
|
384
|
+
end
|
385
|
+
end
|
203
386
|
end
|
204
387
|
end
|
205
388
|
end
|
@@ -8,11 +8,13 @@ require 'rack/test'
|
|
8
8
|
describe MessageBus::Rack::Middleware do
|
9
9
|
include Rack::Test::Methods
|
10
10
|
let(:extra_middleware) { nil }
|
11
|
+
let(:base_route) { nil }
|
11
12
|
|
12
13
|
before do
|
13
14
|
bus = @bus = MessageBus::Instance.new
|
14
15
|
@bus.configure(MESSAGE_BUS_CONFIG)
|
15
16
|
@bus.long_polling_enabled = false
|
17
|
+
@bus.base_route = base_route if base_route
|
16
18
|
|
17
19
|
e_m = extra_middleware
|
18
20
|
builder = Rack::Builder.new {
|
@@ -44,12 +46,27 @@ describe MessageBus::Rack::Middleware do
|
|
44
46
|
@bus.long_polling_enabled = true
|
45
47
|
end
|
46
48
|
|
49
|
+
describe "with altered base_route" do
|
50
|
+
let(:base_route) { "/base/route/" }
|
51
|
+
|
52
|
+
it "should respond as normal" do
|
53
|
+
post "/base/route/message-bus/ABC?dlp=t", '/foo1' => 0
|
54
|
+
@async_middleware.in_async?.must_equal false
|
55
|
+
last_response.ok?.must_equal true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
47
59
|
it "should respond right away if dlp=t" do
|
48
60
|
post "/message-bus/ABC?dlp=t", '/foo1' => 0
|
49
61
|
@async_middleware.in_async?.must_equal false
|
50
62
|
last_response.ok?.must_equal true
|
51
63
|
end
|
52
64
|
|
65
|
+
it "should respond with a 404 if the client_id is missing" do
|
66
|
+
post "/message-bus/?dlp=t", '/foo1' => 0
|
67
|
+
last_response.not_found?.must_equal true
|
68
|
+
end
|
69
|
+
|
53
70
|
it "should respond right away to long polls that are polling on -1 with the last_id" do
|
54
71
|
post "/message-bus/ABC", '/foo' => -1
|
55
72
|
last_response.ok?.must_equal true
|
@@ -141,6 +158,19 @@ describe MessageBus::Rack::Middleware do
|
|
141
158
|
last_response.status.must_equal 200
|
142
159
|
end
|
143
160
|
|
161
|
+
describe "with an altered base_route" do
|
162
|
+
let(:base_route) { "/base/route/" }
|
163
|
+
|
164
|
+
it "should get a 200 with html for an authorized user" do
|
165
|
+
def @bus.is_admin_lookup
|
166
|
+
proc { |_| true }
|
167
|
+
end
|
168
|
+
|
169
|
+
get "/base/route/message-bus/_diagnostics"
|
170
|
+
last_response.status.must_equal 200
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
144
174
|
it "should get the script it asks for" do
|
145
175
|
|
146
176
|
def @bus.is_admin_lookup
|
@@ -243,6 +273,38 @@ describe MessageBus::Rack::Middleware do
|
|
243
273
|
parsed[1]["data"].must_equal "borbs"
|
244
274
|
end
|
245
275
|
|
276
|
+
it "should use the correct client ID" do
|
277
|
+
id = @bus.last_id('/foo')
|
278
|
+
|
279
|
+
client_id = "aBc123"
|
280
|
+
@bus.publish("/foo", "msg1", client_ids: [client_id])
|
281
|
+
@bus.publish("/foo", "msg2", client_ids: ["not_me#{client_id}"])
|
282
|
+
|
283
|
+
post "/message-bus/#{client_id}",
|
284
|
+
'/foo' => id
|
285
|
+
|
286
|
+
parsed = JSON.parse(last_response.body)
|
287
|
+
parsed.length.must_equal 2
|
288
|
+
parsed[0]["data"].must_equal("msg1")
|
289
|
+
parsed[1]["data"].wont_equal("msg2")
|
290
|
+
end
|
291
|
+
|
292
|
+
it "should use the correct client ID with additional path" do
|
293
|
+
id = @bus.last_id('/foo')
|
294
|
+
|
295
|
+
client_id = "aBc123"
|
296
|
+
@bus.publish("/foo", "msg1", client_ids: [client_id])
|
297
|
+
@bus.publish("/foo", "msg2", client_ids: ["not_me#{client_id}"])
|
298
|
+
|
299
|
+
post "/message-bus/#{client_id}/path/not/needed",
|
300
|
+
'/foo' => id
|
301
|
+
|
302
|
+
parsed = JSON.parse(last_response.body)
|
303
|
+
parsed.length.must_equal 2
|
304
|
+
parsed[0]["data"].must_equal("msg1")
|
305
|
+
parsed[1]["data"].wont_equal("msg2")
|
306
|
+
end
|
307
|
+
|
246
308
|
it "should have no cross talk" do
|
247
309
|
seq = 0
|
248
310
|
@bus.site_id_lookup do
|
@@ -37,6 +37,23 @@ describe MessageBus do
|
|
37
37
|
@bus.after_fork
|
38
38
|
end
|
39
39
|
|
40
|
+
describe "#base_route=" do
|
41
|
+
it "adds leading and trailing slashes" do
|
42
|
+
@bus.base_route = "my/base/route"
|
43
|
+
@bus.base_route.must_equal '/my/base/route/'
|
44
|
+
end
|
45
|
+
|
46
|
+
it "leaves existing leading and trailing slashes" do
|
47
|
+
@bus.base_route = "/my/base/route/"
|
48
|
+
@bus.base_route.must_equal '/my/base/route/'
|
49
|
+
end
|
50
|
+
|
51
|
+
it "removes duplicate slashes" do
|
52
|
+
@bus.base_route = "//my///base/route"
|
53
|
+
@bus.base_route.must_equal '/my/base/route/'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
40
57
|
it "can subscribe from a point in time" do
|
41
58
|
@bus.publish("/minion", "banana")
|
42
59
|
|
@@ -292,4 +309,19 @@ describe MessageBus do
|
|
292
309
|
|
293
310
|
data.must_equal(["pre-fork", "from-fork", "continuation"])
|
294
311
|
end
|
312
|
+
|
313
|
+
describe '#register_client_message_filter' do
|
314
|
+
it 'should register the message filter correctly' do
|
315
|
+
@bus.register_client_message_filter('/test')
|
316
|
+
|
317
|
+
@bus.client_message_filters.must_equal([])
|
318
|
+
|
319
|
+
@bus.register_client_message_filter('/test') { puts "hello world" }
|
320
|
+
|
321
|
+
channel, blk = @bus.client_message_filters[0]
|
322
|
+
|
323
|
+
blk.must_respond_to(:call)
|
324
|
+
channel.must_equal('/test')
|
325
|
+
end
|
326
|
+
end
|
295
327
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: message_bus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Saffron
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-06-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -113,6 +113,7 @@ files:
|
|
113
113
|
- lib/message_bus/timer_thread.rb
|
114
114
|
- lib/message_bus/version.rb
|
115
115
|
- message_bus.gemspec
|
116
|
+
- package.json
|
116
117
|
- spec/assets/SpecHelper.js
|
117
118
|
- spec/assets/message-bus.spec.js
|
118
119
|
- spec/assets/support/jasmine.yml
|
@@ -155,7 +156,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
155
156
|
- !ruby/object:Gem::Version
|
156
157
|
version: '0'
|
157
158
|
requirements: []
|
158
|
-
rubygems_version: 3.
|
159
|
+
rubygems_version: 3.1.2
|
159
160
|
signing_key:
|
160
161
|
specification_version: 4
|
161
162
|
summary: ''
|