bps-stan 0.1.2 → 0.2.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5d0143920a7d80cb269a5fd2d8f15ac90becad86b7177a808b6239a0bb77b0a9
4
- data.tar.gz: 23eda94ef4b9e1baaa89a806f435c32f4a087bf7b243bd004484a9e97734377f
3
+ metadata.gz: 7108a7bf17771ebd494e2fc1cd438aff207e49b2492a3bfa43c8e55de3ec61ca
4
+ data.tar.gz: 4876378fbb7efa31e15ee93f633c4596919fde413ce1c4fa261b014a76589486
5
5
  SHA512:
6
- metadata.gz: 7c9d5fab51969ffe5d0c7b5799af272301d603810a7a37ec6593dbe71dc997ac9f3643dc3b2637f329857a616434f59f55733c215552b14f4b099fbe44fd6b67
7
- data.tar.gz: 17448d25241e07c5608f3d4a80a9d8f5b5620059a84f02f4770a82d86124c8111dc4bfd0c2b634349ef2531b48e9f0570e50ca174bb7af57481be1867d1c8866
6
+ metadata.gz: ab1e0361768cfceae98e1709a9065fc33d82f21bc529fa6ef010d6964c7382249e8a3acc1a34a53c33c7bc97f3a10d17198009571e29104461f1d644b7049e48
7
+ data.tar.gz: 85cda889a293c0c48050139b2888cbd238e2924c1630c9484a83ee870bedc5d03ec7a2d5ae71f7c53ed28d60febb46cce28423aabcb1734db4e218b76b29bfb8
data/bps-stan.gemspec CHANGED
@@ -18,5 +18,5 @@ Gem::Specification.new do |s|
18
18
  s.required_ruby_version = '>= 2.6.0'
19
19
 
20
20
  s.add_dependency 'bps', s.version
21
- s.add_dependency 'nats-streaming', '= 0.2.2'
21
+ s.add_dependency 'nats-streaming', '~> 0.2.2'
22
22
  end
@@ -21,44 +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
- # TODO: review, list all of them: https://github.com/nats-io/nats.rb (there's tls config etc)
32
- },
33
- }.freeze
34
-
35
- def self.parse_url(url)
36
- port = url.port&.to_s || '4222'
37
- servers = CGI.unescape(url.host).split(',').map do |host|
38
- addr = "nats://#{host}"
39
- addr << ':' << port unless addr.match(/:\d+$/)
40
- addr
41
- end
42
- opts = CGI.parse(url.query).transform_values {|v| v.size == 1 ? v[0] : v }
43
- cluster_id = opts.delete('cluster_id')
44
- client_id = opts.delete('client_id')
45
- [cluster_id, client_id, { nats: { servers: servers } }]
46
- end
47
-
48
- # @return [BPS::Coercer] the options coercer.
49
- def self.coercer
50
- @coercer ||= BPS::Coercer.new(CLIENT_OPTS).freeze
51
- end
52
-
53
24
  # @param [String] cluster ID.
54
25
  # @param [String] client ID.
55
26
  # @param [Hash] options.
56
- def initialize(cluster_id, client_id, nats: {}, **opts)
27
+ def initialize(cluster_id, client_id, **opts)
57
28
  super()
58
29
 
59
30
  @topics = {}
60
- @client = ::STAN::Client.new
61
- @client.connect(cluster_id, client_id, nats: nats, **opts.slice(*CLIENT_OPTS.keys))
31
+ @client = ::BPS::STAN.connect(cluster_id, client_id, **opts)
62
32
  end
63
33
 
64
34
  def topic(name)
@@ -66,7 +36,9 @@ module BPS
66
36
  end
67
37
 
68
38
  def close
69
- # NATS/STAN do not survive multi-closes, so close only once:
39
+ super
40
+
41
+ # NATS/STAN does not survive multi-closes, so close only once:
70
42
  @client&.close
