splitclient-rb 7.0.4.pre.rc3-java → 7.1.0-java

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.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +10 -3
  3. data/CHANGES.txt +3 -0
  4. data/lib/splitclient-rb/cache/fetchers/segment_fetcher.rb +17 -6
  5. data/lib/splitclient-rb/cache/fetchers/split_fetcher.rb +21 -11
  6. data/lib/splitclient-rb/cache/repositories/splits_repository.rb +1 -1
  7. data/lib/splitclient-rb/clients/split_client.rb +4 -7
  8. data/lib/splitclient-rb/constants.rb +10 -0
  9. data/lib/splitclient-rb/engine/api/splits.rb +0 -1
  10. data/lib/splitclient-rb/engine/auth_api_client.rb +78 -0
  11. data/lib/splitclient-rb/engine/push_manager.rb +53 -0
  12. data/lib/splitclient-rb/engine/sync_manager.rb +123 -0
  13. data/lib/splitclient-rb/engine/synchronizer.rb +90 -0
  14. data/lib/splitclient-rb/exceptions.rb +8 -0
  15. data/lib/splitclient-rb/helpers/thread_helper.rb +25 -0
  16. data/lib/splitclient-rb/split_config.rb +42 -4
  17. data/lib/splitclient-rb/split_factory.rb +25 -15
  18. data/lib/splitclient-rb/sse/event_source/back_off.rb +25 -0
  19. data/lib/splitclient-rb/sse/event_source/client.rb +85 -60
  20. data/lib/splitclient-rb/sse/event_source/stream_data.rb +22 -0
  21. data/lib/splitclient-rb/sse/notification_manager_keeper.rb +71 -0
  22. data/lib/splitclient-rb/sse/notification_processor.rb +50 -0
  23. data/lib/splitclient-rb/sse/sse_handler.rb +47 -52
  24. data/lib/splitclient-rb/sse/workers/segments_worker.rb +21 -6
  25. data/lib/splitclient-rb/sse/workers/splits_worker.rb +24 -6
  26. data/lib/splitclient-rb/version.rb +1 -1
  27. data/lib/splitclient-rb.rb +10 -3
  28. data/splitclient-rb.gemspec +1 -0
  29. metadata +28 -7
  30. data/lib/splitclient-rb/engine/parser/split_adapter.rb +0 -105
  31. data/lib/splitclient-rb/sse/event_source/status.rb +0 -13
  32. data/lib/splitclient-rb/sse/workers/control_worker.rb +0 -34
@@ -102,7 +102,11 @@ module SplitIoClient
102
102
  @split_validator = SplitIoClient::Validators.new(self)
103
103
  @localhost_mode = opts[:localhost_mode]
104
104
 
105
- @push_notification_enabled = opts[:push_notification_enabled].nil? ? true : false
105
+ @streaming_enabled = consumer? ? false : (opts[:streaming_enabled].nil? ? SplitConfig.default_streaming_enabled : opts[:streaming_enabled])
106
+ @streaming_service_url = opts[:streaming_service_url] || SplitConfig.default_streaming_service_url
107
+ @auth_service_url = opts[:auth_service_url] || SplitConfig.default_auth_service_url
108
+ @auth_retry_back_off_base = SplitConfig.init_auth_retry_back_off(opts[:auth_retry_back_off_base] || SplitConfig.default_auth_retry_back_off_base)
109
+ @streaming_reconnect_back_off_base = SplitConfig.init_streaming_reconnect_back_off(opts[:streaming_reconnect_back_off_base] || SplitConfig.default_streaming_reconnect_back_off_base)
106
110
 
107
111
  startup_log
108
112
  end
@@ -256,7 +260,43 @@ module SplitIoClient
256
260
 
257
261
  attr_accessor :ip_addresses_enabled
258
262
 
