prefab-cloud-ruby 0.10.0 → 0.11.0
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.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/lib/prefab/client.rb +2 -4
- data/lib/prefab/config_client.rb +4 -3
- data/lib/prefab/config_resolver.rb +3 -1
- data/lib/prefab/feature_flag_client.rb +32 -20
- data/lib/prefab_pb.rb +11 -18
- data/prefab-cloud-ruby.gemspec +3 -3
- data/test/harness_server.rb +4 -4
- data/test/test_config_resolver.rb +6 -3
- data/test/test_feature_flag_client.rb +142 -55
- data/test/test_helper.rb +20 -5
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 22d564bcc76424b3308623b4d2e27a4acaf4217c285bcca02542291d8cc15827
|
4
|
+
data.tar.gz: c097169d379d9f477a66d20fc378f693173de044baecad08059263fd239257e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cdf6ca3b1b28bf83caa629193b21e2d26e4d22d64ee4cd30b7a88fdce623adc7f3feb3605df7107cdca00e7823709f9eeac2a68176db1d10b6616196d2bcb580
|
7
|
+
data.tar.gz: c61b171a2be217c016fad1cc0a1ce9faed159c6f894065db4202ec200ec1f8ac22caabb880fae0f358fcced0930ce49d4e273f800a0fb72c39c6d6f19cc59d07
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.11.0
|
data/lib/prefab/client.rb
CHANGED
@@ -8,7 +8,7 @@ module Prefab
|
|
8
8
|
}
|
9
9
|
|
10
10
|
|
11
|
-
attr_reader :project_id, :shared_cache, :stats, :namespace, :interceptor, :api_key, :
|
11
|
+
attr_reader :project_id, :shared_cache, :stats, :namespace, :interceptor, :api_key, :prefab_api_url
|
12
12
|
|
13
13
|
def initialize(api_key: ENV['PREFAB_API_KEY'],
|
14
14
|
logdev: nil,
|
@@ -26,14 +26,12 @@ module Prefab
|
|
26
26
|
@stats = (stats || NoopStats.new)
|
27
27
|
@shared_cache = (shared_cache || NoopCache.new)
|
28
28
|
@api_key = api_key
|
29
|
-
@project_id = api_key.split("-")[0].to_i
|
30
|
-
@project_env_id = api_key.split("-")[1].to_i
|
29
|
+
@project_id = api_key.split("-")[0].to_i # unvalidated, but that's ok. APIs only listen to the actual passwd
|
31
30
|
@namespace = namespace
|
32
31
|
@interceptor = Prefab::AuthInterceptor.new(api_key)
|
33
32
|
@stubs = {}
|
34
33
|
@prefab_api_url = ENV["PREFAB_API_URL"] || 'https://api.prefab.cloud'
|
35
34
|
@prefab_grpc_url = ENV["PREFAB_GRPC_URL"] || 'grpc.prefab.cloud:443'
|
36
|
-
log_internal Logger::INFO, "Prefab Initializing in environment: '#{@project_env_id}' and namespace: '#{@namespace}'"
|
37
35
|
log_internal Logger::INFO, "Prefab Connecting to: #{@prefab_api_url} and #{@prefab_grpc_url} Secure: #{http_secure?}"
|
38
36
|
at_exit do
|
39
37
|
channel.destroy
|
data/lib/prefab/config_client.rb
CHANGED
@@ -104,9 +104,6 @@ module Prefab
|
|
104
104
|
resp = stub.get_all_config(config_req)
|
105
105
|
@base_client.log_internal Logger::DEBUG, "Got Response #{resp}"
|
106
106
|
load_configs(resp, :api)
|
107
|
-
resp.configs.each do |delta|
|
108
|
-
@config_loader.set(delta)
|
109
|
-
end
|
110
107
|
@config_resolver.update
|
111
108
|
finish_init!(:api)
|
112
109
|
true
|
@@ -127,6 +124,10 @@ module Prefab
|
|
127
124
|
end
|
128
125
|
|
129
126
|
def load_configs(configs, source)
|
127
|
+
project_env_id = configs.config_service_pointer.project_env_id
|
128
|
+
@config_resolver.project_env_id = project_env_id
|
129
|
+
|
130
|
+
@base_client.log_internal Logger::INFO, "Prefab Initializing in project: #{@base_client.project_id} environment: #{project_env_id} and namespace: '#{@namespace}'"
|
130
131
|
configs.configs.each do |config|
|
131
132
|
@config_loader.set(config)
|
132
133
|
end
|
@@ -3,12 +3,14 @@ module Prefab
|
|
3
3
|
include Prefab::ConfigHelper
|
4
4
|
NAMESPACE_DELIMITER = ".".freeze
|
5
5
|
|
6
|
+
attr_accessor :project_env_id # this will be set by the config_client when it gets an API response
|
7
|
+
|
6
8
|
def initialize(base_client, config_loader)
|
7
9
|
@lock = Concurrent::ReadWriteLock.new
|
8
10
|
@local_store = {}
|
9
|
-
@project_env_id = base_client.project_env_id
|
10
11
|
@namespace = base_client.namespace
|
11
12
|
@config_loader = config_loader
|
13
|
+
@project_env_id = 0
|
12
14
|
make_local
|
13
15
|
end
|
14
16
|
|
@@ -2,7 +2,6 @@ module Prefab
|
|
2
2
|
class FeatureFlagClient
|
3
3
|
include Prefab::ConfigHelper
|
4
4
|
MAX_32_FLOAT = 4294967294.0
|
5
|
-
DISTRIBUTION_SPACE = 1000
|
6
5
|
|
7
6
|
def initialize(base_client)
|
8
7
|
@base_client = base_client
|
@@ -22,9 +21,11 @@ module Prefab
|
|
22
21
|
return is_on?(get(feature_name, lookup_key, attributes))
|
23
22
|
end
|
24
23
|
|
25
|
-
def get(feature_name, lookup_key, attributes)
|
24
|
+
def get(feature_name, lookup_key=nil, attributes={})
|
26
25
|
feature_obj = @base_client.config_client.get(feature_name)
|
27
|
-
|
26
|
+
config_obj = @base_client.config_client.get_config_obj(feature_name)
|
27
|
+
return nil if feature_obj.nil? || config_obj.nil?
|
28
|
+
variants = config_obj.variants
|
28
29
|
evaluate(feature_name, lookup_key, attributes, feature_obj, variants)
|
29
30
|
end
|
30
31
|
|
@@ -39,6 +40,9 @@ module Prefab
|
|
39
40
|
return false
|
40
41
|
end
|
41
42
|
variant.bool
|
43
|
+
rescue
|
44
|
+
@base_client.log.info("is_on? methods only work for boolean feature flags variants. This feature flags variant is '#{variant}'. Returning false")
|
45
|
+
false
|
42
46
|
end
|
43
47
|
|
44
48
|
def get_variant(feature_name, lookup_key, attributes, feature_obj, variants)
|
@@ -46,8 +50,6 @@ module Prefab
|
|
46
50
|
return get_variant_obj(variants, feature_obj.inactive_variant_idx)
|
47
51
|
end
|
48
52
|
|
49
|
-
variant_distribution = feature_obj.default
|
50
|
-
|
51
53
|
# if user_targets.match
|
52
54
|
feature_obj.user_targets.each do |target|
|
53
55
|
if (target.identifiers.include? lookup_key)
|
@@ -55,34 +57,37 @@ module Prefab
|
|
55
57
|
end
|
56
58
|
end
|
57
59
|
|
60
|
+
#default to inactive
|
61
|
+
variant_weights = [Prefab::VariantWeight.new(variant_idx: feature_obj.inactive_variant_idx, weight: 1)]
|
62
|
+
|
58
63
|
# if rules.match
|
59
64
|
feature_obj.rules.each do |rule|
|
60
65
|
if criteria_match?(rule, lookup_key, attributes)
|
61
|
-
|
66
|
+
variant_weights = rule.variant_weights
|
67
|
+
break
|
62
68
|
end
|
63
69
|
end
|
64
70
|
|
65
|
-
if variant_distribution.type == :variant_idx
|
66
|
-
variant_idx = variant_distribution.variant_idx
|
67
|
-
else
|
68
|
-
percent_through_distribution = rand()
|
69
|
-
if lookup_key
|
70
|
-
percent_through_distribution = get_user_pct(feature_name, lookup_key)
|
71
|
-
end
|
72
|
-
distribution_bucket = DISTRIBUTION_SPACE * percent_through_distribution
|
73
71
|
|
74
|
-
|
72
|
+
percent_through_distribution = rand()
|
73
|
+
if lookup_key
|
74
|
+
percent_through_distribution = get_user_pct(feature_name, lookup_key)
|
75
75
|
end
|
76
76
|
|
77
|
+
variant_idx = get_variant_idx_from_weights(variant_weights, percent_through_distribution, feature_name)
|
78
|
+
|
77
79
|
return get_variant_obj(variants, variant_idx)
|
78
80
|
end
|
79
81
|
|
80
82
|
def get_variant_obj(variants, idx)
|
81
|
-
|
83
|
+
# our array is 0 based, but the idx are 1 based so the protos are clearly set
|
84
|
+
return variants[idx - 1] if variants.length >= idx
|
82
85
|
nil
|
83
86
|
end
|
84
87
|
|
85
|
-
def get_variant_idx_from_weights(variant_weights,
|
88
|
+
def get_variant_idx_from_weights(variant_weights, percent_through_distribution, feature_name)
|
89
|
+
distrubution_space = variant_weights.inject(0) { |sum, v| sum + v.weight }
|
90
|
+
bucket = distrubution_space * percent_through_distribution
|
86
91
|
sum = 0
|
87
92
|
variant_weights.each do |variant_weight|
|
88
93
|
if bucket < sum + variant_weight.weight
|
@@ -93,7 +98,7 @@ module Prefab
|
|
93
98
|
end
|
94
99
|
# variants didn't add up to 100%
|
95
100
|
@base_client.log.info("Variants of #{feature_name} did not add to 100%")
|
96
|
-
return variant_weights.last.
|
101
|
+
return variant_weights.last.variant_idx
|
97
102
|
end
|
98
103
|
|
99
104
|
def get_user_pct(feature, lookup_key)
|
@@ -103,14 +108,21 @@ module Prefab
|
|
103
108
|
end
|
104
109
|
|
105
110
|
def criteria_match?(rule, lookup_key, attributes)
|
106
|
-
|
111
|
+
|
112
|
+
if rule.criteria.operator == :ALWAYS_TRUE
|
113
|
+
return true
|
114
|
+
elsif rule.criteria.operator == :LOOKUP_KEY_IN
|
107
115
|
return rule.criteria.values.include?(lookup_key)
|
108
|
-
elsif rule.criteria.operator == :
|
116
|
+
elsif rule.criteria.operator == :LOOKUP_KEY_NOT_IN
|
109
117
|
return !rule.criteria.values.include?(lookup_key)
|
110
118
|
elsif rule.criteria.operator == :IN_SEG
|
111
119
|
return segment_matches(rule.criteria.values, lookup_key, attributes).any?
|
112
120
|
elsif rule.criteria.operator == :NOT_IN_SEG
|
113
121
|
return segment_matches(rule.criteria.values, lookup_key, attributes).none?
|
122
|
+
elsif rule.criteria.operator == :PROP_IS_ONE_OF
|
123
|
+
return rule.criteria.values.include?(attributes[rule.criteria.property]) || rule.criteria.values.include?(attributes[rule.criteria.property.to_sym])
|
124
|
+
elsif rule.criteria.operator == :PROP_IS_NOT_ONE_OF
|
125
|
+
return !(rule.criteria.values.include?(attributes[rule.criteria.property]) || rule.criteria.values.include?(attributes[rule.criteria.property.to_sym]))
|
114
126
|
end
|
115
127
|
@base_client.log.info("Unknown Operator")
|
116
128
|
false
|
data/lib/prefab_pb.rb
CHANGED
@@ -24,6 +24,7 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
|
|
24
24
|
end
|
25
25
|
add_message "prefab.Configs" do
|
26
26
|
repeated :configs, :message, 1, "prefab.Config"
|
27
|
+
optional :config_service_pointer, :message, 2, "prefab.ConfigServicePointer"
|
27
28
|
end
|
28
29
|
add_message "prefab.Config" do
|
29
30
|
optional :id, :int64, 1
|
@@ -90,15 +91,19 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
|
|
90
91
|
end
|
91
92
|
add_enum "prefab.Criteria.CriteriaOperator" do
|
92
93
|
value :NOT_SET, 0
|
93
|
-
value :
|
94
|
-
value :
|
95
|
-
value :
|
96
|
-
value :
|
97
|
-
value :
|
94
|
+
value :LOOKUP_KEY_IN, 1
|
95
|
+
value :LOOKUP_KEY_NOT_IN, 2
|
96
|
+
value :IN_SEG, 3
|
97
|
+
value :NOT_IN_SEG, 4
|
98
|
+
value :ALWAYS_TRUE, 5
|
99
|
+
value :PROP_IS_ONE_OF, 6
|
100
|
+
value :PROP_IS_NOT_ONE_OF, 7
|
101
|
+
value :PROP_ENDS_WITH_ONE_OF, 8
|
102
|
+
value :PROP_DOES_NOT_END_WITH_ONE_OF, 9
|
98
103
|
end
|
99
104
|
add_message "prefab.Rule" do
|
100
105
|
optional :criteria, :message, 1, "prefab.Criteria"
|
101
|
-
|
106
|
+
repeated :variant_weights, :message, 2, "prefab.VariantWeight"
|
102
107
|
end
|
103
108
|
add_message "prefab.Segment" do
|
104
109
|
optional :name, :string, 1
|
@@ -115,19 +120,9 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
|
|
115
120
|
optional :weight, :int32, 1
|
116
121
|
optional :variant_idx, :int32, 2
|
117
122
|
end
|
118
|
-
add_message "prefab.VariantWeights" do
|
119
|
-
repeated :weights, :message, 1, "prefab.VariantWeight"
|
120
|
-
end
|
121
|
-
add_message "prefab.VariantDistribution" do
|
122
|
-
oneof :type do
|
123
|
-
optional :variant_idx, :int32, 1
|
124
|
-
optional :variant_weights, :message, 2, "prefab.VariantWeights"
|
125
|
-
end
|
126
|
-
end
|
127
123
|
add_message "prefab.FeatureFlag" do
|
128
124
|
optional :active, :bool, 1
|
129
125
|
optional :inactive_variant_idx, :int32, 2
|
130
|
-
optional :default, :message, 3, "prefab.VariantDistribution"
|
131
126
|
repeated :user_targets, :message, 4, "prefab.UserTarget"
|
132
127
|
repeated :rules, :message, 5, "prefab.Rule"
|
133
128
|
end
|
@@ -212,8 +207,6 @@ module Prefab
|
|
212
207
|
Segment = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.Segment").msgclass
|
213
208
|
UserTarget = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.UserTarget").msgclass
|
214
209
|
VariantWeight = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.VariantWeight").msgclass
|
215
|
-
VariantWeights = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.VariantWeights").msgclass
|
216
|
-
VariantDistribution = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.VariantDistribution").msgclass
|
217
210
|
FeatureFlag = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.FeatureFlag").msgclass
|
218
211
|
LimitDefinition = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.LimitDefinition").msgclass
|
219
212
|
LimitDefinition::SafetyLevel = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.LimitDefinition.SafetyLevel").enummodule
|
data/prefab-cloud-ruby.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: prefab-cloud-ruby 0.
|
5
|
+
# stub: prefab-cloud-ruby 0.11.0 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "prefab-cloud-ruby".freeze
|
9
|
-
s.version = "0.
|
9
|
+
s.version = "0.11.0"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib".freeze]
|
13
13
|
s.authors = ["Jeff Dwyer".freeze]
|
14
|
-
s.date = "2022-
|
14
|
+
s.date = "2022-05-05"
|
15
15
|
s.description = "RateLimits & Config as a service".freeze
|
16
16
|
s.email = "jdwyer@prefab.cloud".freeze
|
17
17
|
s.extra_rdoc_files = [
|
data/test/harness_server.rb
CHANGED
@@ -15,18 +15,18 @@ class RackApp
|
|
15
15
|
|
16
16
|
key = props["key"]
|
17
17
|
namespace = props["namespace"]
|
18
|
-
|
18
|
+
api_key = props["api_key"]
|
19
19
|
user_key = props["user_key"]
|
20
20
|
is_feature_flag = !props["feature_flag"].nil?
|
21
|
-
|
21
|
+
puts props
|
22
22
|
client = Prefab::Client.new(
|
23
|
-
api_key:
|
23
|
+
api_key: api_key,
|
24
24
|
namespace: namespace,
|
25
25
|
)
|
26
26
|
|
27
27
|
puts "Key #{key}"
|
28
28
|
puts "User #{user_key}"
|
29
|
-
puts "
|
29
|
+
puts "api_key #{api_key}"
|
30
30
|
puts "Namespace #{namespace}"
|
31
31
|
puts "Props! #{props}"
|
32
32
|
puts "is_feature_flag! #{is_feature_flag}"
|
@@ -117,12 +117,12 @@ class TestConfigResolver < Minitest::Test
|
|
117
117
|
rows: [
|
118
118
|
{ value: Prefab::ConfigValue.new(feature_flag: Prefab::FeatureFlag.new(
|
119
119
|
inactive_variant_idx: 0,
|
120
|
-
|
120
|
+
rules: default_ff_rule(1),
|
121
121
|
)) },
|
122
122
|
{ project_env_id: TEST_ENV_ID,
|
123
123
|
value: Prefab::ConfigValue.new(feature_flag: Prefab::FeatureFlag.new(
|
124
124
|
inactive_variant_idx: 0,
|
125
|
-
|
125
|
+
rules: default_ff_rule(2),
|
126
126
|
)) }
|
127
127
|
]
|
128
128
|
)
|
@@ -189,7 +189,10 @@ class TestConfigResolver < Minitest::Test
|
|
189
189
|
end
|
190
190
|
|
191
191
|
def resolver_for_namespace(namespace, loader, project_env_id: TEST_ENV_ID)
|
192
|
-
Prefab::ConfigResolver.new(MockBaseClient.new(namespace: namespace
|
192
|
+
resolver = Prefab::ConfigResolver.new(MockBaseClient.new(namespace: namespace), loader)
|
193
|
+
resolver.project_env_id = project_env_id
|
194
|
+
resolver.update
|
195
|
+
resolver
|
193
196
|
end
|
194
197
|
|
195
198
|
end
|
@@ -19,22 +19,29 @@ class TestFeatureFlagClient < Minitest::Test
|
|
19
19
|
]
|
20
20
|
flag = Prefab::FeatureFlag.new(
|
21
21
|
active: true,
|
22
|
-
inactive_variant_idx:
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
22
|
+
inactive_variant_idx: 1,
|
23
|
+
rules: [
|
24
|
+
Prefab::Rule.new(
|
25
|
+
criteria: Prefab::Criteria.new(operator: Prefab::Criteria::CriteriaOperator::ALWAYS_TRUE),
|
26
|
+
variant_weights: [
|
27
|
+
Prefab::VariantWeight.new(weight: 86,
|
28
|
+
variant_idx: 2), #true
|
29
|
+
Prefab::VariantWeight.new(weight: 14,
|
30
|
+
variant_idx: 1), #false
|
31
|
+
]
|
32
|
+
)
|
33
|
+
]
|
32
34
|
)
|
35
|
+
# weights above chosen to be 86% in variant_idx 2. and 14% in variant_idx 1.
|
36
|
+
# since hashes high is 86.32 > 86 it just falls outside the 86% range and gets false
|
33
37
|
|
38
|
+
# "1FlagNamehashes high" hashes to 86.322% through dist
|
34
39
|
assert_equal false,
|
35
40
|
@client.evaluate(feature, "hashes high", [], flag, variants)
|
41
|
+
# "1FlagNamehashes low" hashes to 44.547% through dist
|
36
42
|
assert_equal true,
|
37
43
|
@client.evaluate(feature, "hashes low", [], flag, variants)
|
44
|
+
|
38
45
|
end
|
39
46
|
|
40
47
|
def test_basic_active_inactive
|
@@ -45,8 +52,8 @@ class TestFeatureFlagClient < Minitest::Test
|
|
45
52
|
]
|
46
53
|
flag = Prefab::FeatureFlag.new(
|
47
54
|
active: true,
|
48
|
-
inactive_variant_idx:
|
49
|
-
|
55
|
+
inactive_variant_idx: 1,
|
56
|
+
rules: default_ff_rule(2)
|
50
57
|
)
|
51
58
|
assert_equal true,
|
52
59
|
@client.evaluate(feature, "hashes high", [], flag, variants)
|
@@ -59,8 +66,8 @@ class TestFeatureFlagClient < Minitest::Test
|
|
59
66
|
]
|
60
67
|
flag = Prefab::FeatureFlag.new(
|
61
68
|
active: false,
|
62
|
-
inactive_variant_idx:
|
63
|
-
|
69
|
+
inactive_variant_idx: 1,
|
70
|
+
rules: default_ff_rule(2)
|
64
71
|
)
|
65
72
|
assert_equal false,
|
66
73
|
@client.evaluate(feature, "hashes high", [], flag, variants)
|
@@ -78,12 +85,12 @@ class TestFeatureFlagClient < Minitest::Test
|
|
78
85
|
]
|
79
86
|
flag = Prefab::FeatureFlag.new(
|
80
87
|
active: true,
|
81
|
-
inactive_variant_idx:
|
88
|
+
inactive_variant_idx: 1,
|
82
89
|
user_targets: [
|
83
|
-
variant_idx:
|
90
|
+
variant_idx: 2,
|
84
91
|
identifiers: ["user:1", "user:3"]
|
85
92
|
],
|
86
|
-
|
93
|
+
rules: default_ff_rule(3)
|
87
94
|
)
|
88
95
|
|
89
96
|
assert_equal "user target",
|
@@ -103,15 +110,27 @@ class TestFeatureFlagClient < Minitest::Test
|
|
103
110
|
]
|
104
111
|
flag = Prefab::FeatureFlag.new(
|
105
112
|
active: true,
|
106
|
-
inactive_variant_idx:
|
107
|
-
rules: [
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
113
|
+
inactive_variant_idx: 1,
|
114
|
+
rules: [
|
115
|
+
Prefab::Rule.new(
|
116
|
+
variant_weights: [
|
117
|
+
Prefab::VariantWeight.new(weight: 1000,
|
118
|
+
variant_idx: 2)
|
119
|
+
],
|
120
|
+
criteria: Prefab::Criteria.new(
|
121
|
+
operator: "LOOKUP_KEY_IN",
|
122
|
+
values: ["user:1"]
|
123
|
+
)
|
124
|
+
),
|
125
|
+
Prefab::Rule.new(
|
126
|
+
criteria: Prefab::Criteria.new(operator: Prefab::Criteria::CriteriaOperator::ALWAYS_TRUE),
|
127
|
+
variant_weights: [
|
128
|
+
Prefab::VariantWeight.new(weight: 1000,
|
129
|
+
variant_idx: 3)
|
130
|
+
]
|
112
131
|
)
|
113
|
-
|
114
|
-
|
132
|
+
|
133
|
+
],
|
115
134
|
)
|
116
135
|
|
117
136
|
assert_equal "rule target",
|
@@ -121,6 +140,50 @@ class TestFeatureFlagClient < Minitest::Test
|
|
121
140
|
|
122
141
|
end
|
123
142
|
|
143
|
+
def test_property_is_one_of
|
144
|
+
feature = "FlagName"
|
145
|
+
variants = [
|
146
|
+
Prefab::FeatureFlagVariant.new(string: "inactive"),
|
147
|
+
Prefab::FeatureFlagVariant.new(string: "rule target"),
|
148
|
+
Prefab::FeatureFlagVariant.new(string: "default"),
|
149
|
+
]
|
150
|
+
flag = Prefab::FeatureFlag.new(
|
151
|
+
active: true,
|
152
|
+
inactive_variant_idx: 1,
|
153
|
+
rules: [
|
154
|
+
Prefab::Rule.new(
|
155
|
+
variant_weights: [
|
156
|
+
Prefab::VariantWeight.new(weight: 1000,
|
157
|
+
variant_idx: 2)
|
158
|
+
],
|
159
|
+
criteria: Prefab::Criteria.new(
|
160
|
+
operator: "PROP_IS_ONE_OF",
|
161
|
+
values: ["a@example.com", "b@example.com"],
|
162
|
+
property: "email"
|
163
|
+
)
|
164
|
+
),
|
165
|
+
Prefab::Rule.new(
|
166
|
+
criteria: Prefab::Criteria.new(operator: Prefab::Criteria::CriteriaOperator::ALWAYS_TRUE),
|
167
|
+
variant_weights: [
|
168
|
+
Prefab::VariantWeight.new(weight: 1000,
|
169
|
+
variant_idx: 3)
|
170
|
+
]
|
171
|
+
)
|
172
|
+
|
173
|
+
],
|
174
|
+
)
|
175
|
+
|
176
|
+
assert_equal "default",
|
177
|
+
@client.evaluate(feature, "user:1", {email: "not@example.com"}, flag, variants)
|
178
|
+
assert_equal "default",
|
179
|
+
@client.evaluate(feature, "user:2", {}, flag, variants)
|
180
|
+
assert_equal "rule target",
|
181
|
+
@client.evaluate(feature, "user:2", {email: "b@example.com"}, flag, variants)
|
182
|
+
assert_equal "rule target",
|
183
|
+
@client.evaluate(feature, "user:2", {"email" => "b@example.com"}, flag, variants)
|
184
|
+
|
185
|
+
end
|
186
|
+
|
124
187
|
def test_segment_match?
|
125
188
|
segment = Prefab::Segment.new(
|
126
189
|
name: "Beta Group",
|
@@ -136,10 +199,10 @@ class TestFeatureFlagClient < Minitest::Test
|
|
136
199
|
def test_segments
|
137
200
|
segment_key = "prefab-segment-beta-group"
|
138
201
|
@mock_base_client.config_client.mock_this_config(segment_key,
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
202
|
+
Prefab::Segment.new(
|
203
|
+
name: "Beta Group",
|
204
|
+
includes: ["user:1"]
|
205
|
+
)
|
143
206
|
)
|
144
207
|
|
145
208
|
feature = "FlagName"
|
@@ -150,15 +213,27 @@ class TestFeatureFlagClient < Minitest::Test
|
|
150
213
|
]
|
151
214
|
flag = Prefab::FeatureFlag.new(
|
152
215
|
active: true,
|
153
|
-
inactive_variant_idx:
|
154
|
-
rules: [
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
216
|
+
inactive_variant_idx: 1,
|
217
|
+
rules: [
|
218
|
+
Prefab::Rule.new(
|
219
|
+
variant_weights: [
|
220
|
+
Prefab::VariantWeight.new(weight: 1000,
|
221
|
+
variant_idx: 2)
|
222
|
+
],
|
223
|
+
criteria: Prefab::Criteria.new(
|
224
|
+
operator: "IN_SEG",
|
225
|
+
values: [segment_key]
|
226
|
+
)
|
227
|
+
),
|
228
|
+
Prefab::Rule.new(
|
229
|
+
criteria: Prefab::Criteria.new(operator: Prefab::Criteria::CriteriaOperator::ALWAYS_TRUE),
|
230
|
+
variant_weights: [
|
231
|
+
Prefab::VariantWeight.new(weight: 1000,
|
232
|
+
variant_idx: 3)
|
233
|
+
]
|
159
234
|
)
|
160
|
-
|
161
|
-
|
235
|
+
|
236
|
+
],
|
162
237
|
)
|
163
238
|
|
164
239
|
assert_equal "rule target",
|
@@ -171,19 +246,19 @@ class TestFeatureFlagClient < Minitest::Test
|
|
171
246
|
def test_in_multiple_segments_has_or_behavior
|
172
247
|
segment_key_one = "prefab-segment-segment-1"
|
173
248
|
@mock_base_client.config_client.mock_this_config(segment_key_one,
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
249
|
+
Prefab::Segment.new(
|
250
|
+
name: "Segment-1",
|
251
|
+
includes: ["user:1", "user:2"],
|
252
|
+
excludes: ["user:3"]
|
253
|
+
)
|
179
254
|
)
|
180
255
|
segment_key_two = "prefab-segment-segment-2"
|
181
256
|
@mock_base_client.config_client.mock_this_config(segment_key_two,
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
257
|
+
Prefab::Segment.new(
|
258
|
+
name: "Segment-2",
|
259
|
+
includes: ["user:3", "user:4"],
|
260
|
+
excludes: ["user:2"]
|
261
|
+
)
|
187
262
|
)
|
188
263
|
|
189
264
|
feature = "FlagName"
|
@@ -194,15 +269,26 @@ class TestFeatureFlagClient < Minitest::Test
|
|
194
269
|
]
|
195
270
|
flag = Prefab::FeatureFlag.new(
|
196
271
|
active: true,
|
197
|
-
inactive_variant_idx:
|
198
|
-
rules: [
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
272
|
+
inactive_variant_idx: 1,
|
273
|
+
rules: [
|
274
|
+
Prefab::Rule.new(
|
275
|
+
variant_weights: [
|
276
|
+
Prefab::VariantWeight.new(weight: 1000,
|
277
|
+
variant_idx: 2)
|
278
|
+
],
|
279
|
+
criteria: Prefab::Criteria.new(
|
280
|
+
operator: "IN_SEG",
|
281
|
+
values: [segment_key_one, segment_key_two]
|
282
|
+
)
|
283
|
+
),
|
284
|
+
Prefab::Rule.new(
|
285
|
+
criteria: Prefab::Criteria.new(operator: Prefab::Criteria::CriteriaOperator::ALWAYS_TRUE),
|
286
|
+
variant_weights: [
|
287
|
+
Prefab::VariantWeight.new(weight: 1000,
|
288
|
+
variant_idx: 3)
|
289
|
+
]
|
203
290
|
)
|
204
|
-
|
205
|
-
default: Prefab::VariantDistribution.new(variant_idx: 2)
|
291
|
+
]
|
206
292
|
)
|
207
293
|
|
208
294
|
assert_equal "rule target",
|
@@ -217,4 +303,5 @@ class TestFeatureFlagClient < Minitest::Test
|
|
217
303
|
@client.evaluate(feature, "user:5", [], flag, variants)
|
218
304
|
|
219
305
|
end
|
306
|
+
|
220
307
|
end
|
data/test/test_helper.rb
CHANGED
@@ -5,10 +5,10 @@ class MockBaseClient
|
|
5
5
|
STAGING_ENV_ID = 1
|
6
6
|
PRODUCTION_ENV_ID = 2
|
7
7
|
TEST_ENV_ID = 3
|
8
|
-
attr_reader :namespace, :logger, :
|
8
|
+
attr_reader :namespace, :logger, :config_client
|
9
|
+
|
10
|
+
def initialize(namespace: "")
|
9
11
|
|
10
|
-
def initialize(project_env_id: TEST_ENV_ID, namespace: "")
|
11
|
-
@project_env_id = project_env_id
|
12
12
|
@namespace = namespace
|
13
13
|
@logger = Logger.new($stdout)
|
14
14
|
@config_client = MockConfigClient.new
|
@@ -18,8 +18,8 @@ class MockBaseClient
|
|
18
18
|
1
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
22
|
-
@
|
21
|
+
def log
|
22
|
+
@logger
|
23
23
|
end
|
24
24
|
|
25
25
|
def log_internal level, message
|
@@ -52,3 +52,18 @@ class MockConfigLoader
|
|
52
52
|
def calc_config
|
53
53
|
end
|
54
54
|
end
|
55
|
+
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def default_ff_rule(variant_idx)
|
60
|
+
[
|
61
|
+
Prefab::Rule.new(
|
62
|
+
criteria: Prefab::Criteria.new(operator: Prefab::Criteria::CriteriaOperator::ALWAYS_TRUE),
|
63
|
+
variant_weights: [
|
64
|
+
Prefab::VariantWeight.new(weight: 1000,
|
65
|
+
variant_idx: variant_idx)
|
66
|
+
]
|
67
|
+
)
|
68
|
+
]
|
69
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prefab-cloud-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeff Dwyer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-05-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|