splitclient-rb 8.5.0.pre.rc1-java → 8.6.0.pre.rc1-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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.txt +4 -0
  3. data/LICENSE +1 -1
  4. data/lib/splitclient-rb/cache/fetchers/split_fetcher.rb +9 -7
  5. data/lib/splitclient-rb/cache/repositories/rule_based_segments_repository.rb +122 -0
  6. data/lib/splitclient-rb/cache/repositories/segments_repository.rb +7 -0
  7. data/lib/splitclient-rb/cache/repositories/splits_repository.rb +19 -11
  8. data/lib/splitclient-rb/cache/stores/localhost_split_builder.rb +2 -1
  9. data/lib/splitclient-rb/clients/split_client.rb +2 -0
  10. data/lib/splitclient-rb/engine/api/client.rb +8 -0
  11. data/lib/splitclient-rb/engine/api/splits.rb +99 -23
  12. data/lib/splitclient-rb/engine/matchers/combining_matcher.rb +3 -1
  13. data/lib/splitclient-rb/engine/matchers/prerequisites_matcher.rb +31 -0
  14. data/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb +75 -0
  15. data/lib/splitclient-rb/engine/models/label.rb +1 -0
  16. data/lib/splitclient-rb/engine/models/segment_type.rb +4 -0
  17. data/lib/splitclient-rb/engine/models/split_http_response.rb +19 -0
  18. data/lib/splitclient-rb/engine/parser/condition.rb +9 -0
  19. data/lib/splitclient-rb/engine/parser/evaluator.rb +24 -30
  20. data/lib/splitclient-rb/engine/synchronizer.rb +33 -13
  21. data/lib/splitclient-rb/helpers/evaluator_helper.rb +37 -0
  22. data/lib/splitclient-rb/helpers/repository_helper.rb +38 -7
  23. data/lib/splitclient-rb/helpers/util.rb +12 -3
  24. data/lib/splitclient-rb/spec.rb +1 -1
  25. data/lib/splitclient-rb/split_config.rb +4 -0
  26. data/lib/splitclient-rb/split_factory.rb +5 -3
  27. data/lib/splitclient-rb/sse/event_source/event_types.rb +1 -0
  28. data/lib/splitclient-rb/sse/notification_processor.rb +3 -1
  29. data/lib/splitclient-rb/sse/workers/splits_worker.rb +58 -19
  30. data/lib/splitclient-rb/version.rb +1 -1
  31. data/lib/splitclient-rb.rb +6 -0
  32. data/splitclient-rb.gemspec +2 -2
  33. metadata +12 -6
@@ -230,6 +230,13 @@ module SplitIoClient
230
230
  )
231
231
  end
232
232
 
233
+ def matcher_in_rule_based_segment(params)
234
+ matcher = params[:matcher]
235
+ segment_name = matcher[:userDefinedSegmentMatcherData] && matcher[:userDefinedSegmentMatcherData][:segmentName]
236
+
237
+ RuleBasedSegmentMatcher.new(params[:segments_repository], params[:rule_based_segments_repository], segment_name, @config)
238
+ end
239
+
233
240
  #
234
241
  # @return [object] the negate value for this condition
235
242
  def negate
@@ -246,6 +253,8 @@ module SplitIoClient
246
253
  # @return [void]
247
254
  def set_partitions
248
255
  partitions_list = []
256
+ return partitions_list unless @data.key?(:partitions) or @data.key?('partitions')
257
+
249
258
  @data[:partitions].each do |p|
250
259
  partition = SplitIoClient::Partition.new(p)
251
260
  partitions_list << partition
@@ -2,9 +2,10 @@ module SplitIoClient
2
2
  module Engine
3
3
  module Parser
4
4
  class Evaluator
5
- def initialize(segments_repository, splits_repository, config)
5
+ def initialize(segments_repository, splits_repository, rb_segment_repository, config)
6
6
  @splits_repository = splits_repository
7
7
  @segments_repository = segments_repository
8
+ @rb_segment_repository = rb_segment_repository
8
9
  @config = config
9
10
  end
10
11
 
@@ -37,6 +38,9 @@ module SplitIoClient
37
38
  end
38
39
 
39
40
  def match(split, keys, attributes)