259
- attr_accessor :push_notification_enabled
263
+ attr_accessor :auth_service_url
264
+
265
+ attr_accessor :auth_retry_back_off_base
266
+
267
+ attr_accessor :streaming_service_url
268
+
269
+ attr_accessor :streaming_reconnect_back_off_base
270
+
271
+ attr_accessor :streaming_enabled
272
+
273
+ def self.default_streaming_enabled
274
+ false
275
+ end
276
+
277
+ def self.default_streaming_service_url
278
+ 'https://split-realtime.ably.io/event-stream'
279
+ end
280
+
281
+ def self.default_auth_service_url
282
+ 'https://auth.split.io/api/auth'
283
+ end
284
+
285
+ def self.default_auth_retry_back_off_base
286
+ 1
287
+ end
288
+
289
+ def self.default_streaming_reconnect_back_off_base
290
+ 1
291
+ end
292
+
293
+ def self.init_auth_retry_back_off(auth_retry_back_off)
294
+ auth_retry_back_off < 1 ? SplitConfig.default_auth_retry_back_off_base : auth_retry_back_off
295
+ end
296
+
297
+ def self.init_streaming_reconnect_back_off(streaming_reconnect_back_off)
298
+ streaming_reconnect_back_off < 1 ? SplitConfig.default_streaming_reconnect_back_off_base : streaming_reconnect_back_off
299
+ end
260
300
 
261
301
  #
262
302
  # The default split client configuration
@@ -385,8 +425,6 @@ module SplitIoClient
385
425
  end
386
426
  end
387
427
 
388
-
389
-
390
428
  #
391
429
  # The default debug value
392
430
  #
@@ -6,6 +6,7 @@ module SplitIoClient
6
6
 
7
7
  include SplitIoClient::Cache::Repositories
8
8
  include SplitIoClient::Cache::Stores
9
+ include SplitIoClient::Cache::Senders
9
10
 
10
11
  attr_reader :adapter, :client, :manager, :config
11
12
 
@@ -29,16 +30,14 @@ module SplitIoClient
29
30
  @splits_repository = SplitsRepository.new(@config)
30
31
  @segments_repository = SegmentsRepository.new(@config)
31
32
  @impressions_repository = ImpressionsRepository.new(@config)
32
- @metrics_repository = MetricsRepository.new(@config)
33
33
  @events_repository = EventsRepository.new(@config, @api_key)
34
-
34
+ @metrics_repository = MetricsRepository.new(@config)
35
35
  @sdk_blocker = SDKBlocker.new(@splits_repository, @segments_repository, @config)
36
+ @metrics = Metrics.new(100, @metrics_repository)
36
37
 
37
- @adapter = start!
38
+ start!
38
39
 
39
- start_sse!
40
-
41
- @client = SplitClient.new(@api_key, @adapter, @splits_repository, @segments_repository, @impressions_repository, @metrics_repository, @events_repository, @sdk_blocker, @config, @sse_handler)
40
+ @client = SplitClient.new(@api_key, @metrics, @splits_repository, @segments_repository, @impressions_repository, @metrics_repository, @events_repository, @sdk_blocker, @config)
42
41
  @manager = SplitManager.new(@splits_repository, @sdk_blocker, @config)
43
42
 
44
43
  validate_api_key
@@ -49,7 +48,11 @@ module SplitIoClient
49
48
  end
50
49
 
51
50
  def start!
52
- SplitAdapter.new(@api_key, @splits_repository, @segments_repository, @impressions_repository, @metrics_repository, @events_repository, @sdk_blocker, @config)
51
+ if @config.localhost_mode
52
+ start_localhost_components
53
+ else
54
+ SplitIoClient::Engine::SyncManager.new(repositories, @api_key, @config, @sdk_blocker, @metrics).start
55
+ end
53
56
  end
54
57
 
55
58
  def stop!
@@ -118,15 +121,22 @@ module SplitIoClient
118
121
  end
119
122
  end
120
123
 
121
- def start_sse!
122
- if @config.push_notification_enabled
123
- @splits_worker = SSE::Workers::SplitsWorker.new(@adapter, @config, @splits_repository)
124
- @segments_worker = SSE::Workers::SegmentsWorker.new(@adapter, @config, @segments_repository)
125
- @control_worker = SSE::Workers::ControlWorker.new(@adapter, @config)
124
+ def repositories
125
+ repos = {}
126
+ repos[:splits] = @splits_repository
127
+ repos[:segments] = @segments_repository
128
+ repos[:impressions] = @impressions_repository
129
+ repos[:events] = @events_repository
130
+ repos[:metrics] = @metrics_repository
126
131
 
