splitclient-rb 8.2.0-java → 8.3.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.
@@ -5,6 +5,10 @@ module SplitIoClient
5
5
  GET_TREATMENTS = 'get_treatments'
6
6
  GET_TREATMENT_WITH_CONFIG = 'get_treatment_with_config'
7
7
  GET_TREATMENTS_WITH_CONFIG = 'get_treatments_with_config'
8
+ GET_TREATMENTS_BY_FLAG_SET = 'get_treatments_by_flag_set'
9
+ GET_TREATMENTS_BY_FLAG_SETS = 'get_treatments_by_flag_sets'
10
+ GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET = 'get_treatments_with_config_by_flag_set'
11
+ GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS = 'get_treatments_with_config_by_flag_sets'
8
12
  TRACK = 'track'
9
13
 
10
14
  class SplitClient
@@ -14,7 +18,7 @@ module SplitIoClient
14
18
  # @param sdk_key [String] the SDK key for your split account
15
19
  #
16
20
  # @return [SplitIoClient] split.io client instance
17
- def initialize(sdk_key, repositories, status_manager, config, impressions_manager, telemetry_evaluation_producer)
21
+ def initialize(sdk_key, repositories, status_manager, config, impressions_manager, telemetry_evaluation_producer, evaluator, split_validator)
18
22
  @api_key = sdk_key
19
23
  @splits_repository = repositories[:splits]
20
24
  @segments_repository = repositories[:segments]
@@ -25,36 +29,29 @@ module SplitIoClient
25
29
  @config = config
26
30
  @impressions_manager = impressions_manager
27
31
  @telemetry_evaluation_producer = telemetry_evaluation_producer
32
+ @split_validator = split_validator
33
+ @evaluator = evaluator
28
34
  end
29
35
 
30
36
  def get_treatment(
31
37
  key, split_name, attributes = {}, split_data = nil, store_impressions = true,
32
38
  multiple = false, evaluator = nil
33
39
  )
34
- impressions = []
35
- result = treatment(key, split_name, attributes, split_data, store_impressions, multiple, evaluator, GET_TREATMENT, impressions)
36
- @impressions_manager.track(impressions)
37
-
38
- if multiple
39
- result.tap { |t| t.delete(:config) }
40
- else
41
- result[:treatment]
42
- end
40
+ result = treatment(key, split_name, attributes, split_data, store_impressions, GET_TREATMENT, multiple)
41
+ return result.tap { |t| t.delete(:config) } if multiple
42
+ result[:treatment]
43
43
  end
44
44
 
45
45
  def get_treatment_with_config(
46
46
  key, split_name, attributes = {}, split_data = nil, store_impressions = true,
47
47
  multiple = false, evaluator = nil
48
48
  )
49
- impressions = []
50
- result = treatment(key, split_name, attributes, split_data, store_impressions, multiple, evaluator, GET_TREATMENT_WITH_CONFIG, impressions)
51
- @impressions_manager.track(impressions)
52
-
53
- result
49
+ treatment(key, split_name, attributes, split_data, store_impressions, GET_TREATMENT_WITH_CONFIG, multiple)
54
50
  end
55
51
 
56
52
  def get_treatments(key, split_names, attributes = {})
57
53
  treatments = treatments(key, split_names, attributes)
54
+
58
55
  return treatments if treatments.nil?
59
56
  keys = treatments.keys
60
57
  treats = treatments.map { |_,t| t[:treatment] }
@@ -65,6 +62,38 @@ module SplitIoClient
65
62
  treatments(key, split_names, attributes, GET_TREATMENTS_WITH_CONFIG)
66
63
  end
67
64
 