41
+
42
+ return treatment_hash(Models::Label::PREREQUISITES_NOT_MET, split[:defaultTreatment], split[:changeNumber], split_configurations(split[:defaultTreatment], split)) unless check_prerequisites_matcher(split, keys, attributes)
43
+
40
44
  in_rollout = false
41
45
  key = keys[:bucketing_key] ? keys[:bucketing_key] : keys[:matching_key]
42
46
  legacy_algo = (split[:algo] == 1 || split[:algo] == nil) ? true : false
@@ -58,8 +62,7 @@ module SplitIoClient
58
62
 
59
63
  in_rollout = true
60
64
  end
61
-
62
- condition_matched = matcher_type(condition).match?(
65
+ condition_matched = Helpers::EvaluatorHelper::matcher_type(condition, @segments_repository, @rb_segment_repository).match?(
63
66
  matching_key: keys[:matching_key],
64
67
  bucketing_key: keys[:bucketing_key],
65
68
  evaluator: self,
@@ -70,35 +73,19 @@ module SplitIoClient
70
73
 
71
74
  result = splitter.get_treatment(key, split[:seed], condition.partitions, split[:algo])
72
75
 
73
- if result.nil?
74
- return treatment_hash(Models::Label::NO_RULE_MATCHED, split[:defaultTreatment], split[:changeNumber], split_configurations(split[:defaultTreatment], split))
75
- else
76
- return treatment_hash(c[:label], result, split[:changeNumber],split_configurations(result, split))
77
- end
76
+ return treatment_from_result(result, split, c)
78
77
  end
79
78
 
80
79
  treatment_hash(Models::Label::NO_RULE_MATCHED, split[:defaultTreatment], split[:changeNumber], split_configurations(split[:defaultTreatment], split))
81
80
  end
82
81
 
83
- def matcher_type(condition)
84
- matchers = []
85
-
86
- @segments_repository.adapter.pipelined do
87
- condition.matchers.each do |matcher|
88
- matchers << if matcher[:negate]
89
- condition.negation_matcher(matcher_instance(matcher[:matcherType], condition, matcher))
90
- else
91
- matcher_instance(matcher[:matcherType], condition, matcher)
92
- end
93
- end
94
- end
95
-
96
- final_matcher = condition.create_condition_matcher(matchers)
82
+ private
97
83
 
98
- if final_matcher.nil?
99
- @logger.error('Invalid matcher type')
84
+ def treatment_from_result(result, split, condition)
85
+ if result.nil?
86
+ return treatment_hash(Models::Label::NO_RULE_MATCHED, split[:defaultTreatment], split[:changeNumber], split_configurations(split[:defaultTreatment], split))
100
87
  else
101
- final_matcher
88
+ return treatment_hash(condition[:label], result, split[:changeNumber],split_configurations(result, split))
102
89
  end
103
90
  end
104
91
 
@@ -106,11 +93,18 @@ module SplitIoClient
106
93
  { label: label, treatment: treatment, change_number: change_number, config: configurations }
107
94
  end
108
95
 
109
- def matcher_instance(type, condition, matcher)
110
- condition.send(
111
- "matcher_#{type.downcase}",
112
- matcher: matcher, segments_repository: @segments_repository
113
- )
96
+ def check_prerequisites_matcher(split, keys, attributes)
97
+ if split.key?(:prerequisites) && !split[:prerequisites].nil?
98
+ prerequisites_matcher = SplitIoClient::PrerequisitesMatcher.new(split[:prerequisites], @config.split_logger)
99
+ return prerequisites_matcher.match?(
100
+ matching_key: keys[:matching_key],
101
+ bucketing_key: keys[:bucketing_key],
102
+ evaluator: self,
103
+ attributes: attributes
104
+ )
105
+ end
106
+
107
+ true
114
108
  end
115
109
  end
116
110
  end
@@ -15,6 +15,7 @@ module SplitIoClient
15
15
  )
16
16
  @splits_repository = repositories[:splits]
17
17
  @segments_repository = repositories[:segments]
18
+ @rule_based_segments_repository = repositories[:rule_based_segments]
18
19
  @impressions_repository = repositories[:impressions]
19
20
  @events_repository = repositories[:events]
20
21
  @config = config
@@ -63,12 +64,12 @@ module SplitIoClient
63
64
  @segment_fetcher.stop_segments_thread
64
65
  end
65
66
 
66
- def fetch_splits(target_change_number)
67
- return if target_change_number <= @splits_repository.get_change_number.to_i
67
+ def fetch_splits(target_change_number, rbs_target_change_number)
68
+ return if check_exit_conditions(target_change_number, rbs_target_change_number)
68
69
 
69
70
  fetch_options = { cache_control_headers: true, till: nil }
70
71
 
71
- result = attempt_splits_sync(target_change_number,
72
+ result = attempt_splits_sync(target_change_number, rbs_target_change_number,
72
73
  fetch_options,
73
74
  @config.on_demand_fetch_max_retries,
74
75
  @config.on_demand_fetch_retry_delay_seconds,
@@ -82,8 +83,13 @@ module SplitIoClient
82
83
  return
83
84
  end
84
85
 
85
- fetch_options[:till] = target_change_number
86
- result = attempt_splits_sync(target_change_number,
86
+ if target_change_number != 0
87
+ fetch_options[:till] = target_change_number
88
+ else
89
+ fetch_options[:till] = rbs_target_change_number
90
+ end
91
+
92
+ result = attempt_splits_sync(target_change_number, rbs_target_change_number,
87
93
  fetch_options,
88
94
  ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES,
89
95
  nil,
@@ -91,12 +97,7 @@ module SplitIoClient
91
97
 
92
98
  attempts = ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES - result[:remaining_attempts]
93
99
 
94
- if result[:success]
95
- @segment_fetcher.fetch_segments_if_not_exists(result[:segment_names], true) unless result[:segment_names].empty?
96
- @config.logger.debug("Refresh completed bypassing the CDN in #{attempts} attempts.") if @config.debug_enabled
97
- else
98
- @config.logger.debug("No changes fetched after #{attempts} attempts with CDN bypassed.") if @config.debug_enabled
99
- end
100
+ process_result(result, attempts)
100
101
  rescue StandardError => e
101
102
  @config.log_found_exception(__method__.to_s, e)
102
103
  end
@@ -139,6 +140,15 @@ module SplitIoClient
139
140
 
140
141
  private
141
142
 
143
+ def process_result(result, attempts)
144
+ if result[:success]
145
+ @segment_fetcher.fetch_segments_if_not_exists(result[:segment_names], true) unless result[:segment_names].empty?
146
+ @config.logger.debug("Refresh completed bypassing the CDN in #{attempts} attempts.") if @config.debug_enabled
147
+ else
148
+ @config.logger.debug("No changes fetched after #{attempts} attempts with CDN bypassed.") if @config.debug_enabled
149
+ end
150
+ end
151
+
142
152
  def attempt_segment_sync(name, target_cn, fetch_options, max_retries, retry_delay_seconds, with_backoff)
143
153
  remaining_attempts = max_retries
144
154
  @segments_sync_backoff.reset
@@ -156,7 +166,7 @@ module SplitIoClient
156
166
  end
157
167
  end
158
168
 
159
- def attempt_splits_sync(target_cn, fetch_options, max_retries, retry_delay_seconds, with_backoff)
169
+ def attempt_splits_sync(target_cn, rbs_target_cn, fetch_options, max_retries, retry_delay_seconds, with_backoff)
160
170
  remaining_attempts = max_retries
161
171
  @splits_sync_backoff.reset
162
172
 
@@ -165,7 +175,7 @@ module SplitIoClient
165
175
 
166
176
  result = @split_fetcher.fetch_splits(fetch_options)
167
177
 
168
- return sync_result(true, remaining_attempts, result[:segment_names]) if target_cn <= @splits_repository.get_change_number
178
+ return sync_result(true, remaining_attempts, result[:segment_names]) if check_exit_conditions(target_cn, rbs_target_cn)
169
179
  return sync_result(false, remaining_attempts, result[:segment_names]) if remaining_attempts <= 0
170
180
 
171
181
  delay = with_backoff ? @splits_sync_backoff.interval : retry_delay_seconds
@@ -206,6 +216,16 @@ module SplitIoClient
206
216
 
207
217
  splits_result[:success] && @segment_fetcher.fetch_segments
208
218
  end
219
+
220
+ def check_exit_conditions(target_change_number, rbs_target_change_number)
221
+ return true if rbs_target_change_number == 0 and target_change_number == 0
222
+
223
+ return target_change_number <= @splits_repository.get_change_number.to_i if rbs_target_change_number == 0
224
+
225
+ return rbs_target_change_number <= @rule_based_segments_repository.get_change_number.to_i if target_change_number == 0
226
+
227
+ return (target_change_number <= @splits_repository.get_change_number.to_i and rbs_target_change_number <= @rule_based_segments_repository.get_change_number.to_i)
228
+ end
209
229
  end
210
230
  end
211
231
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module Helpers
5
+ class EvaluatorHelper
6
+ def self.matcher_type(condition, segments_repository, rb_segment_repository)
7
+ matchers = []
8
+ segments_repository.adapter.pipelined do
9
+ condition.matchers.each do |matcher|
10
+ matchers << if matcher[:negate]
11
+ condition.negation_matcher(matcher_instance(matcher[:matcherType], condition,
12
+ matcher, segments_repository,
13
+ rb_segment_repository))
14
+ else
15
+ matcher_instance(matcher[:matcherType], condition, matcher, segments_repository, rb_segment_repository)
16
+ end
17
+ end
18
+ end
19
+ final_matcher = condition.create_condition_matcher(matchers)
20
+
21
+ if final_matcher.nil?
22
+ config.logger.error('Invalid matcher type')
23
+ else
24
+ final_matcher
25
+ end
26
+ final_matcher
27
+ end
28
+
29
+ def self.matcher_instance(type, condition, matcher, segments_repository, rb_segment_repository)
30
+ condition.send(
31
+ "matcher_#{type.downcase}",
32
+ matcher: matcher, segments_repository: segments_repository, rule_based_segments_repository: rb_segment_repository
33
+ )
34
+ end
35
+ end
36
+ end
37
+ end
@@ -3,7 +3,7 @@
3
3
  module SplitIoClient
4
4
  module Helpers
5
5
  class RepositoryHelper
6
- def self.update_feature_flag_repository(feature_flag_repository, feature_flags, change_number, config)
6
+ def self.update_feature_flag_repository(feature_flag_repository, feature_flags, change_number, config, clear_storage)
7
7
  to_add = []
8
8
  to_delete = []
9
9
  feature_flags.each do |feature_flag|
@@ -13,18 +13,49 @@ module SplitIoClient
13
13
  next
14
14
  end
15
15
 
16
- unless feature_flag.key?(:impressionsDisabled)
17
- feature_flag[:impressionsDisabled] = false
18
- if config.debug_enabled
19
- config.logger.debug("feature flag (#{feature_flag[:name]}) does not have impressionsDisabled field, setting it to false")
20
- end
21
- end
16
+ feature_flag = check_missing_elements(feature_flag, config)
22
17
 
23
18
  config.logger.debug("storing feature flag (#{feature_flag[:name]})") if config.debug_enabled
24
19
  to_add.push(feature_flag)
25
20
  end
21
+ feature_flag_repository.clear if clear_storage
26
22
  feature_flag_repository.update(to_add, to_delete, change_number)
27
23
  end
24
+
25
+ def self.check_missing_elements(feature_flag, config)
26
+ unless feature_flag.key?(:impressionsDisabled)
27
+ feature_flag[:impressionsDisabled] = false
28
+ if config.debug_enabled
29
+ config.logger.debug("feature flag (#{feature_flag[:name]}) does not have impressionsDisabled field, setting it to false")
30
+ end
31
+ end
32
+
33
+ unless feature_flag.key?(:prerequisites)
34
+ feature_flag[:prerequisites] = []
35
+ if config.debug_enabled
36
+ config.logger.debug("feature flag (#{feature_flag[:name]}) does not have prerequisites field, setting it to empty array")
37
+ end
38
+ end
39
+
40
+ feature_flag
41
+ end
42
+
43
+ def self.update_rule_based_segment_repository(rule_based_segment_repository, rule_based_segments, change_number, config)
44
+ to_add = []
45
+ to_delete = []
46
+ rule_based_segments.each do |rule_based_segment|
47
+ if Engine::Models::Split.archived?(rule_based_segment)
48
+ config.logger.debug("removing rule based segment from store(#{rule_based_segment})") if config.debug_enabled
49
+ to_delete.push(rule_based_segment)
50
+ next
51
+ end
52
+
53
+ config.logger.debug("storing rule based segment (#{rule_based_segment[:name]})") if config.debug_enabled
54
+ to_add.push(rule_based_segment)
55
+ end
56
+
57
+ rule_based_segment_repository.update(to_add, to_delete, change_number)
58
+ end
28
59
  end
29
60
  end
30
61
  end
@@ -3,15 +3,24 @@
3
3
  module SplitIoClient
4
4
  module Helpers
5
5
  class Util
6
- def self.segment_names_by_feature_flag(feature_flag)
7
- feature_flag[:conditions].each_with_object(Set.new) do |condition, names|
6
+ def self.segment_names_by_object(object, matcher_type)
7
+ object[:conditions].each_with_object(Set.new) do |condition, names|
8
8
  condition[:matcherGroup][:matchers].each do |matcher|
9
- next if matcher[:userDefinedSegmentMatcherData].nil?
9
+ next if matcher[:userDefinedSegmentMatcherData].nil? || matcher[:matcherType] != matcher_type
10
10
 
11
11
  names << matcher[:userDefinedSegmentMatcherData][:segmentName]
12
12
  end
13
13
  end
14
14
  end
15
+
16
+ def self.segment_names_in_rb_segment(object, matcher_type)
17
+ names = Set.new
18
+ names.merge segment_names_by_object(object, matcher_type)
19
+ object[:excluded][:segments].each do |segment|
20
+ names.add(segment[:name]) if segment[:type] == SplitIoClient::Engine::Models::SegmentType::STANDARD
21
+ end
22
+ names
23
+ end
15
24
  end
16
25
  end
17
26
  end
@@ -3,7 +3,7 @@
3
3
  module SplitIoClient
4
4
  module Spec
5
5
  class FeatureFlags
6
- SPEC_VERSION = "1.1"
6
+ SPEC_VERSION = "1.3"
7
7
  end
8
8
  end
9
9
  end
@@ -645,6 +645,10 @@ module SplitIoClient
645
645
  @mode.equal?(:consumer)
646
646
  end
647
647
 
648
+ def sdk_url_overriden?
649
+ return @base_uri != SplitConfig.default_base_uri
650
+ end
651
+
648
652
  #
649
653
  # gets the hostname where the sdk gem is running
650
654
  #
@@ -55,7 +55,7 @@ module SplitIoClient
55
55
 
56
56
  @status_manager = Engine::StatusManager.new(@config)
57
57
  @split_validator = SplitIoClient::Validators.new(@config)
58
- @evaluator = Engine::Parser::Evaluator.new(@segments_repository, @splits_repository, @config)
58
+ @evaluator = Engine::Parser::Evaluator.new(@segments_repository, @splits_repository, @rule_based_segment_repository, @config)
59
59
 
60
60
  start!
61
61
 
@@ -154,6 +154,7 @@ module SplitIoClient
154
154
  segments: @segments_repository,
155
155
  impressions: @impressions_repository,
156
156
  events: @events_repository,
157
+ rule_based_segments: @rule_based_segment_repository
157
158
  }
158
159
  end
159
160
 
@@ -178,7 +179,7 @@ module SplitIoClient
178
179
  end
179
180
 
180
181
  def build_fetchers
181
- @split_fetcher = SplitFetcher.new(@splits_repository, @api_key, @config, @runtime_producer)
182
+ @split_fetcher = SplitFetcher.new(@splits_repository, @rule_based_segment_repository, @api_key, @config, @runtime_producer)
182
183
  @segment_fetcher = SegmentFetcher.new(@segments_repository, @api_key, @config, @runtime_producer)
183
184
  end
184
185
 
@@ -198,7 +199,7 @@ module SplitIoClient
198
199
 
199
200
  def build_streaming_components
200
201
  @push_status_queue = Queue.new
201
- splits_worker = SSE::Workers::SplitsWorker.new(@synchronizer, @config, @splits_repository, @runtime_producer, @segment_fetcher)
202
+ splits_worker = SSE::Workers::SplitsWorker.new(@synchronizer, @config, @splits_repository, @runtime_producer, @segment_fetcher, @rule_based_segment_repository)
202
203
  segments_worker = SSE::Workers::SegmentsWorker.new(@synchronizer, @config, @segments_repository)
203
204
  notification_manager_keeper = SSE::NotificationManagerKeeper.new(@config, @runtime_producer, @push_status_queue)
204
205
  notification_processor = SSE::NotificationProcessor.new(@config, splits_worker, segments_worker)
@@ -220,6 +221,7 @@ module SplitIoClient
220
221
  end
221
222
  @splits_repository = SplitsRepository.new(@config, @flag_sets_repository, @flag_sets_filter)
222
223
  @segments_repository = SegmentsRepository.new(@config)
224
+ @rule_based_segment_repository = RuleBasedSegmentsRepository.new(@config)
223
225
  @impressions_repository = ImpressionsRepository.new(@config)
224
226
  @events_repository = EventsRepository.new(@config, @api_key, @runtime_producer)
225
227
  end
@@ -8,6 +8,7 @@ module SplitIoClient
8
8
  SPLIT_KILL = 'SPLIT_KILL'
9
9
  SEGMENT_UPDATE = 'SEGMENT_UPDATE'
10
10
  CONTROL = 'CONTROL'
11
+ RB_SEGMENT_UPDATE = 'RB_SEGMENT_UPDATE'
11
12
  end
12
13
  end
13
14
  end
@@ -13,6 +13,8 @@ module SplitIoClient
13
13
  case incoming_notification.data['type']
14
14
  when SSE::EventSource::EventTypes::SPLIT_UPDATE
15
15
  process_split_update(incoming_notification)
16
+ when SSE::EventSource::EventTypes::RB_SEGMENT_UPDATE
17
+ process_split_update(incoming_notification)
16
18
  when SSE::EventSource::EventTypes::SPLIT_KILL
17
19
  process_split_kill(incoming_notification)
18
20
  when SSE::EventSource::EventTypes::SEGMENT_UPDATE
@@ -25,7 +27,7 @@ module SplitIoClient
25
27
  private
26
28
 
27
29
  def process_split_update(notification)
28
- @config.logger.debug("SPLIT UPDATE notification received: #{notification}") if @config.debug_enabled
30
+ @config.logger.debug("#{notification.event_type} notification received: #{notification}") if @config.debug_enabled
29
31
  @splits_worker.add_to_queue(notification)
30
32
  end
31
33
 
@@ -4,7 +4,8 @@ module SplitIoClient
4
4
  module SSE
5
5
  module Workers
6
6
  class SplitsWorker
7
- def initialize(synchronizer, config, feature_flags_repository, telemetry_runtime_producer, segment_fetcher)
7
+ def initialize(synchronizer, config, feature_flags_repository, telemetry_runtime_producer,
8
+ segment_fetcher, rule_based_segment_repository)
8
9
  @synchronizer = synchronizer
9
10
  @config = config
10
11
  @feature_flags_repository = feature_flags_repository
@@ -12,6 +13,7 @@ module SplitIoClient
12
13
  @running = Concurrent::AtomicBoolean.new(false)
13
14
  @telemetry_runtime_producer = telemetry_runtime_producer
14
15
  @segment_fetcher = segment_fetcher
16
+ @rule_based_segment_repository = rule_based_segment_repository
15
17
  end
16
18
 
17
19
  def start
@@ -54,7 +56,10 @@ module SplitIoClient
54
56
  case notification.data['type']
55
57
  when SSE::EventSource::EventTypes::SPLIT_UPDATE
56
58
  success = update_feature_flag(notification)
57
- @synchronizer.fetch_splits(notification.data['changeNumber']) unless success
59
+ @synchronizer.fetch_splits(notification.data['changeNumber'], 0) unless success
60
+ when SSE::EventSource::EventTypes::RB_SEGMENT_UPDATE
61
+ success = update_rule_based_segment(notification)
62
+ @synchronizer.fetch_splits(0, notification.data['changeNumber']) unless success
58
63
  when SSE::EventSource::EventTypes::SPLIT_KILL
59
64
  kill_feature_flag(notification)
60
65
  end
@@ -65,11 +70,12 @@ module SplitIoClient
65
70
  return true if @feature_flags_repository.get_change_number.to_i >= notification.data['changeNumber']
66
71
  return false unless !notification.data['d'].nil? && @feature_flags_repository.get_change_number == notification.data['pcn']
67
72
 
68
- new_split = return_split_from_json(notification)
69
- SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(@feature_flags_repository,
70
- [new_split],
71
- notification.data['changeNumber'], @config)
72
- fetch_segments_if_not_exists(new_split)
73
+ new_split = update_feature_flag_repository(notification)
74
+ fetch_segments_if_not_exists(Helpers::Util.segment_names_by_object(new_split, 'IN_SEGMENT'), @feature_flags_repository)
75
+ if fetch_rule_based_segments_if_not_exists(Helpers::Util.segment_names_by_object(new_split, 'IN_RULE_BASED_SEGMENT'),
76
+ notification.data['changeNumber'])
77
+ return true
78
+ end
73
79
 
74
80
  @telemetry_runtime_producer.record_updates_from_sse(Telemetry::Domain::Constants::SPLITS)
75
81
 
@@ -80,30 +86,63 @@ module SplitIoClient
80
86
  false
81
87
  end
82
88
 
89
+ def update_feature_flag_repository(notification)
90
+ new_split = return_object_from_json(notification)
91
+ SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(@feature_flags_repository, [new_split],
92
+ notification.data['changeNumber'], @config, false)
93
+ new_split
94
+ end
95
+
96
+ def update_rule_based_segment(notification)
97
+ return true if @rule_based_segment_repository.get_change_number.to_i >= notification.data['changeNumber']
98
+ return false unless !notification.data['d'].nil? &&
99
+ @rule_based_segment_repository.get_change_number == notification.data['pcn']
100
+
101
+ new_rb_segment = return_object_from_json(notification)
102
+ SplitIoClient::Helpers::RepositoryHelper.update_rule_based_segment_repository(@rule_based_segment_repository,
103
+ [new_rb_segment],
104
+ notification.data['changeNumber'], @config)
105
+ fetch_segments_if_not_exists(Helpers::Util.segment_names_in_rb_segment(new_rb_segment, 'IN_SEGMENT'),
106
+ @rule_based_segment_repository)
107
+
108
+ # @telemetry_runtime_producer.record_updates_from_sse(Telemetry::Domain::Constants::SPLITS)
109
+
110
+ true
111
+ rescue StandardError => e
112
+ @config.logger.debug("Failed to update Split: #{e.inspect}") if @config.debug_enabled
113
+
114
+ false
115
+ end
116
+
83
117
  def kill_feature_flag(notification)
84
118
  return if @feature_flags_repository.get_change_number.to_i > notification.data['changeNumber']
85
119
 
86
120
  @config.logger.debug("feature_flags_worker kill #{notification.data['splitName']}, #{notification.data['changeNumber']}")
87
- @feature_flags_repository.kill(
88
- notification.data['changeNumber'],
89
- notification.data['splitName'],
90
- notification.data['defaultTreatment']
91
- )
92
- @synchronizer.fetch_splits(notification.data['changeNumber'])
121
+ @feature_flags_repository.kill(notification.data['changeNumber'],
122
+ notification.data['splitName'],
123
+ notification.data['defaultTreatment'])
124
+ @synchronizer.fetch_splits(notification.data['changeNumber'], 0)
93
125
  end
94
126
 
95
- def return_split_from_json(notification)
96
- split_json = Helpers::DecryptionHelper.get_encoded_definition(notification.data['c'], notification.data['d'])
97
- JSON.parse(split_json, symbolize_names: true)
127
+ def return_object_from_json(notification)
128
+ object_json = Helpers::DecryptionHelper.get_encoded_definition(notification.data['c'], notification.data['d'])
129
+ JSON.parse(object_json, symbolize_names: true)
98
130
  end
99
131
 
100
- def fetch_segments_if_not_exists(feature_flag)
101
- segment_names = Helpers::Util.segment_names_by_feature_flag(feature_flag)
132
+ def fetch_segments_if_not_exists(segment_names, object_repository)
102
133
  return if segment_names.nil?
103
134
 
104
- @feature_flags_repository.set_segment_names(segment_names)
135
+ object_repository.set_segment_names(segment_names)
105
136
  @segment_fetcher.fetch_segments_if_not_exists(segment_names)
106
137
  end
138
+
139
+ def fetch_rule_based_segments_if_not_exists(segment_names, change_number)
140
+ return false if segment_names.nil? || segment_names.empty? || @rule_based_segment_repository.contains?(segment_names.to_a)
141
+
142
+ @synchronizer.fetch_splits(0, change_number)
143
+
144
+ true
145
+ end
107
146
  end
108
147
  end
109
148
  end
@@ -1,3 +1,3 @@
1
1
  module SplitIoClient
2
- VERSION = '8.5.0.pre.rc1'
2
+ VERSION = '8.6.0-rc1'
3
3
  end
@@ -23,6 +23,7 @@ require 'splitclient-rb/cache/repositories/segments_repository'
23
23
  require 'splitclient-rb/cache/repositories/splits_repository'
24
24
  require 'splitclient-rb/cache/repositories/events_repository'
25
25
  require 'splitclient-rb/cache/repositories/impressions_repository'
26
+ require 'splitclient-rb/cache/repositories/rule_based_segments_repository'
26
27
  require 'splitclient-rb/cache/repositories/events/memory_repository'
27
28
  require 'splitclient-rb/cache/repositories/events/redis_repository'
28
29
  require 'splitclient-rb/cache/repositories/flag_sets/memory_repository'
@@ -47,6 +48,7 @@ require 'splitclient-rb/helpers/thread_helper'
47
48
  require 'splitclient-rb/helpers/decryption_helper'
48
49
  require 'splitclient-rb/helpers/util'
49
50
  require 'splitclient-rb/helpers/repository_helper'
51
+ require 'splitclient-rb/helpers/evaluator_helper'
50
52
  require 'splitclient-rb/split_factory'
51
53
  require 'splitclient-rb/split_factory_builder'
52
54
  require 'splitclient-rb/split_config'
@@ -96,13 +98,17 @@ require 'splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher'
96
98
  require 'splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher'
97
99
  require 'splitclient-rb/engine/matchers/between_semver_matcher'
98
100
  require 'splitclient-rb/engine/matchers/in_list_semver_matcher'
101
+ require 'splitclient-rb/engine/matchers/rule_based_segment_matcher'
102
+ require 'splitclient-rb/engine/matchers/prerequisites_matcher'
99
103
  require 'splitclient-rb/engine/evaluator/splitter'
100
104
  require 'splitclient-rb/engine/impressions/noop_unique_keys_tracker'
101
105
  require 'splitclient-rb/engine/impressions/unique_keys_tracker'
102
106
  require 'splitclient-rb/engine/metrics/binary_search_latency_tracker'
103
107
  require 'splitclient-rb/engine/models/split'
104
108
  require 'splitclient-rb/engine/models/label'
109
+ require 'splitclient-rb/engine/models/segment_type'
105
110
  require 'splitclient-rb/engine/models/treatment'
111
+ require 'splitclient-rb/engine/models/split_http_response'
106
112
  require 'splitclient-rb/engine/auth_api_client'
107
113
  require 'splitclient-rb/engine/back_off'
108
114
  require 'splitclient-rb/engine/push_manager'
@@ -47,8 +47,8 @@ Gem::Specification.new do |spec|
47
47
  spec.add_development_dependency 'simplecov', '~> 0.20'
48
48
  spec.add_development_dependency 'simplecov-json', '~> 0.2'
49
49
  spec.add_development_dependency 'timecop', '~> 0.9'
50
- spec.add_development_dependency 'webmock', '~> 3.14'
51
- spec.add_development_dependency 'webrick', '~> 1.7'
50
+ spec.add_development_dependency 'webmock', '~> 3.24'
51
+ spec.add_development_dependency 'webrick', '~> 1.8.2'
52
52
 
53
53
  spec.add_runtime_dependency 'bitarray', '~> 1.3'
54
54
  spec.add_runtime_dependency 'concurrent-ruby', '~> 1.0'