71
43
  @client = nil
72
44
  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, **STAN.coercer.coerce(url_opts))
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,37 @@
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
+ super
30
+
31
+ # NATS/STAN does not survive multi-closes, so close only once:
32
+ @client&.close
33
+ @client = nil
34
+ end
35
+ end
36
+ end
37
+ end
@@ -2,32 +2,36 @@ require 'bps/stan'
2
2
  require 'spec_helper'
3
3
 
4
4
  RSpec.describe 'STAN', stan: true do
5
- context 'resolve addrs' do
6
- let(:publisher) { double('BPS::Publisher::STAN') }
5
+ context 'with addr resolving' do
6
+ let(:publisher) { instance_double('BPS::Publisher::STAN') }
7
+
7
8
  before { allow(BPS::Publisher::STAN).to receive(:new).and_return(publisher) }
8
9
 
9
- it 'should resolve simple URLs' do
10
- expect(BPS::Publisher::STAN)
10
+ it 'resolves simple URLs' do
11
+ allow(BPS::Publisher::STAN)
11
12
  .to receive(:new)
12
13
  .with('CLUSTER', 'CLIENT', nats: { servers: ['nats://test.host:4222'] })
13
14
  .and_return(publisher)
14
- 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)
15
17
  end
16
18
 
17
- it 'should resolve URLs with multiple hosts' do
18
- expect(BPS::Publisher::STAN)
19
+ it 'resolves URLs with multiple hosts' do
20
+ allow(BPS::Publisher::STAN)
19
21
  .to receive(:new)
20
22
  .with('CLUSTER', 'CLIENT', nats: { servers: ['nats://foo.host:4222', 'nats://bar.host:4222'] })
21
23
  .and_return(publisher)
22
- 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)
23
26
  end
24
27
 
25
- it 'should resolve URLs with multiple hosts/ports' do
26
- expect(BPS::Publisher::STAN)
28
+ it 'resolves URLs with multiple hosts/ports' do
29
+ allow(BPS::Publisher::STAN)
27
30
  .to receive(:new)
28
31
  .with('CLUSTER', 'CLIENT', nats: { servers: ['nats://foo.host:4223', 'nats://bar.host:4222'] })
29
32
  .and_return(publisher)
30
- 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)
31
35
  end
32
36
  end
33
37
 
@@ -35,7 +39,7 @@ RSpec.describe 'STAN', stan: true do
35
39
  let(:cluster_id) { 'test-cluster' } # this is a default cluster for https://hub.docker.com/_/nats-streaming
36
40
  let(:client_id) { 'bps-test' }
37
41
 
38
- let(:nats_servers) { ENV.fetch('NATS_SERVERS', '127.0.0.1:4222').split(',') }
42
+ let(:nats_servers) { ENV.fetch('STAN_ADDRS', '127.0.0.1:4222').split(',') }
39
43
  let(:nats_servers_with_scheme) { nats_servers.map {|s| "nats://#{s}" } }
40
44
 
41
45
  let(:publisher_url) { "stan://#{CGI.escape(nats_servers.join(','))}/?cluster_id=#{cluster_id}&client_id=#{client_id}" }
@@ -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_ADDRS', '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.1.2
4
+ version: 0.2.2
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: 2020-11-09 00:00:00.000000000 Z
11
+ date: 2021-09-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bps
@@ -16,26 +16,26 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 0.1.2
19
+ version: 0.2.2
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.1.2
26
+ version: 0.2.2
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: nats-streaming
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '='
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: 0.2.2
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '='
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: 0.2.2
41
41
  description: https://github.com/bsm/bps
@@ -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
- - spec/bps/stan_spec.rb
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
@@ -68,9 +70,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
68
70
  - !ruby/object:Gem::Version
69
71
  version: '0'
70
72
  requirements: []
71
- rubygems_version: 3.1.2
73
+ rubygems_version: 3.2.15
72
74
  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