prefab-cloud-ruby 0.24.6 → 1.0.1
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/CHANGELOG.md +13 -0
- data/VERSION +1 -1
- data/lib/prefab/client.rb +6 -45
- data/lib/prefab/config_client.rb +2 -4
- data/lib/prefab/config_resolver.rb +2 -1
- data/lib/prefab/config_value_wrapper.rb +1 -1
- data/lib/prefab/context.rb +4 -0
- data/lib/prefab/feature_flag_client.rb +0 -2
- data/lib/prefab/options.rb +37 -41
- data/lib/prefab-cloud-ruby.rb +0 -3
- data/prefab-cloud-ruby.gemspec +4 -7
- data/test/integration_test.rb +1 -3
- data/test/integration_test_helpers.rb +0 -1
- data/test/support/common_helpers.rb +76 -7
- data/test/support/mock_base_client.rb +2 -2
- data/test/test_client.rb +130 -71
- data/test/test_config_client.rb +1 -0
- data/test/test_config_loader.rb +1 -0
- data/test/test_config_value_wrapper.rb +42 -0
- data/test/test_context.rb +1 -0
- data/test/test_context_shape_aggregator.rb +11 -0
- data/test/test_criteria_evaluator.rb +1 -0
- data/test/test_integration.rb +6 -4
- data/test/test_log_path_aggregator.rb +9 -1
- data/test/test_logger.rb +1 -0
- data/test/test_options.rb +29 -6
- metadata +3 -6
- data/lib/prefab/evaluated_keys_aggregator.rb +0 -41
- data/lib/prefab/noop_cache.rb +0 -15
- data/lib/prefab/noop_stats.rb +0 -8
- data/test/test_evaluated_keys_aggregator.rb +0 -54
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eb91b8be5a01c3bab3312d277a27117c0449713623e1605863486c3809cff0cd
|
4
|
+
data.tar.gz: 3deaeb9fcb458ef461771865463c5163f8b5c6833a45d1e7af4ec3474f5d04bd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2555fc0364fef33c11e3e7866a00ab5df766e859c73baedbb39712ddceb07ec96d0216c5e79dd7336e40951de85b773100864356fd24423f3257a8e3ab40e073
|
7
|
+
data.tar.gz: 44c9813ede3c95db9820e0247c71c7e4a1e0e21b5981d074025baf546e5f3e7de6362744acedd831437e3b10ca747cdb73ec596ec1394b82ff7e70481ab57a52
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 1.0.1 - 2023-08-17
|
4
|
+
|
5
|
+
- Bug fix for StringList w/ ExampleContextsAggregator (#141)
|
6
|
+
|
7
|
+
## 1.0.0 - 2023-08-10
|
8
|
+
|
9
|
+
- Removed EvaluatedKeysAggregator (#137)
|
10
|
+
- Change `collect_evaluation_summaries` default to true (#136)
|
11
|
+
- Removed some backwards compatibility shims (#133)
|
12
|
+
- Standardizing options (#132)
|
13
|
+
- Note that the default value for `context_upload_mode` is `:periodic_example` which means example contexts will be collected.
|
14
|
+
This enables easy variant override assignment in our UI. More at https://prefab.cloud/blog/feature-flag-variant-assignment/
|
15
|
+
|
3
16
|
## 0.24.6 - 2023-07-31
|
4
17
|
|
5
18
|
- Logger Client compatibility (#129)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
1.0.1
|
data/lib/prefab/client.rb
CHANGED
@@ -7,12 +7,10 @@ module Prefab
|
|
7
7
|
MAX_SLEEP_SEC = 10
|
8
8
|
BASE_SLEEP_SEC = 0.5
|
9
9
|
|
10
|
-
attr_reader :
|
10
|
+
attr_reader :namespace, :interceptor, :api_key, :prefab_api_url, :options, :instance_hash
|
11
11
|
|
12
12
|
def initialize(options = Prefab::Options.new)
|
13
13
|
@options = options.is_a?(Prefab::Options) ? options : Prefab::Options.new(options)
|
14
|
-
@shared_cache = @options.shared_cache
|
15
|
-
@stats = @options.stats
|
16
14
|
@namespace = @options.namespace
|
17
15
|
@stubs = {}
|
18
16
|
@instance_hash = UUID.new.generate
|
@@ -32,11 +30,6 @@ module Prefab
|
|
32
30
|
config_client
|
33
31
|
end
|
34
32
|
|
35
|
-
def with_log_context(_lookup_key, properties, &block)
|
36
|
-
warn '[DEPRECATION] `$prefab.with_log_context` is deprecated. Please use `with_context` instead.'
|
37
|
-
with_context(properties, &block)
|
38
|
-
end
|
39
|
-
|
40
33
|
def with_context(properties, &block)
|
41
34
|
Prefab::Context.with_context(properties, &block)
|
42
35
|
end
|
@@ -73,13 +66,6 @@ module Prefab
|
|
73
66
|
sync_interval: @options.collect_sync_interval)
|
74
67
|
end
|
75
68
|
|
76
|
-
def evaluated_keys_aggregator
|
77
|
-
return nil if @options.collect_max_keys <= 0
|
78
|
-
|
79
|
-
@evaluated_keys_aggregator ||= EvaluatedKeysAggregator.new(client: self, max_keys: @options.collect_max_keys,
|
80
|
-
sync_interval: @options.collect_sync_interval)
|
81
|
-
end
|
82
|
-
|
83
69
|
def example_contexts_aggregator
|
84
70
|
return nil if @options.collect_max_example_contexts <= 0
|
85
71
|
|
@@ -117,19 +103,15 @@ module Prefab
|
|
117
103
|
log.log_internal msg, path, nil, level
|
118
104
|
end
|
119
105
|
|
120
|
-
def enabled?(feature_name,
|
121
|
-
|
122
|
-
|
123
|
-
feature_flag_client.feature_is_on_for?(feature_name, properties)
|
106
|
+
def enabled?(feature_name, jit_context = NO_DEFAULT_PROVIDED)
|
107
|
+
feature_flag_client.feature_is_on_for?(feature_name, jit_context)
|
124
108
|
end
|
125
109
|
|
126
|
-
def get(key,
|
110
|
+
def get(key, default = NO_DEFAULT_PROVIDED, jit_context = NO_DEFAULT_PROVIDED)
|
127
111
|
if is_ff?(key)
|
128
|
-
|
129
|
-
|
130
|
-
feature_flag_client.get(key, properties, default: ff_default)
|
112
|
+
feature_flag_client.get(key, jit_context, default: default)
|
131
113
|
else
|
132
|
-
config_client.get(key,
|
114
|
+
config_client.get(key, default, jit_context)
|
133
115
|
end
|
134
116
|
end
|
135
117
|
|
@@ -161,26 +143,5 @@ module Prefab
|
|
161
143
|
|
162
144
|
raw && raw.allowable_values.any?
|
163
145
|
end
|
164
|
-
|
165
|
-
# The goal here is to ease transition from the old API to the new one. The
|
166
|
-
# old API had a lookup_key parameter that is deprecated. This method
|
167
|
-
# handles the transition by checking if the first parameter is a string and
|
168
|
-
# if so, it is assumed to be the lookup_key and a deprecation warning is
|
169
|
-
# issued and we know the second argument is the properties. If the first
|
170
|
-
# parameter is a hash, you're on the new API and no further action is
|
171
|
-
# required.
|
172
|
-
def handle_positional_arguments(lookup_key, properties, method)
|
173
|
-
# handle JIT context
|
174
|
-
if lookup_key.is_a?(Hash) && properties == NO_DEFAULT_PROVIDED
|
175
|
-
properties = lookup_key
|
176
|
-
lookup_key = nil
|
177
|
-
end
|
178
|
-
|
179
|
-
if lookup_key.is_a?(String)
|
180
|
-
warn "[DEPRECATION] `$prefab.#{method}`'s lookup_key argument is deprecated. Please remove it or use context instead."
|
181
|
-
end
|
182
|
-
|
183
|
-
[lookup_key, properties]
|
184
|
-
end
|
185
146
|
end
|
186
147
|
end
|
data/lib/prefab/config_client.rb
CHANGED
@@ -58,14 +58,13 @@ module Prefab
|
|
58
58
|
def get(key, default = NO_DEFAULT_PROVIDED, properties = NO_DEFAULT_PROVIDED)
|
59
59
|
context = @config_resolver.make_context(properties)
|
60
60
|
|
61
|
-
if
|
61
|
+
if !context.blank? && @base_client.example_contexts_aggregator
|
62
62
|
@base_client.example_contexts_aggregator.record(context)
|
63
63
|
end
|
64
64
|
|
65
65
|
evaluation = _get(key, context)
|
66
66
|
|
67
67
|
@base_client.context_shape_aggregator&.push(context)
|
68
|
-
@base_client.evaluated_keys_aggregator&.push(key)
|
69
68
|
|
70
69
|
if evaluation
|
71
70
|
evaluation.report_and_return(@base_client.evaluation_summary_aggregator)
|
@@ -89,7 +88,7 @@ module Prefab
|
|
89
88
|
end
|
90
89
|
|
91
90
|
def _get(key, properties)
|
92
|
-
# wait timeout sec for the
|
91
|
+
# wait timeout sec for the initialization to be complete
|
93
92
|
@initialized_future.value(@options.initialization_timeout_sec)
|
94
93
|
if @initialized_future.incomplete?
|
95
94
|
unless @options.on_init_failure == Prefab::Options::ON_INITIALIZATION_FAILURE::RETURN
|
@@ -157,7 +156,6 @@ module Prefab
|
|
157
156
|
@base_client.log_internal ::Logger::DEBUG,
|
158
157
|
"Checkpoint with highwater id #{@config_loader.highwater_mark} from #{source}. No changes.", 'load_configs'
|
159
158
|
end
|
160
|
-
@base_client.stats.increment('prefab.config.checkpoint.load')
|
161
159
|
@config_resolver.update
|
162
160
|
finish_init!(source)
|
163
161
|
end
|
@@ -10,6 +10,7 @@ module Prefab
|
|
10
10
|
@config_loader = config_loader
|
11
11
|
@project_env_id = 0 # we don't know this yet, it is set from the API results
|
12
12
|
@base_client = base_client
|
13
|
+
@on_update = nil
|
13
14
|
make_local
|
14
15
|
end
|
15
16
|
|
@@ -54,7 +55,7 @@ module Prefab
|
|
54
55
|
end
|
55
56
|
|
56
57
|
def make_context(properties)
|
57
|
-
if properties == NO_DEFAULT_PROVIDED
|
58
|
+
if properties == NO_DEFAULT_PROVIDED || properties.nil?
|
58
59
|
Context.current
|
59
60
|
elsif properties.is_a?(Context)
|
60
61
|
properties
|
@@ -9,7 +9,7 @@ module Prefab
|
|
9
9
|
when TrueClass, FalseClass
|
10
10
|
PrefabProto::ConfigValue.new(bool: value)
|
11
11
|
when Array
|
12
|
-
PrefabProto::ConfigValue.new(string_list:
|
12
|
+
PrefabProto::ConfigValue.new(string_list: PrefabProto::StringList.new(values: value.map(&:to_s)))
|
13
13
|
else
|
14
14
|
PrefabProto::ConfigValue.new(string: value.to_s)
|
15
15
|
end
|
data/lib/prefab/context.rb
CHANGED
@@ -11,8 +11,6 @@ module Prefab
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def feature_is_on_for?(feature_name, properties)
|
14
|
-
@base_client.stats.increment('prefab.featureflag.on', tags: ["feature:#{feature_name}"])
|
15
|
-
|
16
14
|
variant = @base_client.config_client.get(feature_name, false, properties)
|
17
15
|
|
18
16
|
is_on?(variant)
|
data/lib/prefab/options.rb
CHANGED
@@ -7,8 +7,6 @@ module Prefab
|
|
7
7
|
attr_reader :logdev
|
8
8
|
attr_reader :log_prefix
|
9
9
|
attr_reader :log_formatter
|
10
|
-
attr_reader :stats
|
11
|
-
attr_reader :shared_cache
|
12
10
|
attr_reader :namespace
|
13
11
|
attr_reader :prefab_api_url
|
14
12
|
attr_reader :on_no_default
|
@@ -33,22 +31,21 @@ module Prefab
|
|
33
31
|
}
|
34
32
|
|
35
33
|
module ON_INITIALIZATION_FAILURE
|
36
|
-
RAISE =
|
37
|
-
RETURN =
|
34
|
+
RAISE = :raise
|
35
|
+
RETURN = :return
|
38
36
|
end
|
39
37
|
|
40
38
|
module ON_NO_DEFAULT
|
41
|
-
RAISE =
|
42
|
-
RETURN_NIL =
|
39
|
+
RAISE = :raise
|
40
|
+
RETURN_NIL = :return_nil
|
43
41
|
end
|
44
42
|
|
45
43
|
module DATASOURCES
|
46
|
-
ALL =
|
47
|
-
LOCAL_ONLY =
|
44
|
+
ALL = :all
|
45
|
+
LOCAL_ONLY = :local_only
|
48
46
|
end
|
49
47
|
|
50
48
|
DEFAULT_MAX_PATHS = 1_000
|
51
|
-
DEFAULT_MAX_CONTEXT_KEYS = 100_000
|
52
49
|
DEFAULT_MAX_KEYS = 100_000
|
53
50
|
DEFAULT_MAX_EXAMPLE_CONTEXTS = 100_000
|
54
51
|
DEFAULT_MAX_EVAL_SUMMARIES = 100_000
|
@@ -56,37 +53,28 @@ module Prefab
|
|
56
53
|
private def init(
|
57
54
|
api_key: ENV['PREFAB_API_KEY'],
|
58
55
|
logdev: $stdout,
|
59
|
-
stats: NoopStats.new, # receives increment("prefab.limitcheck", {:tags=>["policy_group:page_view", "pass:true"]})
|
60
|
-
shared_cache: NoopCache.new, # Something that quacks like Rails.cache ideally memcached
|
61
56
|
namespace: '',
|
62
57
|
log_formatter: DEFAULT_LOG_FORMATTER,
|
63
58
|
log_prefix: nil,
|
64
59
|
prefab_api_url: ENV['PREFAB_API_URL'] || 'https://api.prefab.cloud',
|
65
60
|
on_no_default: ON_NO_DEFAULT::RAISE, # options :raise, :warn_and_return_nil,
|
66
61
|
initialization_timeout_sec: 10, # how long to wait before on_init_failure
|
67
|
-
on_init_failure: ON_INITIALIZATION_FAILURE::RAISE,
|
68
|
-
# new_config_callback: nil, #callback method
|
69
|
-
# live_override_url: nil,
|
62
|
+
on_init_failure: ON_INITIALIZATION_FAILURE::RAISE,
|
70
63
|
prefab_datasources: ENV['PREFAB_DATASOURCES'] == 'LOCAL_ONLY' ? DATASOURCES::LOCAL_ONLY : DATASOURCES::ALL,
|
71
64
|
prefab_config_override_dir: Dir.home,
|
72
|
-
prefab_config_classpath_dir: '.',
|
65
|
+
prefab_config_classpath_dir: '.', # where to load local overrides
|
73
66
|
prefab_envs: ENV['PREFAB_ENVS'].nil? ? [] : ENV['PREFAB_ENVS'].split(','),
|
74
|
-
|
67
|
+
collect_logger_counts: true,
|
75
68
|
collect_max_paths: DEFAULT_MAX_PATHS,
|
76
69
|
collect_sync_interval: nil,
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
collect_max_example_contexts: DEFAULT_MAX_EXAMPLE_CONTEXTS,
|
83
|
-
collect_evaluation_summaries: false,
|
84
|
-
collect_max_evaluation_summaries: DEFAULT_MAX_EVAL_SUMMARIES
|
70
|
+
context_upload_mode: :periodic_example, # :periodic_example, :shape_only, :none
|
71
|
+
context_max_size: DEFAULT_MAX_EVAL_SUMMARIES,
|
72
|
+
collect_evaluation_summaries: true,
|
73
|
+
collect_max_evaluation_summaries: DEFAULT_MAX_EVAL_SUMMARIES,
|
74
|
+
allow_telemetry_in_local_mode: false
|
85
75
|
)
|
86
76
|
@api_key = api_key
|
87
77
|
@logdev = logdev
|
88
|
-
@stats = stats
|
89
|
-
@shared_cache = shared_cache
|
90
78
|
@namespace = namespace
|
91
79
|
@log_formatter = log_formatter
|
92
80
|
@log_prefix = log_prefix
|
@@ -98,17 +86,31 @@ module Prefab
|
|
98
86
|
@prefab_config_classpath_dir = prefab_config_classpath_dir
|
99
87
|
@prefab_config_override_dir = prefab_config_override_dir
|
100
88
|
@prefab_envs = Array(prefab_envs)
|
101
|
-
@
|
89
|
+
@collect_logger_counts = collect_logger_counts
|
102
90
|
@collect_max_paths = collect_max_paths
|
103
91
|
@collect_sync_interval = collect_sync_interval
|
104
|
-
@collect_shapes = collect_shapes
|
105
|
-
@collect_max_shapes = collect_max_shapes
|
106
|
-
@collect_keys = collect_keys
|
107
|
-
@collect_max_keys = collect_max_keys
|
108
|
-
@collect_example_contexts = collect_example_contexts
|
109
|
-
@collect_max_example_contexts = collect_max_example_contexts
|
110
92
|
@collect_evaluation_summaries = collect_evaluation_summaries
|
111
93
|
@collect_max_evaluation_summaries = collect_max_evaluation_summaries
|
94
|
+
@allow_telemetry_in_local_mode = allow_telemetry_in_local_mode
|
95
|
+
|
96
|
+
# defaults that may be overridden by context_upload_mode
|
97
|
+
@collect_shapes = false
|
98
|
+
@collect_max_shapes = 0
|
99
|
+
@collect_example_contexts = false
|
100
|
+
@collect_max_example_contexts = 0
|
101
|
+
|
102
|
+
case context_upload_mode
|
103
|
+
when :none
|
104
|
+
# do nothing
|
105
|
+
when :periodic_example
|
106
|
+
@collect_example_contexts = true
|
107
|
+
@collect_max_example_contexts = context_max_size
|
108
|
+
when :shape_only
|
109
|
+
@collect_shapes = true
|
110
|
+
@collect_max_shapes = context_max_size
|
111
|
+
else
|
112
|
+
raise "Unknown context_upload_mode #{context_upload_mode}. Please provide :periodic_example, :shape_only, or :none."
|
113
|
+
end
|
112
114
|
end
|
113
115
|
|
114
116
|
def initialize(options = {})
|
@@ -120,7 +122,7 @@ module Prefab
|
|
120
122
|
end
|
121
123
|
|
122
124
|
def collect_max_paths
|
123
|
-
return 0 unless telemetry_allowed?(@
|
125
|
+
return 0 unless telemetry_allowed?(@collect_logger_counts)
|
124
126
|
|
125
127
|
@collect_max_paths
|
126
128
|
end
|
@@ -131,12 +133,6 @@ module Prefab
|
|
131
133
|
@collect_max_shapes
|
132
134
|
end
|
133
135
|
|
134
|
-
def collect_max_keys
|
135
|
-
return 0 unless telemetry_allowed?(@collect_keys)
|
136
|
-
|
137
|
-
@collect_max_keys
|
138
|
-
end
|
139
|
-
|
140
136
|
def collect_max_example_contexts
|
141
137
|
return 0 unless telemetry_allowed?(@collect_example_contexts)
|
142
138
|
|
@@ -157,7 +153,7 @@ module Prefab
|
|
157
153
|
private
|
158
154
|
|
159
155
|
def telemetry_allowed?(option)
|
160
|
-
option && !local_only? ||
|
156
|
+
option && (!local_only? || @allow_telemetry_in_local_mode)
|
161
157
|
end
|
162
158
|
|
163
159
|
def remove_trailing_slash(url)
|
data/lib/prefab-cloud-ruby.rb
CHANGED
@@ -22,7 +22,6 @@ require 'prefab/options'
|
|
22
22
|
require 'prefab/internal_logger'
|
23
23
|
require 'prefab/rate_limit_cache'
|
24
24
|
require 'prefab/context_shape_aggregator'
|
25
|
-
require 'prefab/evaluated_keys_aggregator'
|
26
25
|
require 'prefab/example_contexts_aggregator'
|
27
26
|
require 'prefab/evaluation_summary_aggregator'
|
28
27
|
require 'prefab/log_path_aggregator'
|
@@ -43,6 +42,4 @@ require 'prefab/logger_client'
|
|
43
42
|
require 'prefab/client'
|
44
43
|
require 'prefab/config_client'
|
45
44
|
require 'prefab/feature_flag_client'
|
46
|
-
require 'prefab/noop_cache'
|
47
|
-
require 'prefab/noop_stats'
|
48
45
|
require 'prefab/murmer3'
|
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 1.0.1 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 = "1.0.1"
|
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 = "2023-
|
14
|
+
s.date = "2023-08-17"
|
15
15
|
s.description = "Feature Flags, Live Config, and Dynamic Log Levels as a service".freeze
|
16
16
|
s.email = "jdwyer@prefab.cloud".freeze
|
17
17
|
s.executables = ["console".freeze]
|
@@ -52,7 +52,6 @@ Gem::Specification.new do |s|
|
|
52
52
|
"lib/prefab/errors/initialization_timeout_error.rb",
|
53
53
|
"lib/prefab/errors/invalid_api_key_error.rb",
|
54
54
|
"lib/prefab/errors/missing_default_error.rb",
|
55
|
-
"lib/prefab/evaluated_keys_aggregator.rb",
|
56
55
|
"lib/prefab/evaluation.rb",
|
57
56
|
"lib/prefab/evaluation_summary_aggregator.rb",
|
58
57
|
"lib/prefab/example_contexts_aggregator.rb",
|
@@ -64,8 +63,6 @@ Gem::Specification.new do |s|
|
|
64
63
|
"lib/prefab/log_path_aggregator.rb",
|
65
64
|
"lib/prefab/logger_client.rb",
|
66
65
|
"lib/prefab/murmer3.rb",
|
67
|
-
"lib/prefab/noop_cache.rb",
|
68
|
-
"lib/prefab/noop_stats.rb",
|
69
66
|
"lib/prefab/options.rb",
|
70
67
|
"lib/prefab/periodic_sync.rb",
|
71
68
|
"lib/prefab/rate_limit_cache.rb",
|
@@ -89,11 +86,11 @@ Gem::Specification.new do |s|
|
|
89
86
|
"test/test_config_loader.rb",
|
90
87
|
"test/test_config_resolver.rb",
|
91
88
|
"test/test_config_value_unwrapper.rb",
|
89
|
+
"test/test_config_value_wrapper.rb",
|
92
90
|
"test/test_context.rb",
|
93
91
|
"test/test_context_shape.rb",
|
94
92
|
"test/test_context_shape_aggregator.rb",
|
95
93
|
"test/test_criteria_evaluator.rb",
|
96
|
-
"test/test_evaluated_keys_aggregator.rb",
|
97
94
|
"test/test_evaluation_summary_aggregator.rb",
|
98
95
|
"test/test_example_contexts_aggregator.rb",
|
99
96
|
"test/test_exponential_backoff.rb",
|
data/test/integration_test.rb
CHANGED
@@ -18,8 +18,6 @@ class IntegrationTest
|
|
18
18
|
:raise
|
19
19
|
elsif @expected[:value].nil?
|
20
20
|
:nil
|
21
|
-
elsif @func == :feature_is_on_for?
|
22
|
-
:feature_flag
|
23
21
|
else
|
24
22
|
:simple_equality
|
25
23
|
end
|
@@ -60,7 +58,7 @@ class IntegrationTest
|
|
60
58
|
end
|
61
59
|
|
62
60
|
def parse_ff_input(input)
|
63
|
-
[input['flag'], input['context']]
|
61
|
+
[input['flag'], input['default'], input['context']]
|
64
62
|
end
|
65
63
|
|
66
64
|
def parse_expected(expected)
|
@@ -1,6 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module CommonHelpers
|
4
|
+
require 'timecop'
|
5
|
+
|
6
|
+
def setup
|
7
|
+
$oldstderr, $stderr = $stderr, StringIO.new
|
8
|
+
|
9
|
+
$logs = nil
|
10
|
+
Timecop.freeze('2023-08-09 15:18:12 -0400')
|
11
|
+
end
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
if $logs && !$logs.string.empty?
|
15
|
+
raise "Unexpected logs. Handle logs with assert_only_expected_logs or assert_logged\n\n#{$logs.string}"
|
16
|
+
end
|
17
|
+
|
18
|
+
if $stderr != $oldstderr && !$stderr.string.empty?
|
19
|
+
# we ignore 2.X because of the number of `instance variable @xyz not initialized` warnings
|
20
|
+
if !RUBY_VERSION.start_with?('2.')
|
21
|
+
raise "Unexpected stderr. Handle stderr with assert_stderr\n\n#{$stderr.string}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
$stderr = $oldstderr
|
26
|
+
|
27
|
+
Timecop.return
|
28
|
+
end
|
29
|
+
|
4
30
|
def with_env(key, value, &block)
|
5
31
|
old_value = ENV.fetch(key, nil)
|
6
32
|
|
@@ -18,10 +44,16 @@ module CommonHelpers
|
|
18
44
|
}.freeze
|
19
45
|
|
20
46
|
def new_client(overrides = {})
|
47
|
+
$logs ||= StringIO.new
|
48
|
+
|
21
49
|
config = overrides.delete(:config)
|
22
50
|
project_env_id = overrides.delete(:project_env_id)
|
23
51
|
|
24
|
-
options = Prefab::Options.new(
|
52
|
+
options = Prefab::Options.new(
|
53
|
+
**DEFAULT_NEW_CLIENT_OPTIONS.merge(
|
54
|
+
overrides.merge(logdev: $logs)
|
55
|
+
)
|
56
|
+
)
|
25
57
|
|
26
58
|
Prefab::Client.new(options).tap do |client|
|
27
59
|
inject_config(client, config) if config
|
@@ -51,26 +83,31 @@ module CommonHelpers
|
|
51
83
|
FakeResponse = Struct.new(:status, :body)
|
52
84
|
|
53
85
|
def wait_for_post_requests(client, max_wait: 2, sleep_time: 0.01)
|
54
|
-
|
86
|
+
# we use ivars to avoid re-mocking the post method on subsequent calls
|
87
|
+
client.instance_variable_set("@_requests", [])
|
55
88
|
|
56
|
-
client.
|
57
|
-
|
89
|
+
if !client.instance_variable_get("@_already_faked_post")
|
90
|
+
client.define_singleton_method(:post) do |*params|
|
91
|
+
@_requests.push(params)
|
58
92
|
|
59
|
-
|
93
|
+
FakeResponse.new(200, '')
|
94
|
+
end
|
60
95
|
end
|
61
96
|
|
97
|
+
client.instance_variable_set("@_already_faked_post", true)
|
98
|
+
|
62
99
|
yield
|
63
100
|
|
64
101
|
# let the flush thread run
|
65
102
|
wait_time = 0
|
66
|
-
while
|
103
|
+
while client.instance_variable_get("@_requests").empty?
|
67
104
|
wait_time += sleep_time
|
68
105
|
sleep sleep_time
|
69
106
|
|
70
107
|
raise "Waited #{max_wait} seconds for the flush thread to run, but it never did" if wait_time > max_wait
|
71
108
|
end
|
72
109
|
|
73
|
-
|
110
|
+
client.instance_variable_get("@_requests")
|
74
111
|
end
|
75
112
|
|
76
113
|
def assert_summary(client, data)
|
@@ -102,4 +139,36 @@ module CommonHelpers
|
|
102
139
|
def context(properties)
|
103
140
|
Prefab::Context.new(properties)
|
104
141
|
end
|
142
|
+
|
143
|
+
def assert_only_expected_logs
|
144
|
+
assert_equal "WARN 2023-08-09 15:18:12 -0400: cloud.prefab.client No success loading checkpoints\n", $logs.string
|
145
|
+
# mark nil to indicate we handled it
|
146
|
+
$logs = nil
|
147
|
+
end
|
148
|
+
|
149
|
+
def assert_logged(expected)
|
150
|
+
# we do a uniq here because logging can happen in a separate thread so the
|
151
|
+
# number of times a log might happen could be slightly variable.
|
152
|
+
assert_equal expected, $logs.string.split("\n").uniq
|
153
|
+
# mark nil to indicate we handled it
|
154
|
+
$logs = nil
|
155
|
+
end
|
156
|
+
|
157
|
+
def assert_stderr(expected)
|
158
|
+
assert ($stderr.string.split("\n").uniq & expected).size > 0
|
159
|
+
|
160
|
+
# Ruby 2.X has a lot of warnings about instance variables not being
|
161
|
+
# initialized so we don't try to assert on stderr for those versions.
|
162
|
+
# Instead we just stop after asserting that our expected errors are
|
163
|
+
# included in the output.
|
164
|
+
if RUBY_VERSION.start_with?('2.')
|
165
|
+
puts $stderr.string
|
166
|
+
return
|
167
|
+
end
|
168
|
+
|
169
|
+
assert_equal expected, $stderr.string.split("\n")
|
170
|
+
|
171
|
+
# restore since we've handled it
|
172
|
+
$stderr = $oldstderr
|
173
|
+
end
|
105
174
|
end
|
data/test/test_client.rb
CHANGED
@@ -24,34 +24,33 @@ class TestClient < Minitest::Test
|
|
24
24
|
]
|
25
25
|
)
|
26
26
|
|
27
|
-
def setup
|
28
|
-
@client = new_client
|
29
|
-
end
|
30
|
-
|
31
27
|
def test_get
|
32
28
|
_, err = capture_io do
|
33
|
-
|
34
|
-
assert_equal '
|
35
|
-
assert_equal
|
29
|
+
client = new_client
|
30
|
+
assert_equal 'default', client.get('does.not.exist', 'default')
|
31
|
+
assert_equal 'test sample value', client.get('sample')
|
32
|
+
assert_equal 123, client.get('sample_int')
|
36
33
|
end
|
37
34
|
assert_equal '', err
|
38
35
|
end
|
39
36
|
|
40
37
|
def test_get_with_default
|
38
|
+
client = new_client
|
41
39
|
# A `false` value is not replaced with the default
|
42
|
-
assert_equal false,
|
40
|
+
assert_equal false, client.get('false_value', 'red')
|
43
41
|
|
44
42
|
# A falsy value is not replaced with the default
|
45
|
-
assert_equal 0,
|
43
|
+
assert_equal 0, client.get('zero_value', 'red')
|
46
44
|
|
47
45
|
# A missing value returns the default
|
48
|
-
assert_equal 'buckets',
|
46
|
+
assert_equal 'buckets', client.get('missing_value', 'buckets')
|
49
47
|
end
|
50
48
|
|
51
49
|
def test_get_with_missing_default
|
50
|
+
client = new_client
|
52
51
|
# it raises by default
|
53
52
|
err = assert_raises(Prefab::Errors::MissingDefaultError) do
|
54
|
-
assert_nil
|
53
|
+
assert_nil client.get('missing_value')
|
55
54
|
end
|
56
55
|
|
57
56
|
assert_match(/No value found for key/, err.message)
|
@@ -63,47 +62,81 @@ class TestClient < Minitest::Test
|
|
63
62
|
end
|
64
63
|
|
65
64
|
def test_enabled
|
66
|
-
|
67
|
-
assert_equal
|
68
|
-
assert_equal
|
69
|
-
assert_equal false,
|
65
|
+
client = new_client
|
66
|
+
assert_equal false, client.enabled?('does_not_exist')
|
67
|
+
assert_equal true, client.enabled?('enabled_flag')
|
68
|
+
assert_equal false, client.enabled?('disabled_flag')
|
69
|
+
assert_equal false, client.enabled?('flag_with_a_value')
|
70
70
|
end
|
71
71
|
|
72
72
|
def test_ff_enabled_with_user_key_match
|
73
|
-
|
74
|
-
|
75
|
-
|
73
|
+
client = new_client
|
74
|
+
|
75
|
+
ctx = { user: { key: 'jimmy' } }
|
76
|
+
assert_equal false, client.enabled?('user_key_match', ctx)
|
77
|
+
assert_equal false, Prefab::Context.with_context(ctx) { client.enabled?('user_key_match') }
|
78
|
+
|
79
|
+
ctx = { user: { key: 'abc123' } }
|
80
|
+
assert_equal true, client.enabled?('user_key_match', ctx)
|
81
|
+
assert_equal true, Prefab::Context.with_context(ctx) { client.enabled?('user_key_match') }
|
82
|
+
|
83
|
+
ctx = { user: { key: 'xyz987' } }
|
84
|
+
assert_equal true, client.enabled?('user_key_match', ctx)
|
85
|
+
assert_equal true, Prefab::Context.with_context(ctx) { client.enabled?('user_key_match') }
|
76
86
|
end
|
77
87
|
|
88
|
+
# NOTE: these are all `false` because we're doing a enabled? on a FF with string variants
|
89
|
+
# see test_ff_get_with_context for the raw value tests
|
78
90
|
def test_ff_enabled_with_context
|
79
|
-
|
80
|
-
|
81
|
-
|
91
|
+
client = new_client
|
92
|
+
|
93
|
+
ctx = { user: { domain: 'gmail.com' } }
|
94
|
+
assert_equal false, client.enabled?('just_my_domain', ctx)
|
95
|
+
assert_equal false, Prefab::Context.with_context(ctx) { client.enabled?('just_my_domain') }
|
96
|
+
|
97
|
+
ctx = { user: { domain: 'prefab.cloud' } }
|
98
|
+
assert_equal false, client.enabled?('just_my_domain', ctx)
|
99
|
+
assert_equal false, Prefab::Context.with_context(ctx) { client.enabled?('just_my_domain') }
|
100
|
+
|
101
|
+
ctx = { user: { domain: 'example.com' } }
|
102
|
+
assert_equal false, client.enabled?('just_my_domain', ctx)
|
103
|
+
assert_equal false, Prefab::Context.with_context(ctx) { client.enabled?('just_my_domain') }
|
82
104
|
end
|
83
105
|
|
84
106
|
def test_ff_get_with_context
|
85
|
-
|
86
|
-
|
107
|
+
client = new_client
|
108
|
+
|
109
|
+
ctx = { user: { domain: 'gmail.com' } }
|
110
|
+
assert_equal 'DEFAULT', client.get('just_my_domain', 'DEFAULT', ctx)
|
111
|
+
assert_equal 'DEFAULT', Prefab::Context.with_context(ctx) { client.get('just_my_domain', 'DEFAULT') }
|
87
112
|
|
88
|
-
|
89
|
-
|
113
|
+
ctx = { user: { domain: 'prefab.cloud' } }
|
114
|
+
assert_equal 'new-version', client.get('just_my_domain', 'DEFAULT', ctx)
|
115
|
+
assert_equal 'new-version', Prefab::Context.with_context(ctx) { client.get('just_my_domain', 'DEFAULT') }
|
116
|
+
|
117
|
+
ctx = { user: { domain: 'example.com' } }
|
118
|
+
assert_equal 'new-version', client.get('just_my_domain', 'DEFAULT', ctx)
|
119
|
+
assert_equal 'new-version', Prefab::Context.with_context(ctx) { client.get('just_my_domain', 'DEFAULT') }
|
90
120
|
end
|
91
121
|
|
92
122
|
def test_deprecated_no_dot_notation_ff_enabled_with_jit_context
|
123
|
+
client = new_client
|
93
124
|
# with no lookup key
|
94
|
-
assert_equal false,
|
95
|
-
assert_equal true,
|
96
|
-
assert_equal true,
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
125
|
+
assert_equal false, client.enabled?('deprecated_no_dot_notation', { domain: 'gmail.com' })
|
126
|
+
assert_equal true, client.enabled?('deprecated_no_dot_notation', { domain: 'prefab.cloud' })
|
127
|
+
assert_equal true, client.enabled?('deprecated_no_dot_notation', { domain: 'example.com' })
|
128
|
+
|
129
|
+
assert_stderr [
|
130
|
+
"[DEPRECATION] Prefab contexts should be a hash with a key of the context name and a value of a hash.",
|
131
|
+
"[DEPRECATION] Prefab contexts should be a hash with a key of the context name and a value of a hash.",
|
132
|
+
"[DEPRECATION] Prefab contexts should be a hash with a key of the context name and a value of a hash."
|
133
|
+
]
|
102
134
|
end
|
103
135
|
|
104
136
|
def test_getting_feature_flag_value
|
105
|
-
|
106
|
-
assert_equal
|
137
|
+
client = new_client
|
138
|
+
assert_equal false, client.enabled?('flag_with_a_value')
|
139
|
+
assert_equal 'all-features', client.get('flag_with_a_value')
|
107
140
|
end
|
108
141
|
|
109
142
|
def test_initialization_with_an_options_object
|
@@ -134,43 +167,32 @@ class TestClient < Minitest::Test
|
|
134
167
|
fake_api_key = '123-development-yourapikey-SDK'
|
135
168
|
|
136
169
|
# it is nil by default
|
137
|
-
assert_nil
|
170
|
+
assert_nil new_client(api_key: fake_api_key).evaluation_summary_aggregator
|
138
171
|
|
139
172
|
# it is nil when local_only even if collect_max_evaluation_summaries is true
|
140
|
-
assert_nil
|
141
|
-
collect_evaluation_summaries: true).evaluation_summary_aggregator
|
173
|
+
assert_nil new_client(prefab_datasources: LOCAL_ONLY,
|
174
|
+
collect_evaluation_summaries: true, ).evaluation_summary_aggregator
|
142
175
|
|
143
176
|
# it is nil when collect_max_evaluation_summaries is false
|
144
|
-
assert_nil
|
177
|
+
assert_nil new_client(api_key: fake_api_key,
|
178
|
+
prefab_datasources: :all,
|
145
179
|
collect_evaluation_summaries: false).evaluation_summary_aggregator
|
146
180
|
|
147
181
|
# it is not nil when collect_max_evaluation_summaries is true and the datasource is not local_only
|
148
182
|
assert_equal Prefab::EvaluationSummaryAggregator,
|
149
|
-
|
150
|
-
|
183
|
+
new_client(api_key: fake_api_key,
|
184
|
+
prefab_datasources: :all,
|
185
|
+
collect_evaluation_summaries: true).evaluation_summary_aggregator.class
|
186
|
+
|
187
|
+
assert_logged [
|
188
|
+
"WARN 2023-08-09 15:18:12 -0400: cloud.prefab.client No success loading checkpoints"
|
189
|
+
]
|
151
190
|
end
|
152
191
|
|
153
192
|
def test_get_with_basic_value
|
154
|
-
config =
|
155
|
-
|
156
|
-
|
157
|
-
config_type: PrefabProto::ConfigType::CONFIG,
|
158
|
-
rows: [
|
159
|
-
DEFAULT_ROW,
|
160
|
-
PrefabProto::ConfigRow.new(
|
161
|
-
project_env_id: PROJECT_ENV_ID,
|
162
|
-
values: [
|
163
|
-
PrefabProto::ConditionalValue.new(
|
164
|
-
criteria: [PrefabProto::Criterion.new(operator: PrefabProto::Criterion::CriterionOperator::ALWAYS_TRUE)],
|
165
|
-
value: DESIRED_VALUE_CONFIG
|
166
|
-
)
|
167
|
-
]
|
168
|
-
)
|
169
|
-
]
|
170
|
-
)
|
171
|
-
|
172
|
-
client = new_client(config: config, project_env_id: PROJECT_ENV_ID, collect_evaluation_summaries: :force,
|
173
|
-
collect_example_contexts: :force)
|
193
|
+
config = basic_value_config
|
194
|
+
client = new_client(config: config, project_env_id: PROJECT_ENV_ID, collect_evaluation_summaries: true,
|
195
|
+
context_upload_mode: :periodic_example, allow_telemetry_in_local_mode: true)
|
174
196
|
|
175
197
|
assert_equal DESIRED_VALUE, client.get(config.key, IRRELEVANT, 'user' => { 'key' => 99 })
|
176
198
|
|
@@ -190,6 +212,31 @@ class TestClient < Minitest::Test
|
|
190
212
|
assert_example_contexts client, [Prefab::Context.new({ user: { 'key' => 99 } })]
|
191
213
|
end
|
192
214
|
|
215
|
+
def test_get_with_basic_value_with_context
|
216
|
+
config = basic_value_config
|
217
|
+
client = new_client(config: config, project_env_id: PROJECT_ENV_ID, collect_evaluation_summaries: true,
|
218
|
+
context_upload_mode: :periodic_example, allow_telemetry_in_local_mode: true)
|
219
|
+
|
220
|
+
client.with_context('user' => { 'key' => 99 }) do
|
221
|
+
assert_equal DESIRED_VALUE, client.get(config.key)
|
222
|
+
end
|
223
|
+
|
224
|
+
assert_summary client, {
|
225
|
+
[KEY, :CONFIG] => {
|
226
|
+
{
|
227
|
+
config_id: config.id,
|
228
|
+
config_row_index: 1,
|
229
|
+
selected_value: DESIRED_VALUE_CONFIG,
|
230
|
+
conditional_value_index: 0,
|
231
|
+
weighted_value_index: nil,
|
232
|
+
selected_index: nil
|
233
|
+
} => 1
|
234
|
+
}
|
235
|
+
}
|
236
|
+
|
237
|
+
assert_example_contexts client, [Prefab::Context.new({ user: { 'key' => 99 } })]
|
238
|
+
end
|
239
|
+
|
193
240
|
def test_get_with_weighted_values
|
194
241
|
config = PrefabProto::Config.new(
|
195
242
|
id: 123,
|
@@ -210,8 +257,8 @@ class TestClient < Minitest::Test
|
|
210
257
|
]
|
211
258
|
)
|
212
259
|
|
213
|
-
client = new_client(config: config, project_env_id: PROJECT_ENV_ID, collect_evaluation_summaries:
|
214
|
-
|
260
|
+
client = new_client(config: config, project_env_id: PROJECT_ENV_ID, collect_evaluation_summaries: true,
|
261
|
+
context_upload_mode: :periodic_example, allow_telemetry_in_local_mode: true)
|
215
262
|
|
216
263
|
2.times do
|
217
264
|
assert_equal 'abc', client.get(config.key, IRRELEVANT, 'user' => { 'key' => '1' })
|
@@ -310,7 +357,7 @@ class TestClient < Minitest::Test
|
|
310
357
|
)
|
311
358
|
|
312
359
|
client = new_client(config: [config, segment_config], project_env_id: PROJECT_ENV_ID,
|
313
|
-
collect_evaluation_summaries: :
|
360
|
+
collect_evaluation_summaries: true, context_upload_mode: :periodic_example, allow_telemetry_in_local_mode: true)
|
314
361
|
|
315
362
|
assert_equal DEFAULT_VALUE, client.get(config.key)
|
316
363
|
assert_equal DEFAULT_VALUE,
|
@@ -356,7 +403,7 @@ class TestClient < Minitest::Test
|
|
356
403
|
)
|
357
404
|
|
358
405
|
client = new_client(config: config, project_env_id: PROJECT_ENV_ID,
|
359
|
-
collect_evaluation_summaries: :
|
406
|
+
collect_evaluation_summaries: true, allow_telemetry_in_local_mode: true)
|
360
407
|
|
361
408
|
assert_equal :DEBUG, client.get(config.key, IRRELEVANT)
|
362
409
|
|
@@ -366,11 +413,23 @@ class TestClient < Minitest::Test
|
|
366
413
|
|
367
414
|
private
|
368
415
|
|
369
|
-
def
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
416
|
+
def basic_value_config
|
417
|
+
PrefabProto::Config.new(
|
418
|
+
id: 123,
|
419
|
+
key: KEY,
|
420
|
+
config_type: PrefabProto::ConfigType::CONFIG,
|
421
|
+
rows: [
|
422
|
+
DEFAULT_ROW,
|
423
|
+
PrefabProto::ConfigRow.new(
|
424
|
+
project_env_id: PROJECT_ENV_ID,
|
425
|
+
values: [
|
426
|
+
PrefabProto::ConditionalValue.new(
|
427
|
+
criteria: [PrefabProto::Criterion.new(operator: PrefabProto::Criterion::CriterionOperator::ALWAYS_TRUE)],
|
428
|
+
value: DESIRED_VALUE_CONFIG
|
429
|
+
)
|
430
|
+
]
|
431
|
+
)
|
432
|
+
]
|
433
|
+
)
|
375
434
|
end
|
376
435
|
end
|
data/test/test_config_client.rb
CHANGED
data/test/test_config_loader.rb
CHANGED
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
class TestConfigValueWrapper < Minitest::Test
|
6
|
+
def test_wrap_integer
|
7
|
+
result = Prefab::ConfigValueWrapper.wrap(42)
|
8
|
+
assert_instance_of PrefabProto::ConfigValue, result
|
9
|
+
assert_equal 42, result.int
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_wrap_float
|
13
|
+
result = Prefab::ConfigValueWrapper.wrap(3.14)
|
14
|
+
assert_instance_of PrefabProto::ConfigValue, result
|
15
|
+
assert_equal 3.14, result.double
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_wrap_boolean_true
|
19
|
+
result = Prefab::ConfigValueWrapper.wrap(true)
|
20
|
+
assert_instance_of PrefabProto::ConfigValue, result
|
21
|
+
assert_equal true, result.bool
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_wrap_boolean_false
|
25
|
+
result = Prefab::ConfigValueWrapper.wrap(false)
|
26
|
+
assert_instance_of PrefabProto::ConfigValue, result
|
27
|
+
assert_equal false, result.bool
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_wrap_array
|
31
|
+
result = Prefab::ConfigValueWrapper.wrap(['one', 'two', 'three'])
|
32
|
+
assert_instance_of PrefabProto::ConfigValue, result
|
33
|
+
assert_instance_of PrefabProto::StringList, result.string_list
|
34
|
+
assert_equal ['one', 'two', 'three'], result.string_list.values
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_wrap_string
|
38
|
+
result = Prefab::ConfigValueWrapper.wrap('hello')
|
39
|
+
assert_instance_of PrefabProto::ConfigValue, result
|
40
|
+
assert_equal 'hello', result.string
|
41
|
+
end
|
42
|
+
end
|
data/test/test_context.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'test_helper'
|
4
|
+
require 'timecop'
|
4
5
|
|
5
6
|
class TestContextShapeAggregator < Minitest::Test
|
6
7
|
DOB = Date.new
|
@@ -49,6 +50,8 @@ class TestContextShapeAggregator < Minitest::Test
|
|
49
50
|
|
50
51
|
assert_equal [['user', 'name', 2], ['user', 'email', 2], ['user', 'age', 4], ['subscription', 'plan', 2], ['subscription', 'free', 5], ['user', 'dob', 2], ['device', 'name', 2], ['device', 'os', 2], ['device', 'version', 1]],
|
51
52
|
aggregator.data.to_a
|
53
|
+
|
54
|
+
assert_only_expected_logs
|
52
55
|
end
|
53
56
|
|
54
57
|
def test_prepare_data
|
@@ -82,6 +85,7 @@ class TestContextShapeAggregator < Minitest::Test
|
|
82
85
|
}
|
83
86
|
|
84
87
|
assert_equal [], aggregator.data.to_a
|
88
|
+
assert_only_expected_logs
|
85
89
|
end
|
86
90
|
|
87
91
|
def test_sync
|
@@ -117,6 +121,12 @@ class TestContextShapeAggregator < Minitest::Test
|
|
117
121
|
])
|
118
122
|
]
|
119
123
|
], requests
|
124
|
+
|
125
|
+
|
126
|
+
assert_logged [
|
127
|
+
"WARN 2023-08-09 15:18:12 -0400: cloud.prefab.client No success loading checkpoints",
|
128
|
+
"WARN 2023-08-09 15:18:12 -0400: cloud.prefab.client Couldn't Initialize In 0. Key some.key. Returning what we have"
|
129
|
+
]
|
120
130
|
end
|
121
131
|
|
122
132
|
private
|
@@ -127,6 +137,7 @@ class TestContextShapeAggregator < Minitest::Test
|
|
127
137
|
initialization_timeout_sec: 0,
|
128
138
|
on_init_failure: Prefab::Options::ON_INITIALIZATION_FAILURE::RETURN,
|
129
139
|
api_key: '123-development-yourapikey-SDK',
|
140
|
+
context_upload_mode: :shape_only
|
130
141
|
}.merge(overrides))
|
131
142
|
end
|
132
143
|
|
data/test/test_integration.rb
CHANGED
@@ -25,11 +25,13 @@ class TestIntegration < Minitest::Test
|
|
25
25
|
assert_match(/#{it.expected[:message]}/, err.message)
|
26
26
|
when :nil
|
27
27
|
assert_nil it.test_client.send(it.func, *it.input)
|
28
|
-
when :feature_flag
|
29
|
-
flag, context = *it.input
|
30
|
-
assert_equal it.expected[:value], it.test_client.send(it.func, flag, context)
|
31
28
|
when :simple_equality
|
32
|
-
|
29
|
+
if it.func == :enabled?
|
30
|
+
flag, _default, context = *it.input
|
31
|
+
assert_equal it.expected[:value], it.test_client.send(it.func, flag, context)
|
32
|
+
else
|
33
|
+
assert_equal it.expected[:value], it.test_client.send(it.func, *it.input)
|
34
|
+
end
|
33
35
|
when :log_level
|
34
36
|
assert_equal it.expected[:value].to_sym, it.test_client.send(it.func, *it.input)
|
35
37
|
else
|
@@ -8,7 +8,8 @@ class TestLogPathAggregator < Minitest::Test
|
|
8
8
|
SLEEP_TIME = 0.01
|
9
9
|
|
10
10
|
def test_push
|
11
|
-
|
11
|
+
client = new_client
|
12
|
+
aggregator = Prefab::LogPathAggregator.new(client: client, max_paths: 2, sync_interval: 1000)
|
12
13
|
|
13
14
|
aggregator.push('test.test_log_path_aggregator.test_push.1', ::Logger::INFO)
|
14
15
|
aggregator.push('test.test_log_path_aggregator.test_push.2', ::Logger::DEBUG)
|
@@ -18,6 +19,8 @@ class TestLogPathAggregator < Minitest::Test
|
|
18
19
|
# we've reached the limit, so no more
|
19
20
|
aggregator.push('test.test_log_path_aggregator.test_push.3', ::Logger::INFO)
|
20
21
|
assert_equal 2, aggregator.data.size
|
22
|
+
|
23
|
+
assert_only_expected_logs
|
21
24
|
end
|
22
25
|
|
23
26
|
def test_sync
|
@@ -42,6 +45,11 @@ class TestLogPathAggregator < Minitest::Test
|
|
42
45
|
namespace: 'this.is.a.namespace'
|
43
46
|
)
|
44
47
|
]], requests
|
48
|
+
|
49
|
+
assert_logged [
|
50
|
+
'WARN 2023-08-09 15:18:12 -0400: cloud.prefab.client No success loading checkpoints',
|
51
|
+
'ERROR 2023-08-09 15:18:12 -0400: test.test_log_path_aggregator.test_sync here is a message'
|
52
|
+
]
|
45
53
|
end
|
46
54
|
end
|
47
55
|
|
data/test/test_logger.rb
CHANGED
data/test/test_options.rb
CHANGED
@@ -36,17 +36,40 @@ class TestOptions < Minitest::Test
|
|
36
36
|
assert_equal 0, options.collect_max_paths
|
37
37
|
end
|
38
38
|
|
39
|
-
def
|
39
|
+
def test_collect_max_paths_with_collect_logger_counts_false
|
40
40
|
options = Prefab::Options.new(collect_max_paths: 100,
|
41
|
-
|
41
|
+
collect_logger_counts: false)
|
42
42
|
assert_equal 0, options.collect_max_paths
|
43
43
|
end
|
44
44
|
|
45
45
|
def test_collect_max_evaluation_summaries
|
46
|
-
assert_equal
|
47
|
-
assert_equal
|
46
|
+
assert_equal 100_000, Prefab::Options.new.collect_max_evaluation_summaries
|
47
|
+
assert_equal 0, Prefab::Options.new(collect_evaluation_summaries: false).collect_max_evaluation_summaries
|
48
48
|
assert_equal 3,
|
49
|
-
Prefab::Options.new(
|
50
|
-
|
49
|
+
Prefab::Options.new(collect_max_evaluation_summaries: 3).collect_max_evaluation_summaries
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_context_upload_mode_periodic
|
53
|
+
options = Prefab::Options.new(context_upload_mode: :periodic_example, context_max_size: 100)
|
54
|
+
assert_equal 100, options.collect_max_example_contexts
|
55
|
+
|
56
|
+
options = Prefab::Options.new(context_upload_mode: :none)
|
57
|
+
assert_equal 0, options.collect_max_example_contexts
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_context_upload_mode_shape_only
|
61
|
+
options = Prefab::Options.new(context_upload_mode: :shape_only, context_max_size: 100)
|
62
|
+
assert_equal 100, options.collect_max_shapes
|
63
|
+
|
64
|
+
options = Prefab::Options.new(context_upload_mode: :none)
|
65
|
+
assert_equal 0, options.collect_max_shapes
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_context_upload_mode_none
|
69
|
+
options = Prefab::Options.new(context_upload_mode: :none)
|
70
|
+
assert_equal 0, options.collect_max_example_contexts
|
71
|
+
|
72
|
+
options = Prefab::Options.new(context_upload_mode: :none)
|
73
|
+
assert_equal 0, options.collect_max_shapes
|
51
74
|
end
|
52
75
|
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: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeff Dwyer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-08-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -211,7 +211,6 @@ files:
|
|
211
211
|
- lib/prefab/errors/initialization_timeout_error.rb
|
212
212
|
- lib/prefab/errors/invalid_api_key_error.rb
|
213
213
|
- lib/prefab/errors/missing_default_error.rb
|
214
|
-
- lib/prefab/evaluated_keys_aggregator.rb
|
215
214
|
- lib/prefab/evaluation.rb
|
216
215
|
- lib/prefab/evaluation_summary_aggregator.rb
|
217
216
|
- lib/prefab/example_contexts_aggregator.rb
|
@@ -223,8 +222,6 @@ files:
|
|
223
222
|
- lib/prefab/log_path_aggregator.rb
|
224
223
|
- lib/prefab/logger_client.rb
|
225
224
|
- lib/prefab/murmer3.rb
|
226
|
-
- lib/prefab/noop_cache.rb
|
227
|
-
- lib/prefab/noop_stats.rb
|
228
225
|
- lib/prefab/options.rb
|
229
226
|
- lib/prefab/periodic_sync.rb
|
230
227
|
- lib/prefab/rate_limit_cache.rb
|
@@ -248,11 +245,11 @@ files:
|
|
248
245
|
- test/test_config_loader.rb
|
249
246
|
- test/test_config_resolver.rb
|
250
247
|
- test/test_config_value_unwrapper.rb
|
248
|
+
- test/test_config_value_wrapper.rb
|
251
249
|
- test/test_context.rb
|
252
250
|
- test/test_context_shape.rb
|
253
251
|
- test/test_context_shape_aggregator.rb
|
254
252
|
- test/test_criteria_evaluator.rb
|
255
|
-
- test/test_evaluated_keys_aggregator.rb
|
256
253
|
- test/test_evaluation_summary_aggregator.rb
|
257
254
|
- test/test_example_contexts_aggregator.rb
|
258
255
|
- test/test_exponential_backoff.rb
|
@@ -1,41 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'periodic_sync'
|
4
|
-
|
5
|
-
module Prefab
|
6
|
-
class EvaluatedKeysAggregator
|
7
|
-
include Prefab::PeriodicSync
|
8
|
-
|
9
|
-
attr_reader :data
|
10
|
-
|
11
|
-
def initialize(client:, max_keys:, sync_interval:)
|
12
|
-
@max_keys = max_keys
|
13
|
-
@client = client
|
14
|
-
@name = 'evaluated_keys_aggregator'
|
15
|
-
|
16
|
-
@data = Concurrent::Set.new
|
17
|
-
|
18
|
-
start_periodic_sync(sync_interval)
|
19
|
-
end
|
20
|
-
|
21
|
-
def push(key)
|
22
|
-
return if @data.size >= @max_keys
|
23
|
-
|
24
|
-
@data.add(key)
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
def flush(to_ship, _)
|
30
|
-
pool.post do
|
31
|
-
log_internal "Uploading evaluated keys for #{to_ship.size}"
|
32
|
-
|
33
|
-
keys = PrefabProto::EvaluatedKeys.new(keys: to_ship.to_a, namespace: @client.namespace)
|
34
|
-
|
35
|
-
result = @client.post('/api/v1/evaluated-keys', keys)
|
36
|
-
|
37
|
-
log_internal "Uploaded #{to_ship.size} keys: #{result.status}"
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
data/lib/prefab/noop_cache.rb
DELETED
data/lib/prefab/noop_stats.rb
DELETED
@@ -1,54 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'test_helper'
|
4
|
-
|
5
|
-
class TestEvaluatedKeysAggregator < Minitest::Test
|
6
|
-
MAX_WAIT = 2
|
7
|
-
SLEEP_TIME = 0.01
|
8
|
-
|
9
|
-
def test_push
|
10
|
-
aggregator = Prefab::EvaluatedKeysAggregator.new(client: new_client, max_keys: 2, sync_interval: 1000)
|
11
|
-
|
12
|
-
aggregator.push('key.1')
|
13
|
-
aggregator.push('key.2')
|
14
|
-
|
15
|
-
assert_equal 2, aggregator.data.size
|
16
|
-
|
17
|
-
# we've reached the limit, so no more
|
18
|
-
aggregator.push('key.3')
|
19
|
-
assert_equal 2, aggregator.data.size
|
20
|
-
end
|
21
|
-
|
22
|
-
def test_sync
|
23
|
-
client = new_client(namespace: 'this.is.a.namespace')
|
24
|
-
|
25
|
-
client.get 'key.1', 'default', {}
|
26
|
-
client.get 'key.1', 'default', {}
|
27
|
-
client.get 'key.2', 'default', {}
|
28
|
-
|
29
|
-
requests = wait_for_post_requests(client) do
|
30
|
-
client.evaluated_keys_aggregator.send(:sync)
|
31
|
-
end
|
32
|
-
|
33
|
-
assert_equal [[
|
34
|
-
'/api/v1/evaluated-keys',
|
35
|
-
PrefabProto::EvaluatedKeys.new(
|
36
|
-
keys: ['key.1', 'key.2'],
|
37
|
-
namespace: 'this.is.a.namespace'
|
38
|
-
)
|
39
|
-
]], requests
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
def new_client(overrides = {})
|
45
|
-
super(**{
|
46
|
-
prefab_datasources: Prefab::Options::DATASOURCES::ALL,
|
47
|
-
initialization_timeout_sec: 0,
|
48
|
-
on_init_failure: Prefab::Options::ON_INITIALIZATION_FAILURE::RETURN,
|
49
|
-
api_key: '123-development-yourapikey-SDK',
|
50
|
-
collect_sync_interval: 1000, # we'll trigger sync manually in our test
|
51
|
-
collect_keys: true
|
52
|
-
}.merge(overrides))
|
53
|
-
end
|
54
|
-
end
|