prefab-cloud-ruby 0.24.6 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 36d1b27d1c03aef56882735b73835b65b508b5eef821f65e89ab5ba77f54a8ab
4
- data.tar.gz: 00a336ac664ab495ce1c5fff4b5ab6972169a095087887958c3a0881016ba273
3
+ metadata.gz: 318e4b03e0089cfe68fa20b00e3c400c1005b739f63ed4183b071418342e4bbe
4
+ data.tar.gz: 46e467acdeb1dea78cc2e5c97031c952fa6c2caf9be235a217d9341f54505389
5
5
  SHA512:
6
- metadata.gz: b0dc48a30fd50f53e9709e138afd408bfc2e91e6badb473c1d141db56c7cdfa507aef81db0f06674b8d640dec2a7ac147654def0cdd7e7bcebdd3b0eb2a79854
7
- data.tar.gz: a3bd2f91776a25d646f1ab28d54d395bd24283e16dbca2f0bc7a1d1f77c9bb063488e9f62e00030a8acc84b2ecb8b07791bf84c1893e091f40edbb75e55870ca
6
+ metadata.gz: fac2499f7fc5f4ba46eb6bb0f8a8671ac6fa8412600e8f3fbdeb61db68b60f491def31bd7faf1ee78711644ff4e1c253386c54e08d40f075404ab0b22d5977db
7
+ data.tar.gz: 9b112dcdbe22b2cadc6c20e5d10df94b1f1e269f49e3a12ada73550294a55154db098cc3662c5e25f2d569111b870f09df6abf2fc7af1cbc447983b3b3378bce
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.0 - 2023-08-10
4
+
5
+ - Removed EvaluatedKeysAggregator (#137)
6
+ - Change `collect_evaluation_summaries` default to true (#136)
7
+ - Removed some backwards compatibility shims (#133)
8
+ - Standardizing options (#132)
9
+ - Note that the default value for `context_upload_mode` is `:periodic_example` which means example contexts will be collected.
10
+ This enables easy variant override assignment in our UI. More at https://prefab.cloud/blog/feature-flag-variant-assignment/
11
+
3
12
  ## 0.24.6 - 2023-07-31
4
13
 
5
14
  - Logger Client compatibility (#129)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.24.6
1
+ 1.0.0
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 :shared_cache, :stats, :namespace, :interceptor, :api_key, :prefab_api_url, :options, :instance_hash
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, lookup_key = NO_DEFAULT_PROVIDED, properties = NO_DEFAULT_PROVIDED)
121
- _, properties = handle_positional_arguments(lookup_key, properties, :enabled?)
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, default_or_lookup_key = NO_DEFAULT_PROVIDED, properties = NO_DEFAULT_PROVIDED, ff_default = nil)
110
+ def get(key, default = NO_DEFAULT_PROVIDED, jit_context = NO_DEFAULT_PROVIDED)
127
111
  if is_ff?(key)
128
- default, properties = handle_positional_arguments(default_or_lookup_key, properties, :get)
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, default_or_lookup_key, properties)
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
@@ -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 properties != NO_DEFAULT_PROVIDED && @base_client.example_contexts_aggregator
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 initalization to be complete
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
@@ -91,6 +91,10 @@ module Prefab
91
91
  end
92
92
  end
93
93
 
94
+ def blank?
95
+ contexts.empty?
96
+ end
97
+
94
98
  def set(name, hash)
95
99
  @contexts[name.to_s] = NamedContext.new(name, hash)
96
100
  end
@@ -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)
@@ -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 = 1
37
- RETURN = 2
34
+ RAISE = :raise
35
+ RETURN = :return
38
36
  end
39
37
 
40
38
  module ON_NO_DEFAULT
41
- RAISE = 1
42
- RETURN_NIL = 2
39
+ RAISE = :raise
40
+ RETURN_NIL = :return_nil
43
41
  end
44
42
 
45
43
  module DATASOURCES
46
- ALL = 1
47
- LOCAL_ONLY = 2
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, # options :unlock_and_continue, :lock_and_keep_trying, :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
- collect_logs: true,
67
+ collect_logger_counts: true,
75
68
  collect_max_paths: DEFAULT_MAX_PATHS,
76
69
  collect_sync_interval: nil,
77
- collect_shapes: true,
78
- collect_max_shapes: DEFAULT_MAX_CONTEXT_KEYS,
79
- collect_keys: false,
80
- collect_max_keys: DEFAULT_MAX_KEYS,
81
- collect_example_contexts: false,
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
- @collect_logs = collect_logs
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?(@collect_logs)
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? || option == :force
156
+ option && (!local_only? || @allow_telemetry_in_local_mode)
161
157
  end
162
158
 
163
159
  def remove_trailing_slash(url)
@@ -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'
@@ -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.24.6 ruby lib
5
+ # stub: prefab-cloud-ruby 1.0.0 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "prefab-cloud-ruby".freeze
9
- s.version = "0.24.6"
9
+ s.version = "1.0.0"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
13
13
  s.authors = ["Jeff Dwyer".freeze]
14
- s.date = "2023-07-31"
14
+ s.date = "2023-08-10"
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",
@@ -93,7 +90,6 @@ Gem::Specification.new do |s|
93
90
  "test/test_context_shape.rb",
94
91
  "test/test_context_shape_aggregator.rb",
95
92
  "test/test_criteria_evaluator.rb",
96
- "test/test_evaluated_keys_aggregator.rb",
97
93
  "test/test_evaluation_summary_aggregator.rb",
98
94
  "test/test_example_contexts_aggregator.rb",
99
95
  "test/test_exponential_backoff.rb",
@@ -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)
@@ -14,7 +14,6 @@ module IntegrationTestHelpers
14
14
  raise message if RAISE_IF_NO_TESTS_FOUND
