Skip to content

Commit 16746c2

Browse files
committed
Added models and updated config
1 parent ac959eb commit 16746c2

File tree

8 files changed

+210
-4
lines changed

8 files changed

+210
-4
lines changed

lib/splitclient-rb.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,11 @@
110110
require 'splitclient-rb/engine/models/treatment'
111111
require 'splitclient-rb/engine/models/split_http_response'
112112
require 'splitclient-rb/engine/models/evaluation_options'
113+
require 'splitclient-rb/engine/models/fallback_treatment.rb'
114+
require 'splitclient-rb/engine/models/fallback_treatments_configuration.rb'
113115
require 'splitclient-rb/engine/auth_api_client'
114116
require 'splitclient-rb/engine/back_off'
117+
require 'splitclient-rb/engine/fallback_treatment_calculator.rb'
115118
require 'splitclient-rb/engine/push_manager'
116119
require 'splitclient-rb/engine/status_manager'
117120
require 'splitclient-rb/engine/sync_manager'
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# frozen_string_literal: true
2+
3+
module SplitIoClient
4+
module Engine
5+
class FallbackTreatmentCalculator
6+
attr_accessor :fallback_treatments_configuration, :label_prefix
7+
8+
def initialize(fallback_treatment_configuration)
9+
@label_prefix = 'fallback - '
10+
@fallback_treatments_configuration = fallback_treatment_configuration
11+
end
12+
13+
def resolve(flag_name, label)
14+
default_fallback_treatment = Engine::Models::FallbackTreatment.new(
15+
Engine::Models::Treatment::CONTROL,
16+
nil,
17+
label
18+
)
19+
return default_fallback_treatment if @fallback_treatments_configuration.nil?
20+
21+
if !@fallback_treatments_configuration.by_flag_fallback_treatment.nil? \
22+
&& !@fallback_treatments_configuration.by_flag_fallback_treatment.fetch(flag_name, nil).nil?
23+
return copy_with_label(
24+
@fallback_treatments_configuration.by_flag_fallback_treatment[flag_name],
25+
resolve_label(label)
26+
)
27+
end
28+
29+
return copy_with_label(@fallback_treatments_configuration.global_fallback_treatment, resolve_label(label)) \
30+
unless @fallback_treatments_configuration.global_fallback_treatment.nil?
31+
32+
default_fallback_treatment
33+
end
34+
35+
private
36+
37+
def resolve_label(label)
38+
return nil if label.nil?
39+
40+
@label_prefix + label
41+
end
42+
43+
def copy_with_label(fallback_treatment, label)
44+
Engine::Models::FallbackTreatment.new(fallback_treatment.treatment, fallback_treatment.config, label)
45+
end
46+
end
47+
end
48+
end
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module SplitIoClient::Engine::Models
2+
class FallbackTreatment
3+
attr_accessor :treatment, :config, :label
4+
5+
def initialize(treatment, config=nil, label=nil)
6+
@treatment = treatment
7+
@config = config
8+
@label = label
9+
end
10+
end
11+
end
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
module SplitIoClient::Engine::Models
2+
class FallbackTreatmentsConfiguration
3+
attr_accessor :global_fallback_treatment, :by_flag_fallback_treatment
4+
5+
def initialize(global_fallback_treatment=nil, by_flag_fallback_treatment=nil)
6+
@global_fallback_treatment = build_global_fallback_treatment(global_fallback_treatment)
7+
@by_flag_fallback_treatment = build_by_flag_fallback_treatment(by_flag_fallback_treatment)
8+
end
9+
10+
private
11+
12+
def build_global_fallback_treatment(global_fallback_treatment)
13+
if global_fallback_treatment.is_a? String
14+
return FallbackTreatment.new(global_fallback_treatment)
15+
end
16+
17+
global_fallback_treatment
18+
end
19+
20+
def build_by_flag_fallback_treatment(by_flag_fallback_treatment)
21+
return nil unless by_flag_fallback_treatment.is_a? Hash
22+
processed_by_flag_fallback_treatment = Hash.new
23+
24+
by_flag_fallback_treatment.each do |key, value|
25+
if value.is_a? String
26+
processed_by_flag_fallback_treatment[key] = FallbackTreatment.new(value)
27+
next
28+
end
29+
30+
processed_by_flag_fallback_treatment[key] = value
31+
end
32+
33+
processed_by_flag_fallback_treatment
34+
end
35+
end
36+
end