65
+ def get_treatments_by_flag_set(key, flag_set, attributes = {})
66
+ valid_flag_set = @split_validator.valid_flag_sets(GET_TREATMENTS_BY_FLAG_SET, [flag_set])
67
+ split_names = @splits_repository.get_feature_flags_by_sets(valid_flag_set)
68
+ treatments = treatments(key, split_names, attributes, GET_TREATMENTS_BY_FLAG_SET)
69
+ return treatments if treatments.nil?
70
+ keys = treatments.keys
71
+ treats = treatments.map { |_,t| t[:treatment] }
72
+ Hash[keys.zip(treats)]
73
+ end
74
+
75
+ def get_treatments_by_flag_sets(key, flag_sets, attributes = {})
76
+ valid_flag_set = @split_validator.valid_flag_sets(GET_TREATMENTS_BY_FLAG_SETS, flag_sets)
77
+ split_names = @splits_repository.get_feature_flags_by_sets(valid_flag_set)
78
+ treatments = treatments(key, split_names, attributes, GET_TREATMENTS_BY_FLAG_SETS)
79
+ return treatments if treatments.nil?
80
+ keys = treatments.keys
81
+ treats = treatments.map { |_,t| t[:treatment] }
82
+ Hash[keys.zip(treats)]
83
+ end
84
+
85
+ def get_treatments_with_config_by_flag_set(key, flag_set, attributes = {})
86
+ valid_flag_set = @split_validator.valid_flag_sets(GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET, [flag_set])
87
+ split_names = @splits_repository.get_feature_flags_by_sets(valid_flag_set)
88
+ treatments(key, split_names, attributes, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET)
89
+ end
90
+
91
+ def get_treatments_with_config_by_flag_sets(key, flag_sets, attributes = {})
92
+ valid_flag_set = @split_validator.valid_flag_sets(GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS, flag_sets)
93
+ split_names = @splits_repository.get_feature_flags_by_sets(valid_flag_set)
94
+ treatments(key, split_names, attributes, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS)
95
+ end
96
+
68
97
  def destroy
69
98
  @config.logger.info('Split client shutdown started...') if @config.debug_enabled
70
99
 
@@ -117,6 +146,12 @@ module SplitIoClient
117
146
  false
118
147
  end
119
148
 
149
+ def block_until_ready(time = nil)
150
+ @status_manager.wait_until_ready(time) if @status_manager
151
+ end
152
+
153
+ private
154
+
120
155
  def keys_from_key(key)
121
156
  case key
122
157
  when Hash
@@ -126,7 +161,7 @@ module SplitIoClient
126
161
  end
127
162
  end
128
163
 
129
- def parsed_treatment(multiple, treatment_data)
164
+ def parsed_treatment(treatment_data, multiple = false)
130
165
  if multiple
131
166
  {
132
167
  treatment: treatment_data[:treatment],
@@ -135,16 +170,20 @@ module SplitIoClient
135
170
  config: treatment_data[:config]
136
171
  }
137
172
  else
138
- {
139
- treatment: treatment_data[:treatment],
140
- config: treatment_data[:config],
141
- }
173
+ {
174
+ treatment: treatment_data[:treatment],
175
+ config: treatment_data[:config],
176
+ }
142
177
  end
143
178
  end
144
179
 
145
180
  def sanitize_split_names(calling_method, split_names)
181
+ return nil if !split_names.is_a?(Array)
182
+
146
183
  split_names.compact.uniq.select do |split_name|
147
- if (split_name.is_a?(String) || split_name.is_a?(Symbol)) && !split_name.empty?
184
+ if split_name.nil?
185
+ false
186
+ elsif (split_name.is_a?(String) || split_name.is_a?(Symbol)) && !split_name.empty?
148
187
  true
149
188
  elsif split_name.is_a?(String) && split_name.empty?
150
189
  @config.logger.warn("#{calling_method}: you passed an empty feature_flag_name, flag name must be a non-empty String or a Symbol")
@@ -156,12 +195,6 @@ module SplitIoClient
156
195
  end
157
196
  end
158
197
 
159
- def block_until_ready(time = nil)
160
- @status_manager.wait_until_ready(time) if @status_manager
161
- end
162
-
163
- private
164
-
165
198
  def validate_properties(properties)
166
199
  properties_count = 0
167
200
  size = 0