127
- options = { channels: 'mauro-c', key: 'fake_key', url_host: 'fake-url' }
128
- @sse_handler = SSE::SSEHandler.new(@config, options, @splits_worker, @segments_worker, @control_worker)
129
- end
132
+ repos
133
+ end
134
+
135
+ def start_localhost_components
136
+ LocalhostSplitStore.new(@splits_repository, @config, @sdk_blocker).call
137
+
138
+ # Starts thread which loops constantly and cleans up repositories to avoid memory issues in localhost mode
139
+ LocalhostRepoCleaner.new(@impressions_repository, @metrics_repository, @events_repository, @config).call
130
140
  end
131
141
  end
132
142
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: false
2
+
3
+ module SplitIoClient
4
+ module SSE
5
+ module EventSource
6
+ class BackOff
7
+ def initialize(back_off_base, attempt = 0)
8
+ @attempt = attempt
9
+ @back_off_base = back_off_base
10
+ end
11
+
12
+ def interval
13
+ interval = (@back_off_base * (2**@attempt)) if @attempt.positive?
14
+ @attempt += 1
15
+
16
+ interval || 0
17
+ end
18
+
19
+ def reset
20
+ @attempt = 0
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -8,107 +8,117 @@ module SplitIoClient
8
8
  module SSE
9
9
  module EventSource
10
10
  class Client
11
- DEFAULT_READ_TIMEOUT = 200
11
+ DEFAULT_READ_TIMEOUT = 70
12
12
  KEEP_ALIVE_RESPONSE = "c\r\n:keepalive\n\n\r\n".freeze
13
13
 
14
- def initialize(url, config, read_timeout: DEFAULT_READ_TIMEOUT)
15
- @uri = URI(url)
14
+ def initialize(config, read_timeout: DEFAULT_READ_TIMEOUT)
16
15
  @config = config
17
16
  @read_timeout = read_timeout
18
17
  @connected = Concurrent::AtomicBoolean.new(false)
19
18
  @socket = nil
19
+ @back_off = BackOff.new(@config.streaming_reconnect_back_off_base)
20
20
 
21
- @on = { event: ->(_) {}, error: ->(_) {} }
21
+ @on = { event: ->(_) {}, connected: ->(_) {}, disconnect: ->(_) {} }
22
22
 
23
23
  yield self if block_given?
24
-
25
- connect_thread
26
-
27
- connect_passenger_forked if defined?(PhusionPassenger)
28
24
  end
29
25
 
30
26
  def on_event(&action)
31
27
  @on[:event] = action
32
28
  end
33
29
 
34
- def on_error(&action)
35
- @on[:error] = action
30
+ def on_connected(&action)
31
+ @on[:connected] = action
32
+ end
33
+
34
+ def on_disconnect(&action)
35
+ @on[:disconnect] = action
36
36
  end
37
37
 
38
38
  def close
39
+ dispatch_disconnect
39
40
  @connected.make_false
41
+ SplitIoClient::Helpers::ThreadHelper.stop(:connect_stream, @config)
40
42
  @socket&.close
41
- @socket = nil
43
+ rescue StandardError => e
44
+ @config.logger.error("SSEClient close Error: #{e.inspect}")
42
45
  end
43
46
 
44
- def status
45
- return Status::CONNECTED if @connected.value
47
+ def start(url)
48
+ @uri = URI(url)
46
49
 
47
- Status::DISCONNECTED
50
+ connect_thread
51
+ rescue StandardError => e
52
+ @config.logger.error("SSEClient start Error: #{e.inspect}")
53
+ end
54
+
55
+ def connected?
56
+ @connected.value
48
57
  end
49
58
 
50
59
  private
51
60
 
52
61
  def connect_thread
53
62
  @config.threads[:connect_stream] = Thread.new do
63
+ @config.logger.info('Starting connect_stream thread ...') if @config.debug_enabled
54
64
  connect_stream
55
65
  end
