prefab-cloud-ruby 0.24.6 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 36d1b27d1c03aef56882735b73835b65b508b5eef821f65e89ab5ba77f54a8ab
4
- data.tar.gz: 00a336ac664ab495ce1c5fff4b5ab6972169a095087887958c3a0881016ba273
3
+ metadata.gz: eb91b8be5a01c3bab3312d277a27117c0449713623e1605863486c3809cff0cd
4
+ data.tar.gz: 3deaeb9fcb458ef461771865463c5163f8b5c6833a45d1e7af4ec3474f5d04bd
5
5
  SHA512:
6
- metadata.gz: b0dc48a30fd50f53e9709e138afd408bfc2e91e6badb473c1d141db56c7cdfa507aef81db0f06674b8d640dec2a7ac147654def0cdd7e7bcebdd3b0eb2a79854
7
- data.tar.gz: a3bd2f91776a25d646f1ab28d54d395bd24283e16dbca2f0bc7a1d1f77c9bb063488e9f62e00030a8acc84b2ecb8b07791bf84c1893e091f40edbb75e55870ca
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.24.6
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 :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
@@ -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: Prefab::StringList.new(values: value.map(&:to_s)))
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
@@ -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.1 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.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-07-31"
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",
@@ -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',
@@ -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
@@ -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.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-07-31 00:00:00.000000000 Z
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
@@ -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