bps-stan 0.2.0 → 0.2.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.
- checksums.yaml +4 -4
- data/lib/bps/publisher/stan.rb +3 -42
- data/lib/bps/stan.rb +62 -2
- data/lib/bps/subscriber/stan.rb +35 -0
- data/spec/bps/{stan_spec.rb → publisher/stan_spec.rb} +6 -3
- data/spec/bps/subscriber/stan_spec.rb +61 -0
- metadata +9 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 50e0141e0d0bdb22ecebc986da5d42a20c6fba2bbf33da60c1899fd46d6c6a08
|
4
|
+
data.tar.gz: fe097b32c80fc2b7204a8d08b87cccd9c02cabc63b35c0ecc2222755a677b628
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9d471fe8d4d190b8adb39a4df7b29ab2d9d99de26818f22baa2e38a8ad3436ce093b4fae3421c5c2bae9891db4f8fcbbbb8828141b6ad4f2f47506abc86b8fae
|
7
|
+
data.tar.gz: f2d33c064907929224c17838042392bc9b26cd5b482c176e7594d9d0f7ac42aab5995858a15c658320aa689f187efbc1cf5832b7eaa9d6fe25605898f0221183
|
data/lib/bps/publisher/stan.rb
CHANGED
@@ -21,53 +21,14 @@ module BPS
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
CLIENT_OPTS = {
|
25
|
-
nats: {
|
26
|
-
servers: [:string],
|
27
|
-
dont_randomize_servers: :bool,
|
28
|
-
reconnect_time_wait: :float,
|
29
|
-
max_reconnect_attempts: :int,
|
30
|
-
connect_timeout: :float,
|
31
|
-
tls_ca_file: :string,
|
32
|
-
# TODO: review, list all of them: https://github.com/nats-io/nats.rb (there's tls config etc)
|
33
|
-
},
|
34
|
-
}.freeze
|
35
|
-
|
36
|
-
def self.parse_url(url)
|
37
|
-
port = url.port&.to_s || '4222'
|
38
|
-
servers = CGI.unescape(url.host).split(',').map do |host|
|
39
|
-
addr = "nats://#{host}"
|
40
|
-
addr << ':' << port unless /:\d+$/.match?(addr)
|
41
|
-
addr
|
42
|
-
end
|
43
|
-
opts = CGI.parse(url.query || '').transform_values {|v| v.size == 1 ? v[0] : v }
|
44
|
-
cluster_id = opts.delete('cluster_id')
|
45
|
-
client_id = opts.delete('client_id')
|
46
|
-
[cluster_id, client_id, { nats: { servers: servers } }]
|
47
|
-
end
|
48
|
-
|
49
|
-
# @return [BPS::Coercer] the options coercer.
|
50
|
-
def self.coercer
|
51
|
-
@coercer ||= BPS::Coercer.new(CLIENT_OPTS).freeze
|
52
|
-
end
|
53
|
-
|
54
24
|
# @param [String] cluster ID.
|
55
25
|
# @param [String] client ID.
|
56
26
|
# @param [Hash] options.
|
57
|
-
def initialize(cluster_id, client_id,
|
27
|
+
def initialize(cluster_id, client_id, **opts)
|
58
28
|
super()
|
59
29
|
|
60
|
-
# handle TLS if CA file is provided:
|
61
|
-
if !nats[:tls] && nats[:tls_ca_file]
|
62
|
-
ctx = OpenSSL::SSL::SSLContext.new
|
63
|
-
ctx.set_params
|
64
|
-
ctx.ca_file = nats.delete(:tls_ca_file)
|
65
|
-
nats[:tls] = ctx
|
66
|
-
end
|
67
|
-
|
68
30
|
@topics = {}
|
69
|
-
@client = ::STAN
|
70
|
-
@client.connect(cluster_id, client_id, nats: nats, **opts.slice(*CLIENT_OPTS.keys))
|
31
|
+
@client = ::BPS::STAN.connect(cluster_id, client_id, **opts)
|
71
32
|
end
|
72
33
|
|
73
34
|
def topic(name)
|
@@ -75,7 +36,7 @@ module BPS
|
|
75
36
|
end
|
76
37
|
|
77
38
|
def close
|
78
|
-
# NATS/STAN
|
39
|
+
# NATS/STAN does not survive multi-closes, so close only once:
|
79
40
|
@client&.close
|
80
41
|
@client = nil
|
81
42
|
end
|
data/lib/bps/stan.rb
CHANGED
@@ -1,12 +1,72 @@
|
|
1
1
|
require 'bps'
|
2
2
|
require 'bps/publisher/stan'
|
3
|
+
require 'bps/subscriber/stan'
|
3
4
|
|
4
5
|
module BPS
|
5
6
|
module Publisher
|
6
7
|
register('stan') do |url, **opts|
|
7
|
-
cluster_id, client_id, url_opts = STAN.parse_url(url)
|
8
|
+
cluster_id, client_id, url_opts = ::BPS::STAN.parse_url(url)
|
8
9
|
url_opts.update(opts)
|
9
|
-
STAN.new(cluster_id, client_id,
|
10
|
+
STAN.new(cluster_id, client_id, **::BPS::STAN.coercer.coerce(url_opts))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module Subscriber
|
15
|
+
register('stan') do |url, **opts|
|
16
|
+
cluster_id, client_id, url_opts = ::BPS::STAN.parse_url(url)
|
17
|
+
url_opts.update(opts)
|
18
|
+
STAN.new(cluster_id, client_id, **::BPS::STAN.coercer.coerce(url_opts))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module STAN
|
23
|
+
CLIENT_OPTS = {
|
24
|
+
nats: {
|
25
|
+
servers: [:string],
|
26
|
+
dont_randomize_servers: :bool,
|
27
|
+
reconnect_time_wait: :float,
|
28
|
+
max_reconnect_attempts: :int,
|
29
|
+
connect_timeout: :float,
|
30
|
+
tls_ca_file: :string,
|
31
|
+
# TODO: review, list all of them: https://github.com/nats-io/nats.rb
|
32
|
+
},
|
33
|
+
}.freeze
|
34
|
+
|
35
|
+
# @param [String] cluster ID
|
36
|
+
# @param [String] client ID
|
37
|
+
# @param [Hash] options
|
38
|
+
# @return [STAN::Client] connected STAN client
|
39
|
+
def self.connect(cluster_id, client_id, nats: {}, **opts)
|
40
|
+
# handle TLS if CA file is provided:
|
41
|
+
if !nats[:tls] && nats[:tls_ca_file]
|
42
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
43
|
+
ctx.set_params
|
44
|
+
ctx.ca_file = nats.delete(:tls_ca_file)
|
45
|
+
nats[:tls] = ctx
|
46
|
+
end
|
47
|
+
|
48
|
+
client = ::STAN::Client.new
|
49
|
+
client.connect(cluster_id, client_id, nats: nats, **opts.slice(*CLIENT_OPTS.keys))
|
50
|
+
client
|
51
|
+
end
|
52
|
+
|
53
|
+
# @return [BPS::Coercer] the options coercer
|
54
|
+
def self.coercer
|
55
|
+
@coercer ||= BPS::Coercer.new(CLIENT_OPTS).freeze
|
56
|
+
end
|
57
|
+
|
58
|
+
# @return [Array] arguments for connecting to STAN
|
59
|
+
def self.parse_url(url)
|
60
|
+
port = url.port&.to_s || '4222'
|
61
|
+
servers = CGI.unescape(url.host).split(',').map do |host|
|
62
|
+
addr = "nats://#{host}"
|
63
|
+
addr << ':' << port unless /:\d+$/.match?(addr)
|
64
|
+
addr
|
65
|
+
end
|
66
|
+
opts = CGI.parse(url.query || '').transform_values {|v| v.size == 1 ? v[0] : v }
|
67
|
+
cluster_id = opts.delete('cluster_id')
|
68
|
+
client_id = opts.delete('client_id')
|
69
|
+
[cluster_id, client_id, { nats: { servers: servers } }]
|
10
70
|
end
|
11
71
|
end
|
12
72
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'stan/client'
|
3
|
+
|
4
|
+
module BPS
|
5
|
+
module Subscriber
|
6
|
+
class STAN < Abstract
|
7
|
+
# @param [String] cluster ID.
|
8
|
+
# @param [String] client ID.
|
9
|
+
# @param [Hash] options.
|
10
|
+
def initialize(cluster_id, client_id, **opts)
|
11
|
+
super()
|
12
|
+
|
13
|
+
@client = ::BPS::STAN.connect(cluster_id, client_id, **opts)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Subscribe to a topic
|
17
|
+
# @param topic [String] topic the topic name.
|
18
|
+
def subscribe(topic, **opts)
|
19
|
+
# important opts:
|
20
|
+
# - queue: 'queue-name' # https://docs.nats.io/developing-with-nats-streaming/queues
|
21
|
+
# - durable_name: 'durable-name' # https://docs.nats.io/developing-with-nats-streaming/durables
|
22
|
+
@client.subscribe(topic, **opts) do |msg|
|
23
|
+
yield msg.data # TODO: maybe yielding just bytes is not too flexible? But IMO can wait till (much) later
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Close the subscriber.
|
28
|
+
def close
|
29
|
+
# NATS/STAN does not survive multi-closes, so close only once:
|
30
|
+
@client&.close
|
31
|
+
@client = nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -12,7 +12,8 @@ RSpec.describe 'STAN', stan: true do
|
|
12
12
|
.to receive(:new)
|
13
13
|
.with('CLUSTER', 'CLIENT', nats: { servers: ['nats://test.host:4222'] })
|
14
14
|
.and_return(publisher)
|
15
|
-
BPS::Publisher.resolve(URI.parse('stan://test.host:4222?cluster_id=CLUSTER&client_id=CLIENT'))
|
15
|
+
expect(BPS::Publisher.resolve(URI.parse('stan://test.host:4222?cluster_id=CLUSTER&client_id=CLIENT')))
|
16
|
+
.to eq(publisher)
|
16
17
|
end
|
17
18
|
|
18
19
|
it 'resolves URLs with multiple hosts' do
|
@@ -20,7 +21,8 @@ RSpec.describe 'STAN', stan: true do
|
|
20
21
|
.to receive(:new)
|
21
22
|
.with('CLUSTER', 'CLIENT', nats: { servers: ['nats://foo.host:4222', 'nats://bar.host:4222'] })
|
22
23
|
.and_return(publisher)
|
23
|
-
BPS::Publisher.resolve(URI.parse('stan://foo.host,bar.host:4222?cluster_id=CLUSTER&client_id=CLIENT'))
|
24
|
+
expect(BPS::Publisher.resolve(URI.parse('stan://foo.host,bar.host:4222?cluster_id=CLUSTER&client_id=CLIENT')))
|
25
|
+
.to eq(publisher)
|
24
26
|
end
|
25
27
|
|
26
28
|
it 'resolves URLs with multiple hosts/ports' do
|
@@ -28,7 +30,8 @@ RSpec.describe 'STAN', stan: true do
|
|
28
30
|
.to receive(:new)
|
29
31
|
.with('CLUSTER', 'CLIENT', nats: { servers: ['nats://foo.host:4223', 'nats://bar.host:4222'] })
|
30
32
|
.and_return(publisher)
|
31
|
-
BPS::Publisher.resolve(URI.parse('stan://foo.host%3A4223,bar.host?cluster_id=CLUSTER&client_id=CLIENT'))
|
33
|
+
expect(BPS::Publisher.resolve(URI.parse('stan://foo.host%3A4223,bar.host?cluster_id=CLUSTER&client_id=CLIENT')))
|
34
|
+
.to eq(publisher)
|
32
35
|
end
|
33
36
|
end
|
34
37
|
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'bps/stan'
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
RSpec.describe 'STAN', stan: true do
|
5
|
+
context 'with addr resolving' do
|
6
|
+
let(:subscriber) { instance_double('BPS::Subscriber::STAN') }
|
7
|
+
|
8
|
+
before { allow(BPS::Subscriber::STAN).to receive(:new).and_return(subscriber) }
|
9
|
+
|
10
|
+
it 'resolves simple URLs' do
|
11
|
+
allow(BPS::Subscriber::STAN)
|
12
|
+
.to receive(:new)
|
13
|
+
.with('CLUSTER', 'CLIENT', nats: { servers: ['nats://test.host:4222'] })
|
14
|
+
.and_return(subscriber)
|
15
|
+
expect(BPS::Subscriber.resolve(URI.parse('stan://test.host:4222?cluster_id=CLUSTER&client_id=CLIENT')))
|
16
|
+
.to eq(subscriber)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'resolves URLs with multiple hosts' do
|
20
|
+
allow(BPS::Subscriber::STAN)
|
21
|
+
.to receive(:new)
|
22
|
+
.with('CLUSTER', 'CLIENT', nats: { servers: ['nats://foo.host:4222', 'nats://bar.host:4222'] })
|
23
|
+
.and_return(subscriber)
|
24
|
+
expect(BPS::Subscriber.resolve(URI.parse('stan://foo.host,bar.host:4222?cluster_id=CLUSTER&client_id=CLIENT')))
|
25
|
+
.to eq(subscriber)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'resolves URLs with multiple hosts/ports' do
|
29
|
+
allow(BPS::Subscriber::STAN)
|
30
|
+
.to receive(:new)
|
31
|
+
.with('CLUSTER', 'CLIENT', nats: { servers: ['nats://foo.host:4223', 'nats://bar.host:4222'] })
|
32
|
+
.and_return(subscriber)
|
33
|
+
expect(BPS::Subscriber.resolve(URI.parse('stan://foo.host%3A4223,bar.host?cluster_id=CLUSTER&client_id=CLIENT')))
|
34
|
+
.to eq(subscriber)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context BPS::Subscriber::STAN do
|
39
|
+
let(:cluster_id) { 'test-cluster' } # this is a default cluster for https://hub.docker.com/_/nats-streaming
|
40
|
+
let(:client_id) { 'bps-test' }
|
41
|
+
|
42
|
+
let(:nats_servers) { ENV.fetch('STAN_SERVERS', '127.0.0.1:4222').split(',') }
|
43
|
+
let(:nats_servers_with_scheme) { nats_servers.map {|s| "nats://#{s}" } }
|
44
|
+
|
45
|
+
let(:subscriber_url) { "stan://#{CGI.escape(nats_servers.join(','))}/?cluster_id=#{cluster_id}&client_id=#{client_id}" }
|
46
|
+
|
47
|
+
def produce_messages(topic_name, messages)
|
48
|
+
opts = {
|
49
|
+
servers: nats_servers_with_scheme,
|
50
|
+
dont_randomize_servers: true,
|
51
|
+
}
|
52
|
+
::STAN::Client.new.connect(cluster_id, client_id, nats: opts) do |client|
|
53
|
+
messages.each do |message|
|
54
|
+
client.publish(topic_name, message)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
it_behaves_like 'subscriber'
|
60
|
+
end
|
61
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bps-stan
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Black Square Media
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-03-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bps
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.2.
|
19
|
+
version: 0.2.1
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.2.
|
26
|
+
version: 0.2.1
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: nats-streaming
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -48,7 +48,9 @@ files:
|
|
48
48
|
- lib/bps-stan.rb
|
49
49
|
- lib/bps/publisher/stan.rb
|
50
50
|
- lib/bps/stan.rb
|
51
|
-
-
|
51
|
+
- lib/bps/subscriber/stan.rb
|
52
|
+
- spec/bps/publisher/stan_spec.rb
|
53
|
+
- spec/bps/subscriber/stan_spec.rb
|
52
54
|
homepage: https://github.com/bsm/bps
|
53
55
|
licenses:
|
54
56
|
- Apache-2.0
|
@@ -73,4 +75,5 @@ signing_key:
|
|
73
75
|
specification_version: 4
|
74
76
|
summary: BPS adapter for nats-streaming
|
75
77
|
test_files:
|
76
|
-
- spec/bps/stan_spec.rb
|
78
|
+
- spec/bps/publisher/stan_spec.rb
|
79
|
+
- spec/bps/subscriber/stan_spec.rb
|