bps-stan 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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