56
66
  end
57
67
 
58
- def connect_passenger_forked
59
- PhusionPassenger.on_event(:starting_worker_process) { |forked| connect_thread if forked }
60
- end
61
-
62
68
  def connect_stream
63
- @config.logger.info("Connecting to #{@uri.host}...")
69
+ interval = @back_off.interval
70
+ sleep(interval) if interval.positive?
64
71
 
65
- begin
66
- @socket = socket_connect
67
-
68
- @socket.write(build_request(@uri))
69
- @connected.make_true
70
- rescue StandardError => e
71
- dispatch_error(e.inspect)
72
- end
72
+ socket_write
73
73
 
74
74
  while @connected.value
75
75
  begin
76
- partial_data = @socket.readpartial(2048, timeout: @read_timeout)
77
- rescue Socketry::TimeoutError
78
- @config.logger.error("Socket read time out in #{@read_timeout}")
76
+ partial_data = @socket.readpartial(10_000, timeout: @read_timeout)
77
+
78
+ raise 'eof exception' if partial_data == :eof
79
+ rescue StandardError => e
80
+ @config.logger.error(e.inspect) if @config.debug_enabled
79
81
  @connected.make_false
82
+ @socket&.close
83
+ @socket = nil
80
84
  connect_stream
81
85
  end
82
86
 
83
- proccess_data(partial_data) unless partial_data == KEEP_ALIVE_RESPONSE
87
+ process_data(partial_data)
84
88
  end
85
89
  end
86
90
 
91
+ def socket_write
92
+ @socket = socket_connect
93
+ @socket.write(build_request(@uri))
94
+ dispatch_connected
95
+ rescue StandardError => e
96
+ @config.logger.error("Error during connecting to #{@uri.host}. Error: #{e.inspect}")
97
+ close
98
+ end
99
+
87
100
  def socket_connect
88
101
  return Socketry::SSL::Socket.connect(@uri.host, @uri.port) if @uri.scheme.casecmp('https').zero?
89
102
 
90
103
  Socketry::TCP::Socket.connect(@uri.host, @uri.port)
91
104
  end
92
105
 
93
- def proccess_data(partial_data)
94
- unless partial_data.nil?
95
- @config.logger.debug("Event partial data: #{partial_data}")
106
+ def process_data(partial_data)
107
+ unless partial_data.nil? || partial_data == KEEP_ALIVE_RESPONSE
108
+ @config.logger.debug("Event partial data: #{partial_data}") if @config.debug_enabled
96
109
  buffer = read_partial_data(partial_data)
97
- event = parse_event(buffer)
98
-
99
- dispatch_event(event)
110
+ parse_event(buffer)
100
111
  end
101
112
  rescue StandardError => e
102
- dispatch_error(e.inspect)
113
+ @config.logger.error("process_data error: #{e.inspect}")
103
114
  end
104
115
 
105
116
  def build_request(uri)
106
117
  req = "GET #{uri.request_uri} HTTP/1.1\r\n"
107
118
  req << "Host: #{uri.host}\r\n"
108
119
  req << "Accept: text/event-stream\r\n"
109
- req << "Cache-Control: no-cache\r\n"
110
- req << "\r\n"
111
- @config.logger.debug("Request info: #{req}")
120
+ req << "Cache-Control: no-cache\r\n\r\n"
121
+ @config.logger.debug("Request info: #{req}") if @config.debug_enabled
112
122
  req
113
123
  end
114
124
 
@@ -120,43 +130,58 @@ module SplitIoClient
120
130
  end
121
131
 
122
132
  def parse_event(buffer)
123
- event_type = nil
124
- parsed_data = nil
125
- client_id = nil
133
+ type = nil
126
134
 
127
135
  buffer.each do |d|
128
136
  splited_data = d.split(':')
129
137
 
130
138
  case splited_data[0]
131
139
  when 'event'
132
- event_type = splited_data[1].strip
140
+ type = splited_data[1].strip
133
141
  when 'data'
