bps-stan 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5d0143920a7d80cb269a5fd2d8f15ac90becad86b7177a808b6239a0bb77b0a9
4
+ data.tar.gz: 23eda94ef4b9e1baaa89a806f435c32f4a087bf7b243bd004484a9e97734377f
5
+ SHA512:
6
+ metadata.gz: 7c9d5fab51969ffe5d0c7b5799af272301d603810a7a37ec6593dbe71dc997ac9f3643dc3b2637f329857a616434f59f55733c215552b14f4b099fbe44fd6b67
7
+ data.tar.gz: 17448d25241e07c5608f3d4a80a9d8f5b5620059a84f02f4770a82d86124c8111dc4bfd0c2b634349ef2531b48e9f0570e50ca174bb7af57481be1867d1c8866
@@ -0,0 +1,22 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'bps-stan'
3
+ s.version = File.read(File.expand_path('../../.version', __dir__)).strip
4
+ s.platform = Gem::Platform::RUBY
5
+
6
+ s.licenses = ['Apache-2.0']
7
+ s.summary = 'BPS adapter for nats-streaming'
8
+ s.description = 'https://github.com/bsm/bps'
9
+
10
+ s.authors = ['Black Square Media']
11
+ s.email = 'info@blacksquaremedia.com'
12
+ s.homepage = 'https://github.com/bsm/bps'
13
+
14
+ s.executables = []
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- spec/*`.split("\n")
17
+ s.require_paths = ['lib']
18
+ s.required_ruby_version = '>= 2.6.0'
19
+
20
+ s.add_dependency 'bps', s.version
21
+ s.add_dependency 'nats-streaming', '= 0.2.2'
22
+ end
@@ -0,0 +1 @@
1
+ require 'bps/stan'
@@ -0,0 +1,75 @@
1
+ require 'cgi'
2
+ require 'stan/client'
3
+
4
+ module BPS
5
+ module Publisher
6
+ class STAN < Abstract
7
+ class Topic < Abstract::Topic
8
+ def initialize(client, topic)
9
+ super()
10
+
11
+ @client = client
12
+ @topic = topic
13
+ end
14
+
15
+ def publish(message, **_opts)
16
+ @client.publish(@topic, message)
17
+ end
18
+
19
+ def flush(**)
20
+ # noop
21
+ end
22
+ end
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
+ # @param [String] cluster ID.
54
+ # @param [String] client ID.
55
+ # @param [Hash] options.
56
+ def initialize(cluster_id, client_id, nats: {}, **opts)
57
+ super()
58
+
59
+ @topics = {}
60
+ @client = ::STAN::Client.new
61
+ @client.connect(cluster_id, client_id, nats: nats, **opts.slice(*CLIENT_OPTS.keys))
62
+ end
63
+
64
+ def topic(name)
65
+ @topics[name] ||= self.class::Topic.new(@client, name)
66
+ end
67
+
68
+ def close
69
+ # NATS/STAN do not survive multi-closes, so close only once:
70
+ @client&.close
71
+ @client = nil
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,12 @@
1
+ require 'bps'
2
+ require 'bps/publisher/stan'
3
+
4
+ module BPS
5
+ module Publisher
6
+ register('stan') do |url, **opts|
7
+ cluster_id, client_id, url_opts = STAN.parse_url(url)
8
+ url_opts.update(opts)
9
+ STAN.new(cluster_id, client_id, **STAN.coercer.coerce(url_opts))
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,60 @@
1
+ require 'bps/stan'
2
+ require 'spec_helper'
3
+
4
+ RSpec.describe 'STAN', stan: true do
5
+ context 'resolve addrs' do
6
+ let(:publisher) { double('BPS::Publisher::STAN') }
7
+ before { allow(BPS::Publisher::STAN).to receive(:new).and_return(publisher) }
8
+
9
+ it 'should resolve simple URLs' do
10
+ expect(BPS::Publisher::STAN)
11
+ .to receive(:new)
12
+ .with('CLUSTER', 'CLIENT', nats: { servers: ['nats://test.host:4222'] })
13
+ .and_return(publisher)
14
+ BPS::Publisher.resolve(URI.parse('stan://test.host:4222?cluster_id=CLUSTER&client_id=CLIENT'))
15
+ end
16
+
17
+ it 'should resolve URLs with multiple hosts' do
18
+ expect(BPS::Publisher::STAN)
19
+ .to receive(:new)
20
+ .with('CLUSTER', 'CLIENT', nats: { servers: ['nats://foo.host:4222', 'nats://bar.host:4222'] })
21
+ .and_return(publisher)
22
+ BPS::Publisher.resolve(URI.parse('stan://foo.host,bar.host:4222?cluster_id=CLUSTER&client_id=CLIENT'))
23
+ end
24
+
25
+ it 'should resolve URLs with multiple hosts/ports' do
26
+ expect(BPS::Publisher::STAN)
27
+ .to receive(:new)
28
+ .with('CLUSTER', 'CLIENT', nats: { servers: ['nats://foo.host:4223', 'nats://bar.host:4222'] })
29
+ .and_return(publisher)
30
+ BPS::Publisher.resolve(URI.parse('stan://foo.host%3A4223,bar.host?cluster_id=CLUSTER&client_id=CLIENT'))
31
+ end
32
+ end
33
+
34
+ context BPS::Publisher::STAN do
35
+ let(:cluster_id) { 'test-cluster' } # this is a default cluster for https://hub.docker.com/_/nats-streaming
36
+ let(:client_id) { 'bps-test' }
37
+
38
+ let(:nats_servers) { ENV.fetch('NATS_SERVERS', '127.0.0.1:4222').split(',') }
39
+ let(:nats_servers_with_scheme) { nats_servers.map {|s| "nats://#{s}" } }
40
+
41
+ let(:publisher_url) { "stan://#{CGI.escape(nats_servers.join(','))}/?cluster_id=#{cluster_id}&client_id=#{client_id}" }
42
+
43
+ def read_messages(topic_name, num_messages)
44
+ [].tap do |messages|
45
+ opts = {
46
+ servers: nats_servers_with_scheme,
47
+ dont_randomize_servers: true,
48
+ }
49
+ ::STAN::Client.new.connect(cluster_id, client_id, nats: opts) do |client|
50
+ client.subscribe(topic_name, start_at: :first) do |msg|
51
+ messages << msg.data
52
+ next if messages.size == num_messages
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ it_behaves_like 'publisher'
59
+ end
60
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bps-stan
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Black Square Media
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-11-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bps
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 0.1.2
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 0.1.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: nats-streaming
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 0.2.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 0.2.2
41
+ description: https://github.com/bsm/bps
42
+ email: info@blacksquaremedia.com
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - bps-stan.gemspec
48
+ - lib/bps-stan.rb
49
+ - lib/bps/publisher/stan.rb
50
+ - lib/bps/stan.rb
51
+ - spec/bps/stan_spec.rb
52
+ homepage: https://github.com/bsm/bps
53
+ licenses:
54
+ - Apache-2.0
55
+ metadata: {}
56
+ post_install_message:
57
+ rdoc_options: []
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: 2.6.0
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ requirements: []
71
+ rubygems_version: 3.1.2
72
+ signing_key:
73
+ specification_version: 4
74
+ summary: BPS adapter for nats-streaming
75
+ test_files:
76
+ - spec/bps/stan_spec.rb