@@ -194,39 +227,72 @@ module SplitIoClient
194
227
  @config.valid_mode
195
228
  end
196
229
 
197
- def treatments(key, split_names, attributes = {}, calling_method = 'get_treatments')
198
- return nil unless @config.split_validator.valid_get_treatments_parameters(calling_method, split_names)
230
+ def treatments(key, feature_flag_names, attributes = {}, calling_method = 'get_treatments')
231
+ attributes = {} if attributes.nil?
232
+ sanitized_feature_flag_names = sanitize_split_names(calling_method, feature_flag_names)
199
233
 
200
- sanitized_split_names = sanitize_split_names(calling_method, split_names)
234
+ if sanitized_feature_flag_names.nil?
235
+ @config.logger.error("#{calling_method}: feature_flag_names must be a non-empty Array")
236
+ return nil
237
+ end
201
238
 
202
- if sanitized_split_names.empty?
239
+ if sanitized_feature_flag_names.empty?
203
240
  @config.logger.error("#{calling_method}: feature_flag_names must be a non-empty Array")
204
241
  return {}
205
242
  end
206
243
 
207
244
  bucketing_key, matching_key = keys_from_key(key)
208
245
  bucketing_key = bucketing_key ? bucketing_key.to_s : nil
209
- matching_key = matching_key ? matching_key.to_s : nil
246
+ matching_key = matching_key ? matching_key.to_s : nil
210
247
 
211
- evaluator = Engine::Parser::Evaluator.new(@segments_repository, @splits_repository, @config, true)
212
- start = Time.now
213
- impressions = []
214
- treatments_labels_change_numbers =
215
- @splits_repository.get_splits(sanitized_split_names).each_with_object({}) do |(name, data), memo|
216
- memo.merge!(name => treatment(key, name, attributes, data, false, true, evaluator, calling_method, impressions))
217
- end
248
+ if !@config.split_validator.valid_get_treatments_parameters(calling_method, key, sanitized_feature_flag_names, matching_key, bucketing_key, attributes)
249
+ to_return = Hash.new
250
+ sanitized_feature_flag_names.each {|name|
251
+ to_return[name.to_sym] = control_treatment_with_config
252
+ }
253
+ return to_return
254
+ end
218
255
 
219
- record_latency(calling_method, start)
220
- @impressions_manager.track(impressions)
256
+ if !ready?
257
+ impressions = []
258
+ to_return = Hash.new
259
+ sanitized_feature_flag_names.each {|name|
260
+ to_return[name.to_sym] = control_treatment_with_config
261
+ impressions << @impressions_manager.build_impression(matching_key, bucketing_key, name.to_sym, control_treatment_with_config.merge({ label: Engine::Models::Label::NOT_READY }), { attributes: attributes, time: nil })
262
+ }
263
+ @impressions_manager.track(impressions)
264
+ return to_return
265
+ end
221
266
 
222
- split_names_keys = treatments_labels_change_numbers.keys
223
- treatments = treatments_labels_change_numbers.values.map do |v|
267
+ valid_feature_flag_names = []
268
+ sanitized_feature_flag_names.each { |feature_flag_name|
269
+ valid_feature_flag_names << feature_flag_name unless feature_flag_name.nil?
270
+ }
271
+ start = Time.now
272
+ impressions_total = []
273
+
274
+ feature_flags = @splits_repository.splits(valid_feature_flag_names)
275
+ treatments = Hash.new
276
+ invalid_treatments = Hash.new
277
+ feature_flags.each do |key, feature_flag|
278
+ if feature_flag.nil?
279
+ @config.logger.warn("#{calling_method}: you passed #{key} that " \
280
+ 'does not exist in this environment, please double check what feature flags exist in the Split user interface')
281
+ invalid_treatments[key] = control_treatment_with_config
282
+ next
283
+ end
284
+ treatments_labels_change_numbers, impressions = evaluate_treatment(feature_flag, key, bucketing_key, matching_key, attributes, calling_method)
285
+ impressions_total.concat(impressions) unless impressions.nil?
286
+ treatments[key] =
224
287
  {
225
- treatment: v[:treatment],
226
- config: v[:config]
288
+ treatment: treatments_labels_change_numbers[:treatment],
289
+ config: treatments_labels_change_numbers[:config]
227
290
  }