15
15
 
16
16
  puts message
17
-
18
17
  end
19
18
 
20
19
  files
@@ -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(**DEFAULT_NEW_CLIENT_OPTIONS.merge(overrides))
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
- requests = []
86
+ # we use ivars to avoid re-mocking the post method on subsequent calls
87
+ client.instance_variable_set("@_requests", [])
55
88
 
56
- client.define_singleton_method(:post) do |*params|
57
- requests.push(params)
89
+ if !client.instance_variable_get("@_already_faked_post")
90
+ client.define_singleton_method(:post) do |*params|
91
+ @_requests.push(params)
58
92
 
59
- FakeResponse.new(200, '')
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 requests.empty?
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
- requests
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
@@ -34,10 +34,10 @@ class MockBaseClient
34
34
 
35
35
  def context_shape_aggregator; end
36
36
 
37
- def evaluated_keys_aggregator; end
38
-
39
37
  def evaluation_summary_aggregator; end
40
38
 
39
+ def example_contexts_aggregator; end
40
+
41
41
  def config_value(key)
42
42
  @config_values[key]
43
43
  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
- assert_equal 'default', @client.get('does.not.exist', 'default')
34
- assert_equal 'test sample value', @client.get('sample')
35
- assert_equal 123, @client.get('sample_int')
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, @client.get('false_value', 'red')
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, @client.get('zero_value', 'red')
43
+ assert_equal 0, client.get('zero_value', 'red')
46
44
 
47
45
  # A missing value returns the default