lib/splitclient-rb/split_config.rb

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ def initialize(opts = {})
123123
@on_demand_fetch_max_retries = SplitConfig.default_on_demand_fetch_max_retries
124124

125125
@flag_sets_filter = SplitConfig.sanitize_flag_set_filter(opts[:flag_sets_filter], @split_validator, opts[:cache_adapter], @logger)
126+
127+
@fallback_treatments_configuration = SplitConfig.sanitize_fallback_config(opts[:fallback_treatments], @split_validator, @logger)
126128
startup_log
127129
end
128130

@@ -303,6 +305,8 @@ def initialize(opts = {})
303305
# @return [Array]
304306
attr_accessor :flag_sets_filter
305307

308+
attr_accessor :fallback_treatments_configuration
309+
306310
def self.default_counter_refresh_rate(adapter)
307311
return 300 if adapter == :redis # Send bulk impressions count - Refresh rate: 5 min.
308312

@@ -697,5 +701,36 @@ def self.machine_ip(ip_addresses_enabled, ip, adapter)
697701

698702
return ''.freeze
699703
end
704+
705+
def self.sanitize_fallback_config(fallback_config, validator, logger)
706+
return fallback_config if fallback_config.nil?
707+
708+
processed = Engine::Models::FallbackTreatmentsConfiguration.new
709+
if !fallback_config.is_a?(Engine::Models::FallbackTreatmentsConfiguration)
710+
logger.warn('Config: fallbackTreatments parameter should be of `FallbackTreatmentsConfiguration` class.')
711+
return processed
712+
end
713+
714+
sanitized_global_fallback_treatment = fallback_config.global_fallback_treatment
715+
if !fallback_config.global_fallback_treatment.nil? && !validator.validate_fallback_treatment(fallback_config.global_fallback_treatment)
716+
logger.warn('Config: global fallbacktreatment parameter is discarded.')
717+
sanitized_global_fallback_treatment = None
718+
end
719+
720+
sanitized_flag_fallback_treatments = Hash.new
721+
if !fallback_config.by_flag_fallback_treatment.nil? && fallback_config.by_flag_fallback_treatment.is_a?(Hash)
722+
for feature_name in fallback_config.by_flag_fallback_treatment.keys()
723+
if !validator.valid_split_name?('Config', feature_name) || !validator.validate_fallback_treatment(fallback_config.by_flag_fallback_treatment[feature_name])
724+
logger.warn("Config: fallback treatment parameter for feature flag #{feature_name} is discarded.")
725+
next
726+
end
727+
728+
sanitized_flag_fallback_treatments[feature_name] = fallback_config.by_flag_fallback_treatment[feature_name]
729+
end
730+
end
731+
processed = Engine::Models::FallbackTreatmentsConfiguration.new(sanitized_global_fallback_treatment, sanitized_flag_fallback_treatments)
732+
733+
processed
734+
end
700735
end
701736
end

lib/splitclient-rb/validators.rb

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ module SplitIoClient
44
class Validators
55

66
Flagset_regex = /^[a-z0-9][_a-z0-9]{0,49}$/
7+
Fallback_treatment_regex = /^[0-9]+[.a-zA-Z0-9_-]*$|^[a-zA-Z]+[a-zA-Z0-9_-]*$/
8+
Fallback_treatment_size = 100
79

810
def initialize(config)
911
@config = config
@@ -68,7 +70,7 @@ def valid_flag_sets(method, flag_sets)
6870
log_invalid_flag_set_type(method)
6971
elsif flag_set.is_a?(String) && flag_set.empty?
7072
log_invalid_flag_set_type(method)
71-
elsif !flag_set.empty? && string_match?(flag_set.strip.downcase, method)
73+
elsif !flag_set.empty? && string_match?(flag_set.strip.downcase, method, Flagset_regex, log_invalid_match)
7274
valid_flag_sets.add(flag_set.strip.downcase)
7375
else
7476
log_invalid_flag_set_type(method)
@@ -91,9 +93,9 @@ def number_or_string?(value)
9193
(value.is_a?(Numeric) && !value.to_f.nan?) || string?(value)
9294
end
9395