228
291
  end
229
- Hash[split_names_keys.zip(treatments)]
292
+ record_latency(calling_method, start)
293
+ @impressions_manager.track(impressions_total) unless impressions_total.empty?
294
+
295
+ treatments.merge(invalid_treatments)
230
296
  end
231
297
 
232
298
  #
@@ -237,72 +303,74 @@ module SplitIoClient
237
303
  # @param attributes [Hash] attributes to pass to the treatment class
238
304
  # @param split_data [Hash] split data, when provided this method doesn't fetch splits_repository for the data
239
305
  # @param store_impressions [Boolean] impressions aren't stored if this flag is false
240
- # @param multiple [Hash] internal flag to signal if method is called by get_treatments
241
- # @param evaluator [Evaluator] Evaluator class instance, used to cache treatments
242
- #
243
306
  # @return [String/Hash] Treatment as String or Hash of treatments in case of array of features
244
- def treatment(
245
- key, split_name, attributes = {}, split_data = nil, store_impressions = true,
246
- multiple = false, evaluator = nil, calling_method = 'get_treatment', impressions = []
247
- )
248
- control_treatment = { treatment: Engine::Models::Treatment::CONTROL }
249
-
307
+ def treatment(key, feature_flag_name, attributes = {}, split_data = nil, store_impressions = true,
308
+ calling_method = 'get_treatment', multiple = false)
309
+ impressions = []
250
310
  bucketing_key, matching_key = keys_from_key(key)
251
311
 
252
312
  attributes = parsed_attributes(attributes)
253
313
 
254
- return parsed_treatment(multiple, control_treatment) unless valid_client && @config.split_validator.valid_get_treatment_parameters(calling_method, key, split_name, matching_key, bucketing_key, attributes)
314
+ return parsed_treatment(control_treatment, multiple) unless valid_client && @config.split_validator.valid_get_treatment_parameters(calling_method, key, feature_flag_name, matching_key, bucketing_key, attributes)
255
315
 
256
316
  bucketing_key = bucketing_key ? bucketing_key.to_s : nil
257
317
  matching_key = matching_key.to_s
258
- sanitized_split_name = split_name.to_s.strip
318
+ sanitized_feature_flag_name = feature_flag_name.to_s.strip
259
319
 
260
- if split_name.to_s != sanitized_split_name
261
- @config.logger.warn("#{calling_method}: feature_flag_name #{split_name} has extra whitespace, trimming")
262
- split_name = sanitized_split_name
320
+ if feature_flag_name.to_s != sanitized_feature_flag_name
321
+ @config.logger.warn("#{calling_method}: feature_flag_name #{feature_flag_name} has extra whitespace, trimming")
322
+ feature_flag_name = sanitized_feature_flag_name
263
323
  end
264
324
 
265
- evaluator ||= Engine::Parser::Evaluator.new(@segments_repository, @splits_repository, @config)
325
+ feature_flag = @splits_repository.get_split(feature_flag_name)
326
+ treatments, impressions = evaluate_treatment(feature_flag, feature_flag_name, bucketing_key, matching_key, attributes, calling_method, multiple)
266
327
 
328
+ @impressions_manager.track(impressions) unless impressions.nil?
329
+ treatments
330
+ end
331
+
332
+ def evaluate_treatment(feature_flag, feature_flag_name, bucketing_key, matching_key, attributes, calling_method, multiple = false)
333
+ impressions = []
267
334
  begin
268
335
  start = Time.now