134
- event_data = JSON.parse(d.sub('data: ', ''))
135
- client_id = event_data['clientId']&.strip
136
- parsed_data = JSON.parse(event_data['data'])
142
+ data = parse_event_data(d, type)
143
+ unless type.nil? || data[:data].nil?
144
+ event = StreamData.new(type, data[:client_id], data[:data], data[:channel])
145
+ dispatch_event(event)
146
+ end
137
147
  end
138
148
  end
149
+ rescue StandardError => e
150
+ @config.logger.error("Error during parsing a event: #{e.inspect}")
151
+ end
139
152
 
140
- return StreamData.new(event_type, client_id, parsed_data) unless event_type.nil? || parsed_data.nil?
153
+ def parse_event_data(data, type)
154
+ event_data = JSON.parse(data.sub('data: ', ''))
155
+ client_id = event_data['clientId']&.strip
156
+ channel = event_data['channel']&.strip
157
+ parsed_data = JSON.parse(event_data['data']) unless type == 'error'
158
+ parsed_data = event_data if type == 'error'
141
159
 
142
- raise 'Invalid event format.'
143
- rescue StandardError => e
144
- dispatch_error(e.inspect)
145
- nil
160
+ { client_id: client_id, channel: channel, data: parsed_data }
146
161
  end
147
162
 
148
163
  def dispatch_event(event)
149
- @config.logger.debug("Dispatching event: #{event}") unless event.nil?
150
- @on[:event].call(event) unless event.nil?
164
+ raise SSEClientException.new(event), 'Error event' if event.event_type == 'error'
165
+
166
+ @config.logger.debug("Dispatching event: #{event.event_type}, #{event.channel}") if @config.debug_enabled
167
+ @on[:event].call(event)
168
+ rescue SSEClientException => e
169
+ @config.logger.error("Event error: #{e.event.event_type}, #{e.event.data}")
170
+ close
151
171
  end
152
172
 
153
- def dispatch_error(error)
154
- @config.logger.debug("Dispatching error: #{error}")
155
- @on[:error].call(error)
173
+ def dispatch_connected
174
+ @connected.make_true
175
+ @back_off.reset
176
+ @config.logger.debug('Dispatching connected') if @config.debug_enabled
177
+ @on[:connected].call
156
178
  end
157
- end
158
179
 
159
- StreamData = Struct.new(:event_type, :client_id, :data)
180
+ def dispatch_disconnect
181
+ @config.logger.debug('Dispatching disconnect') if @config.debug_enabled
182
+ @on[:disconnect].call
183
+ end
184
+ end
160
185
  end
161
186
  end