48
- assert_equal 'buckets', @client.get('missing_value', '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 @client.get('missing_value')
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
- 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')
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
- assert_equal_context_and_jit(false, :enabled?, 'user_key_match', { user: { key: 'jimmy' } })
74
- assert_equal_context_and_jit(true, :enabled?, 'user_key_match', { user: { key: 'abc123' } })
75
- assert_equal_context_and_jit(true, :enabled?, 'user_key_match', { user: { key: 'xyz987' } })
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
- assert_equal_context_and_jit(false, :enabled?, 'just_my_domain', user: { domain: 'gmail.com' })
80
- assert_equal_context_and_jit(false, :enabled?, 'just_my_domain', user: { domain: 'prefab.cloud' })
81
- assert_equal_context_and_jit(false, :enabled?, 'just_my_domain', user: { domain: 'example.com' })
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
- assert_nil @client.get('just_my_domain', 'abc123', user: { domain: 'gmail.com' })
86
- assert_equal 'DEFAULT', @client.get('just_my_domain', 'abc123', { user: { domain: 'gmail.com' } }, 'DEFAULT')
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
- assert_equal_context_and_jit('new-version', :get, 'just_my_domain', { user: { domain: 'prefab.cloud' } })
89
- assert_equal_context_and_jit('new-version', :get, 'just_my_domain', { user: { domain: 'example.com' } })
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, @client.enabled?('deprecated_no_dot_notation', { domain: 'gmail.com' })
95
- assert_equal true, @client.enabled?('deprecated_no_dot_notation', { domain: 'prefab.cloud' })
96
- assert_equal true, @client.enabled?('deprecated_no_dot_notation', { domain: 'example.com' })
97
-
98
- # with a lookup key
99
- assert_equal false, @client.enabled?('deprecated_no_dot_notation', 'some-lookup-key', { domain: 'gmail.com' })
100
- assert_equal true, @client.enabled?('deprecated_no_dot_notation', 'some-lookup-key', { domain: 'prefab.cloud' })
101
- assert_equal true, @client.enabled?('deprecated_no_dot_notation', 'some-lookup-key', { domain: 'example.com' })
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
- assert_equal false, @client.enabled?('flag_with_a_value')
106
- assert_equal 'all-features', @client.get('flag_with_a_value')
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 Prefab::Client.new(api_key: fake_api_key).evaluation_summary_aggregator
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 Prefab::Client.new(prefab_datasources: LOCAL_ONLY,
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 Prefab::Client.new(api_key: fake_api_key,
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
- Prefab::Client.new(api_key: fake_api_key,
150
- collect_evaluation_summaries: true).evaluation_summary_aggregator.class
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 = PrefabProto::Config.new(
155
- id: 123,
156
- key: KEY,
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: :force,
214
- collect_example_contexts: :force)
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: :force, collect_example_contexts: :force)
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: :force)
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 assert_equal_context_and_jit(expected, method, key, context)
370
- assert_equal expected, @client.send(method, key, context)
371
-
372
- Prefab::Context.with_context(context) do
373
- assert_equal expected, @client.send(method, key)
374
- end
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
@@ -4,6 +4,7 @@ require 'test_helper'
4
4
 
5
5
  class TestConfigClient < Minitest::Test
6
6
  def setup
7
+ super
7
8
  options = Prefab::Options.new(
8
9
  prefab_config_override_dir: 'none',
9
10
  prefab_config_classpath_dir: 'test',
@@ -4,6 +4,7 @@ require 'test_helper'
4
4
 
5
5
  class TestConfigLoader < Minitest::Test
6
6
  def setup
7
+ super
7
8
  options = Prefab::Options.new(
8
9
  prefab_config_override_dir: 'none',
9
10
  prefab_config_classpath_dir: 'test',
data/test/test_context.rb CHANGED
@@ -6,6 +6,7 @@ class TestContext < Minitest::Test
6
6
  EXAMPLE_PROPERTIES = { user: { key: 'some-user-key', name: 'Ted' }, team: { key: 'abc', plan: 'pro' } }.freeze
7
7
 
8
8
  def setup
9
+ super
9
10
  Prefab::Context.current = nil
10
11
  end
11
12
 
@@ -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
 
@@ -24,6 +24,7 @@ class TestCriteriaEvaluator < Minitest::Test
24
24
  )
25
25
 
26
26
  def setup
27
+ super
27
28
  @base_client = FakeBaseClient.new
28
29
  end
29
30
 
@@ -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
- assert_equal it.expected[:value], it.test_client.send(it.func, *it.input)
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
- aggregator = Prefab::LogPathAggregator.new(client: new_client, max_paths: 2, sync_interval: 1000)
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
@@ -19,6 +19,7 @@ class TestLogger < Minitest::Test
19
19
  )
20
20
 
21
21
  def setup
22
+ super
22
23
  Prefab::LoggerClient.send(:public, :get_path)
23
24
  Prefab::LoggerClient.send(:public, :get_loc_path)
24
25
  Prefab::LoggerClient.send(:public, :level_of)
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 test_collect_max_paths_with_collect_logs_false
39
+ def test_collect_max_paths_with_collect_logger_counts_false
40
40
  options = Prefab::Options.new(collect_max_paths: 100,
41
- collect_logs: false)
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 0, Prefab::Options.new.collect_max_evaluation_summaries
47
- assert_equal 100_000, Prefab::Options.new(collect_evaluation_summaries: true).collect_max_evaluation_summaries
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(collect_evaluation_summaries: true,
50
- collect_max_evaluation_summaries: 3).collect_max_evaluation_summaries
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.24.6
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff Dwyer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-07-31 00:00:00.000000000 Z
11
+ date: 2023-08-10 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
@@ -252,7 +249,6 @@ files:
252
249
  - test/test_context_shape.rb
253
250
  - test/test_context_shape_aggregator.rb
254
251
  - test/test_criteria_evaluator.rb
255
- - test/test_evaluated_keys_aggregator.rb
256
252
  - test/test_evaluation_summary_aggregator.rb
257
253
  - test/test_example_contexts_aggregator.rb
258
254
  - 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
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Prefab
4
- class NoopCache
5
- def fetch(_name, _opts)
6
- yield
7
- end
8
-
9
- def write(name, value, opts = nil); end
10
-
11
- def read(name); end
12
-
13
- def delete(name); end
14
- end
15
- end
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Prefab
4
- class NoopStats
5
- # receives increment("prefab.ratelimit.limitcheck", {:tags=>["policy_group:page_view", "pass:true"]})
6
- def increment(name, opts = {}); end
7
- end
8
- end
@@ -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