269
-
270
- split = multiple ? split_data : @splits_repository.get_split(split_name)
271
-
272
- if split.nil? && ready?
273
- @config.logger.warn("#{calling_method}: you passed #{split_name} that " \
336
+ if feature_flag.nil? && ready?
337
+ @config.logger.warn("#{calling_method}: you passed #{feature_flag_name} that " \
274
338
  'does not exist in this environment, please double check what feature flags exist in the Split user interface')
275
-
276
- return parsed_treatment(multiple, control_treatment.merge({ label: Engine::Models::Label::NOT_FOUND }))
339
+ return parsed_treatment(control_treatment.merge({ label: Engine::Models::Label::NOT_FOUND }), multiple), nil
277
340
  end
278
-
279
341
  treatment_data =
280
- if !split.nil? && ready?
281
- evaluator.call(
282
- { bucketing_key: bucketing_key, matching_key: matching_key }, split, attributes
342
+ if !feature_flag.nil? && ready?
343
+ @evaluator.evaluate_feature_flag(
344
+ { bucketing_key: bucketing_key, matching_key: matching_key }, feature_flag, attributes
283
345
  )
284
346
  else
285
347
  @config.logger.error("#{calling_method}: the SDK is not ready, the operation cannot be executed")
286
-
287
348
  control_treatment.merge({ label: Engine::Models::Label::NOT_READY })
288
349
  end
289
350
 
290
- record_latency(calling_method, start) unless multiple
291
-
292
- impression = @impressions_manager.build_impression(matching_key, bucketing_key, split_name, treatment_data, { attributes: attributes, time: nil })
351
+ record_latency(calling_method, start)
352
+ impression = @impressions_manager.build_impression(matching_key, bucketing_key, feature_flag_name, treatment_data, { attributes: attributes, time: nil })
293
353
  impressions << impression unless impression.nil?
294
354
  rescue StandardError => e
295
355
  @config.log_found_exception(__method__.to_s, e)
296
356
 
297
357
  record_exception(calling_method)
298
358
 
299
- impression = @impressions_manager.build_impression(matching_key, bucketing_key, split_name, control_treatment, { attributes: attributes, time: nil })
359
+ impression = @impressions_manager.build_impression(matching_key, bucketing_key, feature_flag_name, control_treatment, { attributes: attributes, time: nil })
300
360
  impressions << impression unless impression.nil?
301
361
 
302
- return parsed_treatment(multiple, control_treatment.merge({ label: Engine::Models::Label::EXCEPTION }))
362
+ return parsed_treatment(control_treatment.merge({ label: Engine::Models::Label::EXCEPTION }), multiple), impressions
303
363
  end
304
364
 
305
- parsed_treatment(multiple, treatment_data)
365
+ return parsed_treatment(treatment_data, multiple), impressions
366
+ end
367
+
368
+ def control_treatment
369
+ { treatment: Engine::Models::Treatment::CONTROL }
370
+ end
371
+
372
+ def control_treatment_with_config
373
+ {:treatment => Engine::Models::Treatment::CONTROL, :config => nil}
306
374
  end
307
375
 
308
376
  def variable_size(value)
@@ -320,7 +388,7 @@ module SplitIoClient
320
388
 
321
389
  def record_latency(method, start)
322
390
  bucket = BinarySearchLatencyTracker.get_bucket((Time.now - start) * 1000.0)
323
-
391
+
324
392
  case method
325
393
  when GET_TREATMENT
326
394
  @telemetry_evaluation_producer.record_latency(Telemetry::Domain::Constants::TREATMENT, bucket)
@@ -330,9 +398,17 @@ module SplitIoClient
330
398
  @telemetry_evaluation_producer.record_latency(Telemetry::Domain::Constants::TREATMENT_WITH_CONFIG, bucket)
331
399
  when GET_TREATMENTS_WITH_CONFIG
332
400
  @telemetry_evaluation_producer.record_latency(Telemetry::Domain::Constants::TREATMENTS_WITH_CONFIG, bucket)
401
+ when GET_TREATMENTS_BY_FLAG_SET
402
+ @telemetry_evaluation_producer.record_latency(Telemetry::Domain::Constants::TREATMENTS_BY_FLAG_SET, bucket)
403
+ when GET_TREATMENTS_BY_FLAG_SETS
404
+ @telemetry_evaluation_producer.record_latency(Telemetry::Domain::Constants::TREATMENTS_BY_FLAG_SETS, bucket)
405
+ when GET_TREATMENT_WITH_CONFIG
406
+ @telemetry_evaluation_producer.record_latency(Telemetry::Domain::Constants::TREATMENT_WITH_CONFIG, bucket)
407
+ when GET_TREATMENTS_WITH_CONFIG
408
+ @telemetry_evaluation_producer.record_latency(Telemetry::Domain::Constants::TREATMENTS_WITH_CONFIG, bucket)
333
409
  when TRACK
334
410
  @telemetry_evaluation_producer.record_latency(Telemetry::Domain::Constants::TRACK, bucket)
335
- end
411
+ end
336
412
  end
337
413
 
338
414
  def record_exception(method)
@@ -345,6 +421,14 @@ module SplitIoClient
345
421
  @telemetry_evaluation_producer.record_exception(Telemetry::Domain::Constants::TREATMENT_WITH_CONFIG)
346
422
  when GET_TREATMENTS_WITH_CONFIG
347
423
  @telemetry_evaluation_producer.record_exception(Telemetry::Domain::Constants::TREATMENTS_WITH_CONFIG)
424
+ when GET_TREATMENTS_BY_FLAG_SET
425
+ @telemetry_evaluation_producer.record_exception(Telemetry::Domain::Constants::TREATMENTS_BY_FLAG_SET)
426
+ when GET_TREATMENTS_BY_FLAG_SETS
427
+ @telemetry_evaluation_producer.record_exception(Telemetry::Domain::Constants::TREATMENTS_BY_FLAG_SETS)
428
+ when GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET
429
+ @telemetry_evaluation_producer.record_exception(Telemetry::Domain::Constants::TREATMENTS_WITH_CONFIG_BY_FLAG_SET)
430
+ when GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS
431
+ @telemetry_evaluation_producer.record_exception(Telemetry::Domain::Constants::TREATMENTS_WITH_CONFIG_BY_FLAG_SETS)
348
432
  when TRACK
349
433
  @telemetry_evaluation_producer.record_exception(Telemetry::Domain::Constants::TRACK)
350
434
  end
@@ -8,17 +8,22 @@ module SplitIoClient
8
8
  super(config)
9
9
  @api_key = api_key
10
10
  @telemetry_runtime_producer = telemetry_runtime_producer
11
+ @flag_sets_filter = @config.flag_sets_filter
11
12
  end
12
13
 
13
- def since(since, fetch_options = { cache_control_headers: false, till: nil })
14
+ def since(since, fetch_options = { cache_control_headers: false, till: nil, sets: nil })
14
15
  start = Time.now
15
-
16
+
16
17
  params = { since: since }
17
18
  params[:till] = fetch_options[:till] unless fetch_options[:till].nil?
19
+ params[:sets] = @flag_sets_filter.join(",") unless @flag_sets_filter.empty?
18
20
  response = get_api("#{@config.base_uri}/splitChanges", @api_key, params, fetch_options[:cache_control_headers])
21
+ if response.status == 414
22
+ @config.logger.error("Error fetching feature flags; the amount of flag sets provided are too big, causing uri length error.")
23
+ raise ApiException.new response.body, 414
24
+ end
19
25
  if response.success?
20
26
  result = splits_with_segment_names(response.body)
21
-
22
27
  unless result[:splits].empty?
23
28
  @config.split_logger.log_if_debug("#{result[:splits].length} feature flags retrieved. since=#{since}")
24
29
  end
@@ -12,7 +12,7 @@ module SplitIoClient
12
12
 
13
13
  def match?(args)
14
14
  keys = { matching_key: args[:matching_key], bucketing_key: args[:bucketing_key] }
15
- evaluate = args[:evaluator].call(keys, @feature_flag, args[:attributes])
15
+ evaluate = args[:evaluator].evaluate_feature_flag(keys, @feature_flag, args[:attributes])
16
16
  matches = @treatments.include?(evaluate[:treatment])
17
17
  @logger.log_if_debug("[dependencyMatcher] Parent feature flag #{@feature_flag} evaluated to #{evaluate[:treatment]} \
18
18
  with label #{evaluate[:label]}. #{@feature_flag} evaluated treatment is part of [#{@treatments}] ? #{matches}.")
@@ -2,41 +2,35 @@ module SplitIoClient
2
2
  module Engine
3
3
  module Parser
4
4
  class Evaluator
5
- def initialize(segments_repository, splits_repository, config, multiple = false)
5
+ def initialize(segments_repository, splits_repository, config)
6
6
  @splits_repository = splits_repository
7
7
  @segments_repository = segments_repository
8
- @multiple = multiple
9
8
  @config = config
10
- @cache = {}
11
9
  end
12
10
 
13
- def call(keys, split, attributes = nil)
11
+ def evaluate_feature_flag(keys, feature_flag, attributes = nil)
14
12
  # DependencyMatcher here, split is actually a split_name in this case
15
- cache_result = split.is_a? String
16
- split = @splits_repository.get_split(split) if cache_result
17
- digest = Digest::MD5.hexdigest("#{{matching_key: keys[:matching_key]}}#{split}#{attributes}")
13
+ cache_result = feature_flag.is_a? String
14
+ feature_flag = @splits_repository.get_split(feature_flag) if cache_result
15
+ evaluate_treatment(keys, feature_flag, attributes)
16
+ end
18
17
 
19
- if Models::Split.archived?(split)
20
- return treatment_hash(Models::Label::ARCHIVED, Models::Treatment::CONTROL, split[:changeNumber])
21
- end
18
+ private
22
19
 
23
- treatment = if Models::Split.matchable?(split)
24
- if @multiple && @cache[digest]
25
- @cache[digest]
26
- else
27
- match(split, keys, attributes)
28
- end
29
- else
30
- treatment_hash(Models::Label::KILLED, split[:defaultTreatment], split[:changeNumber], split_configurations(split[:defaultTreatment], split))
20
+ def evaluate_treatment(keys, feature_flag, attributes)
21
+ if Models::Split.archived?(feature_flag)
22
+ return treatment_hash(Models::Label::ARCHIVED, Models::Treatment::CONTROL, feature_flag[:changeNumber])
31
23
  end
32
24
 
33
- @cache[digest] = treatment if cache_result
25
+ treatment = if Models::Split.matchable?(feature_flag)
26
+ match(feature_flag, keys, attributes)
27
+ else
28
+ treatment_hash(Models::Label::KILLED, feature_flag[:defaultTreatment], feature_flag[:changeNumber], split_configurations(feature_flag[:defaultTreatment], feature_flag))
29
+ end
34
30
 
35
31
  treatment
36
32
  end
37
33
 
38
- private
39
-
40
34
  def split_configurations(treatment, split)
41
35
  return nil if split[:configurations].nil?
42
36
  split[:configurations][treatment.to_sym]
@@ -12,4 +12,15 @@ module SplitIoClient
12
12
  @event = event
13
13
  end
14
14
  end
15
+
16
+ class ApiException < SplitIoError
17
+ def initialize(msg, exception_code)
18
+ @@exception_code = exception_code
19
+ super(msg)
20
+ end
21
+ def exception_code
22
+ @@exception_code
23
+ end
24
+ end
25
+
15
26
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module Helpers
5
+ class RepositoryHelper
6
+ def self.update_feature_flag_repository(feature_flag_repository, feature_flags, change_number, config)
7
+ to_add = []
8
+ to_delete = []
9
+ feature_flags.each do |feature_flag|
10
+ if Engine::Models::Split.archived?(feature_flag) || !feature_flag_repository.flag_set_filter.intersect?(feature_flag[:sets])
11
+ config.logger.debug("removing feature flag from store(#{feature_flag})") if config.debug_enabled
12
+ to_delete.push(feature_flag)
13
+ next
14
+ end
15
+
16
+ config.logger.debug("storing feature flag (#{feature_flag[:name]})") if config.debug_enabled
17
+ to_add.push(feature_flag)
18
+ end
19
+ feature_flag_repository.update(to_add, to_delete, change_number)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -122,6 +122,7 @@ module SplitIoClient
122
122
  @on_demand_fetch_retry_delay_seconds = SplitConfig.default_on_demand_fetch_retry_delay_seconds
123
123
  @on_demand_fetch_max_retries = SplitConfig.default_on_demand_fetch_max_retries
124
124
 
125
+ @flag_sets_filter = SplitConfig.sanitize_flag_set_filter(opts[:flag_sets_filter], @split_validator, opts[:cache_adapter], @logger)
125
126
  startup_log
126
127
  end
127
128
 
@@ -287,7 +288,7 @@ module SplitIoClient
287
288
 
288
289
  attr_accessor :sdk_start_time
289
290
 
290
- attr_accessor :on_demand_fetch_retry_delay_seconds
291
+ attr_accessor :on_demand_fetch_retry_delay_seconds
291
292
  attr_accessor :on_demand_fetch_max_retries
292
293
 
293
294
  attr_accessor :unique_keys_refresh_rate
@@ -296,6 +297,12 @@ module SplitIoClient
296
297
 
297
298
  attr_accessor :counter_refresh_rate
298
299
 
300
+ #
301
+ # Flagsets filter
302
+ #
303
+ # @return [Array]
304
+ attr_accessor :flag_sets_filter
305
+
299
306
  def self.default_counter_refresh_rate(adapter)
300
307
  return 300 if adapter == :redis # Send bulk impressions count - Refresh rate: 5 min.
301
308
 
@@ -325,9 +332,9 @@ module SplitIoClient
325
332
  end
326
333
  end
327
334
 
328
- def self.init_impressions_refresh_rate(impressions_mode, refresh_rate, default_rate)
335
+ def self.init_impressions_refresh_rate(impressions_mode, refresh_rate, default_rate)
329
336
  return (refresh_rate.nil? || refresh_rate <= 0 ? default_rate : refresh_rate) if impressions_mode == :debug
330
-
337
+
331
338
  return refresh_rate.nil? || refresh_rate <= 0 ? SplitConfig.default_impressions_refresh_rate_optimized : [default_rate, refresh_rate].max
332
339
  end
333
340
 
@@ -482,7 +489,7 @@ module SplitIoClient
482
489
  end
483
490
 
484
491
  def self.default_telemetry_refresh_rate
485
- 3600
492
+ 3600
486
493
  end
487
494
 
488
495
  def self.default_unique_keys_refresh_rate(adapter)
@@ -513,6 +520,15 @@ module SplitIoClient
513
520
  5
514
521
  end
515
522
 
523
+ def self.sanitize_flag_set_filter(flag_sets, validator, adapter, logger)
524
+ return [] if flag_sets.nil?
525
+ if adapter == :redis
526
+ logger.warn("config: : flag_sets_filter is not applicable for Consumer modes where the SDK does not keep rollout data in sync. FlagSet filter was discarded")
527
+ return []
528
+ end
529
+ return validator.valid_flag_sets(:config, flag_sets)
530
+ end
531
+
516
532
  #
517
533
  # The default logger object
518
534
  #
@@ -646,7 +662,7 @@ module SplitIoClient
646
662
  return 'NA'.freeze
647
663
  end
648
664
  end
649
-
665
+
650
666
  return ''.freeze
651
667
  end
652
668
 
@@ -656,7 +672,7 @@ module SplitIoClient
656
672
  # @return [string]
657
673
  def self.machine_ip(ip_addresses_enabled, ip, adapter)
658
674
  if ip_addresses_enabled
659
- begin
675
+ begin
660
676
  return ip unless ip.nil? || ip.to_s.empty?
661
677
 
662
678
  loopback_ip = Socket.ip_address_list.find { |ip| ip.ipv4_loopback? }