162
187
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: false
2
+
3
+ module SplitIoClient
4
+ module SSE
5
+ module EventSource
6
+ class StreamData
7
+ attr_reader :event_type, :channel, :data, :client_id
8
+
9
+ def initialize(event_type, client_id, data, channel)
10
+ @event_type = event_type
11
+ @client_id = client_id
12
+ @data = data
13
+ @channel = channel&.gsub(SplitIoClient::Constants::OCCUPANCY_CHANNEL_PREFIX, '')
14
+ end
15
+
16
+ def occupancy?
17
+ @channel.include? 'control'
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'concurrent/atomics'
4
+
5
+ module SplitIoClient
6
+ module SSE
7
+ class NotificationManagerKeeper
8
+ def initialize(config)
9
+ @config = config
10
+ @publisher_available = Concurrent::AtomicBoolean.new(true)
11
+ @on = { occupancy: ->(_) {}, push_shutdown: ->(_) {} }
12
+
13
+ yield self if block_given?
14
+ end
15
+
16
+ def handle_incoming_occupancy_event(event)
17
+ if event.data['type'] == 'CONTROL'
18
+ process_event_control(event.data['controlType'])
19
+ elsif event.channel == SplitIoClient::Constants::CONTROL_PRI
20
+ process_event_occupancy(event.data['metrics']['publishers'])
21
+ end
22
+ rescue StandardError => e
23
+ @config.logger.error(e)
24
+ end
25
+
26
+ def on_occupancy(&action)
27
+ @on[:occupancy] = action
28
+ end
29
+
30
+ def on_push_shutdown(&action)
31
+ @on[:push_shutdown] = action
32
+ end
33
+
34
+ private
35
+
36
+ def process_event_control(type)
37
+ case type
38
+ when 'STREAMING_PAUSED'
39
+ dispatch_occupancy_event(false)
40
+ when 'STREAMING_RESUMED'
41
+ dispatch_occupancy_event(true) if @publisher_available.value
42
+ when 'STREAMING_DISABLED'
43
+ dispatch_push_shutdown
44
+ else
45
+ @config.logger.error("Incorrect event type: #{incoming_notification}")
46
+ end
47
+ end
48
+
49
+ def process_event_occupancy(publishers)
50
+ @config.logger.debug("Occupancy process event with #{publishers} publishers") if @config.debug_enabled
51
+ if publishers <= 0 && @publisher_available.value
52
+ @publisher_available.make_false
53
+ dispatch_occupancy_event(false)
54
+ elsif publishers >= 1 && !@publisher_available.value
55
+ @publisher_available.make_true
56
+ dispatch_occupancy_event(true)
57
+ end
58
+ end
59
+
60
+ def dispatch_occupancy_event(push_enable)
61
+ @config.logger.debug("Dispatching occupancy event with publisher avaliable: #{push_enable}")
62
+ @on[:occupancy].call(push_enable)
63
+ end
64
+
65
+ def dispatch_push_shutdown
66
+ @config.logger.debug('Dispatching push shutdown')
67
+ @on[:push_shutdown].call
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module SSE
5
+ class NotificationProcessor
6
+ def initialize(config, splits_worker, segments_worker)
7
+ @config = config
8
+ @splits_worker = splits_worker
9
+ @segments_worker = segments_worker
10
+ end
11
+
12
+ def process(incoming_notification)
13
+ case incoming_notification.data['type']
14
+ when SSE::EventSource::EventTypes::SPLIT_UPDATE
15
+ process_split_update(incoming_notification)
16
+ when SSE::EventSource::EventTypes::SPLIT_KILL
17
+ process_split_kill(incoming_notification)
18
+ when SSE::EventSource::EventTypes::SEGMENT_UPDATE
19
+ process_segment_update(incoming_notification)
20
+ else
21
+ @config.logger.error("Incorrect event type: #{incoming_notification}")
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def process_split_update(notification)
28
+ @config.logger.debug("SPLIT UPDATE notification received: #{notification}") if @config.debug_enabled
29
+ @splits_worker.add_to_queue(notification.data['changeNumber'])
30
+ end
31
+
32
+ def process_split_kill(notification)
33
+ @config.logger.debug("SPLIT KILL notification received: #{notification}") if @config.debug_enabled
34
+ change_number = notification.data['changeNumber']
35
+ default_treatment = notification.data['defaultTreatment']
36
+ split_name = notification.data['splitName']
37
+
38
+ @splits_worker.kill_split(change_number, split_name, default_treatment)
39
+ end
40
+
41
+ def process_segment_update(notification)
42
+ @config.logger.debug("SEGMENT UPDATE notification received: #{notification}") if @config.debug_enabled
43
+ change_number = notification.data['changeNumber']
44
+ segment_name = notification.data['segmentName']
45
+
46
+ @segments_worker.add_to_queue(change_number, segment_name)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -5,78 +5,73 @@ module SplitIoClient
5
5
  class SSEHandler
6
6
  attr_reader :sse_client
7
7
 
8
- def initialize(config, options, splits_worker, segments_worker, control_worker)
8
+ def initialize(config, synchronizer, splits_repository, segments_repository, notification_manager_keeper)
9
9
  @config = config
10
- @options = options
11
- @splits_worker = splits_worker
12
- @segments_worker = segments_worker
13
- @control_worker = control_worker
14
-
15
- @sse_client = start_sse_client
16
- end
17
-
18
- private
10
+ @notification_manager_keeper = notification_manager_keeper
11
+ @splits_worker = SplitIoClient::SSE::Workers::SplitsWorker.new(synchronizer, config, splits_repository)
12
+ @segments_worker = SplitIoClient::SSE::Workers::SegmentsWorker.new(synchronizer, config, segments_repository)
13
+ @notification_processor = SplitIoClient::SSE::NotificationProcessor.new(config, @splits_worker, @segments_worker)
14
+ @sse_client = SSE::EventSource::Client.new(@config) do |client|
15
+ client.on_event { |event| handle_incoming_message(event) }
16
+ client.on_connected { process_connected }
17
+ client.on_disconnect { process_disconnect }
18
+ end
19
19
 
