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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e6d0067ac2cf8b4ee41b4f7ff5f33c1ee6eff75304cd4c5775b753b1f126f100
4
- data.tar.gz: 64c67e20c09fbe73f8e72512a4d9320551d518c962e2297f536a806270351748
3
+ metadata.gz: 50e0141e0d0bdb22ecebc986da5d42a20c6fba2bbf33da60c1899fd46d6c6a08
4
+ data.tar.gz: fe097b32c80fc2b7204a8d08b87cccd9c02cabc63b35c0ecc2222755a677b628
5
5
  SHA512:
6
- metadata.gz: 80d8471eb192dd551615b1c0f8c8278dd3bc9e578dc4ebdf1a0844162a95a32c639eb3047572c692323756085b3bacb5d3c658dd4e1f73c27637e86335fda151
7
- data.tar.gz: 3af18785c7eb3ab110dc0966e7800797fe1b387104311c6736f1ad91621152a4b2d27b628021d7bcbeae880dd39ea4d90b7a8053ebdf986928f31527d2360bf0
6
+ metadata.gz: 9d471fe8d4d190b8adb39a4df7b29ab2d9d99de26818f22baa2e38a8ad3436ce093b4fae3421c5c2bae9891db4f8fcbbbb8828141b6ad4f2f47506abc86b8fae
7
+ data.tar.gz: f2d33c064907929224c17838042392bc9b26cd5b482c176e7594d9d0f7ac42aab5995858a15c658320aa689f187efbc1cf5832b7eaa9d6fe25605898f0221183
@@ -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, nats: {}, **opts)
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::Client.new
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 do not survive multi-closes, so close only once:
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, **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,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.0
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-01-20 00:00:00.000000000 Z
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.0
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.0
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
- - 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
@@ -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