94-
def string_match?(value, method)
95-
if Flagset_regex.match(value) == nil
96-
log_invalid_match(value, method)
96+
def string_match?(value, method, regex_exp, log_if_invalid)
97+
if regex_exp.match(value) == nil
98+
log_if_invalid(value, method)
9799
false
98100
else
99101
true
@@ -326,5 +328,30 @@ def valid_properties?(properties)
326328

327329
true
328330
end
331+
332+
def validate_fallback_treatment(method, fallback_treatment)
333+
if !fallback_treatment.is_a? Engine::Models::FallbackTreatment
334+
@config.logger.warn("#{method}: Fallback treatment instance should be FallbackTreatment, input is discarded")
335+
return false
336+
end
337+
338+
if !fallback_treatment.treatment.is_a? String
339+
@config.logger.warn("#{method}: Fallback treatment value should be str type, input is discarded")
340+
return false
341+
end
342+
343+
return false unless string_match?(fallback_treatment.treatment, method, Fallback_treatment_regex)
344+
345+
if fallback_treatment.treatment.size > Fallback_treatment_size
346+
@config.logger.warn("#{method}: Fallback treatment size should not exceed %s characters", Fallback_treatment_size)
347+
return false
348+
end
349+
350+
true
351+
end
352+
353+
def log_invalid_fallback_treatment(key, method)
354+
@config.logger.warn("#{method}: Invalid treatment #{key}, Fallback treatment should match regex %s", Fallback_treatment_regex)
355+
end
329356
end
330357
end
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
describe SplitIoClient::Engine::FallbackTreatmentCalculator do
6+
context 'works' do
7+
it 'process fallback treatments' do
8+
fallback_config = SplitIoClient::Engine::Models::FallbackTreatmentsConfiguration.new(SplitIoClient::Engine::Models::FallbackTreatment.new("on" ,"{}"))
9+
fallback_calculator = SplitIoClient::Engine::FallbackTreatmentCalculator.new(fallback_config)
10+
expect(fallback_calculator.fallback_treatments_configuration).to be fallback_config
11+
expect(fallback_calculator.label_prefix).to eq("fallback - ")
12+
13+
fallback_treatment = fallback_calculator.resolve("feature", "not ready")
14+
expect(fallback_treatment.treatment).to eq("on")
15+
expect(fallback_treatment.label).to eq("fallback - not ready")
16+
expect(fallback_treatment.config).to eq("{}")
17+
18+
fallback_calculator.fallback_treatments_configuration = SplitIoClient::Engine::Models::FallbackTreatmentsConfiguration.new(SplitIoClient::Engine::Models::FallbackTreatment.new("on" ,"{}"), {:feature => SplitIoClient::Engine::Models::FallbackTreatment.new("off" , '{"prop": "val"}')})
19+
fallback_treatment = fallback_calculator.resolve(:feature, "not ready")
20+
expect(fallback_treatment.treatment).to eq("off")
21+
expect(fallback_treatment.label).to eq("fallback - not ready")
22+
expect(fallback_treatment.config).to eq('{"prop": "val"}')
23+
24+
fallback_treatment = fallback_calculator.resolve(:feature2, "not ready")
25+
expect(fallback_treatment.treatment).to eq("on")
26+
expect(fallback_treatment.label).to eq("fallback - not ready")
27+
expect(fallback_treatment.config).to eq("{}")
28+
end
29+
end
30+
end
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
describe SplitIoClient::Engine::Models::FallbackTreatmentsConfiguration do
6+
context 'works' do
7+
it 'it converts string to fallback treatment' do
8+
fb_config = described_class.new("global", {:feature => "local"})
9+
expect(fb_config.global_fallback_treatment.is_a?(SplitIoClient::Engine::Models::FallbackTreatment)).to be true
10+
expect(fb_config.global_fallback_treatment.treatment).to be "global"
11+
12+
expect(fb_config.by_flag_fallback_treatment[:feature].is_a?(SplitIoClient::Engine::Models::FallbackTreatment)).to be true
13+
expect(fb_config.by_flag_fallback_treatment[:feature].treatment).to be "local"
14+
end
15+
end
16+
end

0 commit comments

Comments
 (0)