prefab-cloud-ruby 0.24.6 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|