20
- def start_sse_client
21
- url = "#{@options[:url_host]}/event-stream?channels=#{@options[:channels]}&v=1.1&key=#{@options[:key]}"
20
+ @on = { connected: ->(_) {}, disconnect: ->(_) {} }
22
21
 
23
- sse_client = SSE::EventSource::Client.new(url, @config) do |client|
24
- client.on_event do |event|
25
- process_event(event)
26
- end
22
+ yield self if block_given?
23
+ end
27
24
 
28
- client.on_error do |error|
29
- process_error(error)
30
- end
31
- end
25
+ def start(token_jwt, channels)
26
+ url = "#{@config.streaming_service_url}?channels=#{channels}&v=1.1&accessToken=#{token_jwt}"
27
+ @sse_client.start(url)
28
+ end
32
29
 
33
- sse_client
30
+ def stop
31
+ @sse_client.close
32
+ stop_workers
33
+ rescue StandardError => e
34
+ @config.logger.debug("SSEHandler stop error: #{e.inspect}") if @config.debug_enabled
34
35
  end
35
36
 
36
- def process_event(event)
37
- case event.data['type']
38
- when SSE::EventSource::EventTypes::SPLIT_UPDATE
39
- split_update_notification(event)
40
- when SSE::EventSource::EventTypes::SPLIT_KILL
41
- split_kill_notification(event)
42
- when SSE::EventSource::EventTypes::SEGMENT_UPDATE
43
- segment_update_notification(event)
44
- when SSE::EventSource::EventTypes::CONTROL
45
- control_notification(event)
46
- else
47
- @config.logger.error("Incorrect event type: #{event}")
48
- end
37
+ def connected?
38
+ @sse_client&.connected? || false
49
39
  end
50
40
 
51
- def process_error(error)
52
- @config.logger.error("SSE::EventSource::Client error: #{error}")
41
+ def start_workers
42
+ @splits_worker.start
43
+ @segments_worker.start
53
44
  end
54
45
 
55
- def split_update_notification(event)
56
- @config.logger.debug("SPLIT UPDATE notification received: #{event}")
57
- @splits_worker.add_to_queue(event.data['changeNumber'])
46
+ def stop_workers
47
+ @splits_worker.stop
48
+ @segments_worker.stop
58
49
  end
59
50
 
60
- def split_kill_notification(event)
61
- @config.logger.debug("SPLIT KILL notification received: #{event}")
51
+ def on_connected(&action)
52
+ @on[:connected] = action
53
+ end
62
54
 
63
- change_number = event.data['changeNumber']
64
- default_treatment = event.data['defaultTreatment']
65
- split_name = event.data['splitName']
55
+ def on_disconnect(&action)
56
+ @on[:disconnect] = action
57
+ end
66
58
 
67
- @splits_worker.kill_split(change_number, split_name, default_treatment)
59
+ def process_disconnect
60
+ @on[:disconnect].call
68
61
  end
69
62
 
70
- def segment_update_notification(event)
71
- @config.logger.debug("SEGMENT UPDATE notification received: #{event}")
72
- change_number = event.data['changeNumber']
73
- segment_name = event.data['segmentName']
63
+ private
74
64
 
75
- @segments_worker.add_to_queue(change_number, segment_name)
65
+ def process_connected
66
+ @on[:connected].call
76
67
  end
77
68
 
78
- def control_notification(event)
79
- @config.logger.debug("CONTROL notification received: #{event}")
69
+ def handle_incoming_message(notification)
70
+ if notification.occupancy?
71
+ @notification_manager_keeper.handle_incoming_occupancy_event(notification)
72
+ else
73
+ @notification_processor.process(notification)
74
+ end
80
75
  end
81
76
  end
82
77
  end