prefab-cloud-ruby 0 → 0.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.
Files changed (101) hide show
  1. checksums.yaml +5 -5
  2. data/.ruby-version +1 -0
  3. data/Gemfile +9 -22
  4. data/Gemfile.lock +88 -160
  5. data/LICENSE.txt +1 -1
  6. data/Rakefile +14 -14
  7. data/VERSION +1 -1
  8. data/lib/prefab/auth_interceptor.rb +25 -0
  9. data/lib/prefab/client.rb +34 -139
  10. data/lib/prefab/config_client.rb +23 -275
  11. data/lib/prefab/config_loader.rb +27 -60
  12. data/lib/prefab/config_resolver.rb +40 -53
  13. data/lib/prefab/noop_cache.rb +13 -0
  14. data/lib/prefab/noop_stats.rb +8 -0
  15. data/lib/prefab/prefab_pb.rb +39 -0
  16. data/lib/prefab/prefab_services_pb.rb +37 -0
  17. data/lib/prefab/ratelimit_client.rb +58 -0
  18. data/lib/prefab/ratelimit_pb.rb +125 -0
  19. data/lib/prefab/store.rb +29 -0
  20. data/lib/prefab_client.rb +35 -0
  21. metadata +36 -198
  22. data/.envrc.sample +0 -3
  23. data/.github/workflows/ruby.yml +0 -46
  24. data/.gitmodules +0 -3
  25. data/.rubocop.yml +0 -13
  26. data/.tool-versions +0 -1
  27. data/CHANGELOG.md +0 -169
  28. data/CODEOWNERS +0 -1
  29. data/README.md +0 -94
  30. data/bin/console +0 -21
  31. data/compile_protos.sh +0 -18
  32. data/lib/prefab/config_client_presenter.rb +0 -18
  33. data/lib/prefab/config_value_unwrapper.rb +0 -115
  34. data/lib/prefab/config_value_wrapper.rb +0 -18
  35. data/lib/prefab/context.rb +0 -179
  36. data/lib/prefab/context_shape.rb +0 -20
  37. data/lib/prefab/context_shape_aggregator.rb +0 -65
  38. data/lib/prefab/criteria_evaluator.rb +0 -136
  39. data/lib/prefab/encryption.rb +0 -65
  40. data/lib/prefab/error.rb +0 -6
  41. data/lib/prefab/errors/env_var_parse_error.rb +0 -11
  42. data/lib/prefab/errors/initialization_timeout_error.rb +0 -13
  43. data/lib/prefab/errors/invalid_api_key_error.rb +0 -19
  44. data/lib/prefab/errors/missing_default_error.rb +0 -13
  45. data/lib/prefab/errors/missing_env_var_error.rb +0 -11
  46. data/lib/prefab/errors/uninitialized_error.rb +0 -13
  47. data/lib/prefab/evaluation.rb +0 -52
  48. data/lib/prefab/evaluation_summary_aggregator.rb +0 -87
  49. data/lib/prefab/example_contexts_aggregator.rb +0 -78
  50. data/lib/prefab/exponential_backoff.rb +0 -21
  51. data/lib/prefab/feature_flag_client.rb +0 -42
  52. data/lib/prefab/http_connection.rb +0 -41
  53. data/lib/prefab/internal_logger.rb +0 -16
  54. data/lib/prefab/local_config_parser.rb +0 -151
  55. data/lib/prefab/log_path_aggregator.rb +0 -69
  56. data/lib/prefab/logger_client.rb +0 -264
  57. data/lib/prefab/murmer3.rb +0 -50
  58. data/lib/prefab/options.rb +0 -208
  59. data/lib/prefab/periodic_sync.rb +0 -69
  60. data/lib/prefab/prefab.rb +0 -56
  61. data/lib/prefab/rate_limit_cache.rb +0 -41
  62. data/lib/prefab/resolved_config_presenter.rb +0 -86
  63. data/lib/prefab/time_helpers.rb +0 -7
  64. data/lib/prefab/weighted_value_resolver.rb +0 -42
  65. data/lib/prefab/yaml_config_parser.rb +0 -34
  66. data/lib/prefab-cloud-ruby.rb +0 -57
  67. data/lib/prefab_pb.rb +0 -93
  68. data/prefab-cloud-ruby.gemspec +0 -155
  69. data/test/.prefab.default.config.yaml +0 -2
  70. data/test/.prefab.unit_tests.config.yaml +0 -28
  71. data/test/integration_test.rb +0 -150
  72. data/test/integration_test_helpers.rb +0 -151
  73. data/test/support/common_helpers.rb +0 -180
  74. data/test/support/mock_base_client.rb +0 -42
  75. data/test/support/mock_config_client.rb +0 -19
  76. data/test/support/mock_config_loader.rb +0 -1
  77. data/test/test_client.rb +0 -444
  78. data/test/test_config_client.rb +0 -109
  79. data/test/test_config_loader.rb +0 -117
  80. data/test/test_config_resolver.rb +0 -430
  81. data/test/test_config_value_unwrapper.rb +0 -224
  82. data/test/test_config_value_wrapper.rb +0 -42
  83. data/test/test_context.rb +0 -203
  84. data/test/test_context_shape.rb +0 -50
  85. data/test/test_context_shape_aggregator.rb +0 -147
  86. data/test/test_criteria_evaluator.rb +0 -726
  87. data/test/test_encryption.rb +0 -16
  88. data/test/test_evaluation_summary_aggregator.rb +0 -162
  89. data/test/test_example_contexts_aggregator.rb +0 -238
  90. data/test/test_exponential_backoff.rb +0 -18
  91. data/test/test_feature_flag_client.rb +0 -48
  92. data/test/test_helper.rb +0 -17
  93. data/test/test_integration.rb +0 -58
  94. data/test/test_local_config_parser.rb +0 -147
  95. data/test/test_log_path_aggregator.rb +0 -62
  96. data/test/test_logger.rb +0 -621
  97. data/test/test_logger_initialization.rb +0 -12
  98. data/test/test_options.rb +0 -75
  99. data/test/test_prefab.rb +0 -12
  100. data/test/test_rate_limit_cache.rb +0 -44
  101. data/test/test_weighted_value_resolver.rb +0 -71
@@ -1,180 +0,0 @@
1
- # frozen_string_literal: true
2
-
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
-
30
- def with_env(key, value, &block)
31
- old_value = ENV.fetch(key, nil)
32
-
33
- ENV[key] = value
34
- block.call
35
- ensure
36
- ENV[key] = old_value
37
- end
38
-
39
- DEFAULT_NEW_CLIENT_OPTIONS = {
40
- prefab_config_override_dir: 'none',
41
- prefab_config_classpath_dir: 'test',
42
- prefab_envs: ['unit_tests'],
43
- prefab_datasources: Prefab::Options::DATASOURCES::LOCAL_ONLY
44
- }.freeze
45
-
46
- def new_client(overrides = {})
47
-
48
- config = overrides.delete(:config)
49
- project_env_id = overrides.delete(:project_env_id)
50
-
51
- Prefab::Client.new(prefab_options(overrides)).tap do |client|
52
- inject_config(client, config) if config
53
-
54
- client.resolver.project_env_id = project_env_id if project_env_id
55
- end
56
- end
57
-
58
- def prefab_options(overrides = {})
59
- $logs ||= StringIO.new
60
- Prefab::Options.new(
61
- **DEFAULT_NEW_CLIENT_OPTIONS.merge(
62
- overrides.merge(logdev: $logs)
63
- )
64
- )
65
- end
66
-
67
- def string_list(values)
68
- PrefabProto::ConfigValue.new(string_list: PrefabProto::StringList.new(values: values))
69
- end
70
-
71
- def inject_config(client, config)
72
- resolver = client.config_client.instance_variable_get('@config_resolver')
73
- store = resolver.instance_variable_get('@local_store')
74
-
75
- Array(config).each do |c|
76
- store[c.key] = { config: c }
77
- end
78
- end
79
-
80
- def inject_project_env_id(client, project_env_id)
81
- resolver = client.config_client.instance_variable_get('@config_resolver')
82
- resolver.project_env_id = project_env_id
83
- end
84
-
85
- FakeResponse = Struct.new(:status, :body)
86
-
87
- def wait_for(condition, max_wait: 2, sleep_time: 0.01)
88
- wait_time = 0
89
- while !condition.call
90
- wait_time += sleep_time
91
- sleep sleep_time
92
-
93
- raise "Waited #{max_wait} seconds for the condition to be true, but it never was" if wait_time > max_wait
94
- end
95
- end
96
-
97
- def wait_for_post_requests(client, max_wait: 2, sleep_time: 0.01)
98
- # we use ivars to avoid re-mocking the post method on subsequent calls
99
- client.instance_variable_set("@_requests", [])
100
-
101
- if !client.instance_variable_get("@_already_faked_post")
102
- client.define_singleton_method(:post) do |*params|
103
- @_requests.push(params)
104
-
105
- FakeResponse.new(200, '')
106
- end
107
- end
108
-
109
- client.instance_variable_set("@_already_faked_post", true)
110
-
111
- yield
112
-
113
- # let the flush thread run
114
- wait_for -> { client.instance_variable_get("@_requests").size > 0 }, max_wait: max_wait, sleep_time: sleep_time
115
-
116
- client.instance_variable_get("@_requests")
117
- end
118
-
119
- def assert_summary(client, data)
120
- raise 'Evaluation summary aggregator not enabled' unless client.evaluation_summary_aggregator
121
-
122
- assert_equal data, client.evaluation_summary_aggregator.data
123
- end
124
-
125
- def assert_example_contexts(client, data)
126
- raise 'Example contexts aggregator not enabled' unless client.example_contexts_aggregator
127
-
128
- assert_equal data, client.example_contexts_aggregator.data
129
- end
130
-
131
- def weighted_values(values_and_weights, hash_by_property_name: 'user.key')
132
- values = values_and_weights.map do |value, weight|
133
- weighted_value(value, weight)
134
- end
135
-
136
- PrefabProto::WeightedValues.new(weighted_values: values, hash_by_property_name: hash_by_property_name)
137
- end
138
-
139
- def weighted_value(string, weight)
140
- PrefabProto::WeightedValue.new(
141
- value: PrefabProto::ConfigValue.new(string: string), weight: weight
142
- )
143
- end
144
-
145
- def context(properties)
146
- Prefab::Context.new(properties)
147
- end
148
-
149
- def assert_only_expected_logs
150
- assert_equal "WARN 2023-08-09 15:18:12 -0400: cloud.prefab.client.configclient No success loading checkpoints\n", $logs.string
151
- # mark nil to indicate we handled it
152
- $logs = nil
153
- end
154
-
155
- def assert_logged(expected)
156
- # we do a uniq here because logging can happen in a separate thread so the
157
- # number of times a log might happen could be slightly variable.
158
- assert_equal expected, $logs.string.split("\n").uniq
159
- # mark nil to indicate we handled it
160
- $logs = nil
161
- end
162
-
163
- def assert_stderr(expected)
164
- assert ($stderr.string.split("\n").uniq & expected).size > 0
165
-
166
- # Ruby 2.X has a lot of warnings about instance variables not being
167
- # initialized so we don't try to assert on stderr for those versions.
168
- # Instead we just stop after asserting that our expected errors are
169
- # included in the output.
170
- if RUBY_VERSION.start_with?('2.')
171
- puts $stderr.string
172
- return
173
- end
174
-
175
- assert_equal expected, $stderr.string.split("\n")
176
-
177
- # restore since we've handled it
178
- $stderr = $oldstderr
179
- end
180
- end
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class MockBaseClient
4
- STAGING_ENV_ID = 1
5
- PRODUCTION_ENV_ID = 2
6
- TEST_ENV_ID = 3
7
- attr_reader :namespace, :logger, :config_client, :options, :posts
8
-
9
- def initialize(options = Prefab::Options.new)
10
- @options = options
11
- @namespace = namespace
12
- @config_client = MockConfigClient.new
13
- Prefab::LoggerClient.new(options.logdev)
14
- @posts = []
15
- end
16
-
17
- def instance_hash
18
- 'mock-base-client-instance-hash'
19
- end
20
-
21
- def project_id
22
- 1
23
- end
24
-
25
- def post(_, _)
26
- raise 'Use wait_for_post_requests'
27
- end
28
-
29
- def log
30
- @logger
31
- end
32
-
33
- def context_shape_aggregator; end
34
-
35
- def evaluation_summary_aggregator; end
36
-
37
- def example_contexts_aggregator; end
38
-
39
- def config_value(key)
40
- @config_values[key]
41
- end
42
- end
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class MockConfigClient
4
- def initialize(config_values = {})
5
- @config_values = config_values
6
- end
7
-
8
- def get(key, default = nil)
9
- @config_values.fetch(key, default)
10
- end
11
-
12
- def get_config(key)
13
- PrefabProto::Config.new(value: @config_values[key], key: key)
14
- end
15
-
16
- def mock_this_config(key, config_value)
17
- @config_values[key] = config_value
18
- end
19
- end
@@ -1 +0,0 @@
1
- # frozen_string_literal: true
data/test/test_client.rb DELETED
@@ -1,444 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
-
5
- class TestClient < Minitest::Test
6
- LOCAL_ONLY = Prefab::Options::DATASOURCES::LOCAL_ONLY
7
-
8
- PROJECT_ENV_ID = 1
9
- KEY = 'the-key'
10
- DEFAULT_VALUE = 'default_value'
11
- DESIRED_VALUE = 'desired_value'
12
-
13
- IRRELEVANT = 'this should never show up'
14
-
15
- DEFAULT_VALUE_CONFIG = PrefabProto::ConfigValue.new(string: DEFAULT_VALUE)
16
- DESIRED_VALUE_CONFIG = PrefabProto::ConfigValue.new(string: DESIRED_VALUE)
17
-
18
- TRUE_CONFIG = PrefabProto::ConfigValue.new(bool: true)
19
- FALSE_CONFIG = PrefabProto::ConfigValue.new(bool: false)
20
-
21
- DEFAULT_ROW = PrefabProto::ConfigRow.new(
22
- values: [
23
- PrefabProto::ConditionalValue.new(value: DEFAULT_VALUE_CONFIG)
24
- ]
25
- )
26
-
27
- def test_get
28
- _, err = capture_io do
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')
33
- end
34
- assert_equal '', err
35
- end
36
-
37
- def test_get_with_default
38
- client = new_client
39
- # A `false` value is not replaced with the default
40
- assert_equal false, client.get('false_value', 'red')
41
-
42
- # A falsy value is not replaced with the default
43
- assert_equal 0, client.get('zero_value', 'red')
44
-
45
- # A missing value returns the default
46
- assert_equal 'buckets', client.get('missing_value', 'buckets')
47
- end
48
-
49
- def test_get_with_missing_default
50
- client = new_client
51
- # it raises by default
52
- err = assert_raises(Prefab::Errors::MissingDefaultError) do
53
- assert_nil client.get('missing_value')
54
- end
55
-
56
- assert_match(/No value found for key/, err.message)
57
- assert_match(/on_no_default/, err.message)
58
-
59
- # you can opt-in to return `nil` instead
60
- client = new_client(on_no_default: Prefab::Options::ON_NO_DEFAULT::RETURN_NIL)
61
- assert_nil client.get('missing_value')
62
- end
63
-
64
- def test_enabled
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
- end
71
-
72
- def test_ff_enabled_with_user_key_match
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') }
86
- end
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
90
- def test_ff_enabled_with_context
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') }
104
- end
105
-
106
- def test_ff_get_with_context
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') }
112
-
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') }
120
- end
121
-
122
- def test_deprecated_no_dot_notation_ff_enabled_with_jit_context
123
- client = new_client
124
- # with no lookup key
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
- ]
134
- end
135
-
136
- def test_getting_feature_flag_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')
140
- end
141
-
142
- def test_initialization_with_an_options_object
143
- options_hash = {
144
- namespace: 'test-namespace',
145
- prefab_datasources: LOCAL_ONLY
146
- }
147
-
148
- options = Prefab::Options.new(options_hash)
149
-
150
- client = Prefab::Client.new(options)
151
-
152
- assert_equal client.namespace, 'test-namespace'
153
- end
154
-
155
- def test_initialization_with_a_hash
156
- options_hash = {
157
- namespace: 'test-namespace',
158
- prefab_datasources: LOCAL_ONLY
159
- }
160
-
161
- client = Prefab::Client.new(options_hash)
162
-
163
- assert_equal client.namespace, 'test-namespace'
164
- end
165
-
166
- def test_evaluation_summary_aggregator
167
- fake_api_key = '123-development-yourapikey-SDK'
168
-
169
- # it is nil by default
170
- assert_nil new_client(api_key: fake_api_key).evaluation_summary_aggregator
171
-
172
- # it is nil when local_only even if collect_max_evaluation_summaries is true
173
- assert_nil new_client(prefab_datasources: LOCAL_ONLY,
174
- collect_evaluation_summaries: true, ).evaluation_summary_aggregator
175
-
176
- # it is nil when collect_max_evaluation_summaries is false
177
- assert_nil new_client(api_key: fake_api_key,
178
- prefab_datasources: :all,
179
- collect_evaluation_summaries: false).evaluation_summary_aggregator
180
-
181
- # it is not nil when collect_max_evaluation_summaries is true and the datasource is not local_only
182
- assert_equal Prefab::EvaluationSummaryAggregator,
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.configclient No success loading checkpoints"
189
- ]
190
- end
191
-
192
- def test_get_with_basic_value
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)
196
-
197
- assert_equal DESIRED_VALUE, client.get(config.key, IRRELEVANT, 'user' => { 'key' => 99 })
198
-
199
- assert_summary client, {
200
- [KEY, :CONFIG] => {
201
- {
202
- config_id: config.id,
203
- config_row_index: 1,
204
- selected_value: DESIRED_VALUE_CONFIG,
205
- conditional_value_index: 0,
206
- weighted_value_index: nil,
207
- selected_index: nil
208
- } => 1
209
- }
210
- }
211
-
212
- assert_example_contexts client, [Prefab::Context.new({ user: { 'key' => 99 } })]
213
- end
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
-
240
- def test_get_with_weighted_values
241
- config = PrefabProto::Config.new(
242
- id: 123,
243
- key: KEY,
244
- config_type: PrefabProto::ConfigType::CONFIG,
245
- rows: [
246
- DEFAULT_ROW,
247
- PrefabProto::ConfigRow.new(
248
- project_env_id: PROJECT_ENV_ID,
249
- values: [
250
- PrefabProto::ConditionalValue.new(
251
- criteria: [PrefabProto::Criterion.new(operator: PrefabProto::Criterion::CriterionOperator::ALWAYS_TRUE)],
252
- value: PrefabProto::ConfigValue.new(weighted_values: weighted_values([['abc', 98], ['def', 1],
253
- ['ghi', 1]]))
254
- )
255
- ]
256
- )
257
- ]
258
- )
259
-
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)
262
-
263
- 2.times do
264
- assert_equal 'abc', client.get(config.key, IRRELEVANT, 'user' => { 'key' => '1' })
265
- end
266
-
267
- 3.times do
268
- assert_equal 'def', client.get(config.key, IRRELEVANT, 'user' => { 'key' => '12' })
269
- end
270
-
271
- assert_equal 'ghi',
272
- client.get(config.key, IRRELEVANT, 'user' => { 'key' => '4', admin: true })
273
-
274
- assert_summary client, {
275
- [KEY, :CONFIG] => {
276
- {
277
- config_id: config.id,
278
- config_row_index: 1,
279
- selected_value: PrefabProto::ConfigValue.new(string: 'abc'),
280
- conditional_value_index: 0,
281
- weighted_value_index: 0,
282
- selected_index: nil
283
- } => 2,
284
-
285
- {
286
- config_id: config.id,
287
- config_row_index: 1,
288
- selected_value: PrefabProto::ConfigValue.new(string: 'def'),
289
- conditional_value_index: 0,
290
- weighted_value_index: 1,
291
- selected_index: nil
292
- } => 3,
293
-
294
- {
295
- config_id: config.id,
296
- config_row_index: 1,
297
- selected_value: PrefabProto::ConfigValue.new(string: 'ghi'),
298
- conditional_value_index: 0,
299
- weighted_value_index: 2,
300
- selected_index: nil
301
- } => 1
302
- }
303
- }
304
-
305
- assert_example_contexts client, [
306
- Prefab::Context.new(user: { 'key' => '1' }),
307
- Prefab::Context.new(user: { 'key' => '12' }),
308
- Prefab::Context.new(user: { 'key' => '4', admin: true })
309
- ]
310
- end
311
-
312
- def test_in_seg
313
- segment_key = 'segment_key'
314
-
315
- segment_config = PrefabProto::Config.new(
316
- config_type: PrefabProto::ConfigType::SEGMENT,
317
- key: segment_key,
318
- rows: [
319
- PrefabProto::ConfigRow.new(
320
- values: [
321
- PrefabProto::ConditionalValue.new(
322
- value: TRUE_CONFIG,
323
- criteria: [
324
- PrefabProto::Criterion.new(
325
- operator: PrefabProto::Criterion::CriterionOperator::PROP_ENDS_WITH_ONE_OF,
326
- value_to_match: string_list(['hotmail.com', 'gmail.com']),
327
- property_name: 'user.email'
328
- )
329
- ]
330
- ),
331
- PrefabProto::ConditionalValue.new(value: FALSE_CONFIG)
332
- ]
333
- )
334
- ]
335
- )
336
-
337
- config = PrefabProto::Config.new(
338
- key: KEY,
339
- rows: [
340
- DEFAULT_ROW,
341
-
342
- PrefabProto::ConfigRow.new(
343
- project_env_id: PROJECT_ENV_ID,
344
- values: [
345
- PrefabProto::ConditionalValue.new(
346
- criteria: [
347
- PrefabProto::Criterion.new(
348
- operator: PrefabProto::Criterion::CriterionOperator::IN_SEG,
349
- value_to_match: PrefabProto::ConfigValue.new(string: segment_key)
350
- )
351
- ],
352
- value: DESIRED_VALUE_CONFIG
353
- )
354
- ]
355
- )
356
- ]
357
- )
358
-
359
- client = new_client(config: [config, segment_config], project_env_id: PROJECT_ENV_ID,
360
- collect_evaluation_summaries: true, context_upload_mode: :periodic_example, allow_telemetry_in_local_mode: true)
361
-
362
- assert_equal DEFAULT_VALUE, client.get(config.key)
363
- assert_equal DEFAULT_VALUE,
364
- client.get(config.key, IRRELEVANT, user: { key: 'abc', email: 'example@prefab.cloud' })
365
- assert_equal DESIRED_VALUE, client.get(config.key, IRRELEVANT, user: { key: 'def', email: 'example@hotmail.com' })
366
-
367
- assert_summary client, {
368
- [segment_key, :SEGMENT] => {
369
- { config_id: 0, config_row_index: 0, conditional_value_index: 1, selected_value: FALSE_CONFIG,
370
- weighted_value_index: nil, selected_index: nil } => 2,
371
- { config_id: 0, config_row_index: 0, conditional_value_index: 0, selected_value: TRUE_CONFIG,
372
- weighted_value_index: nil, selected_index: nil } => 1
373
- },
374
- [KEY, :NOT_SET_CONFIG_TYPE] => {
375
- { config_id: 0, config_row_index: 0, conditional_value_index: 0, selected_value: DEFAULT_VALUE_CONFIG,
376
- weighted_value_index: nil, selected_index: nil } => 2,
377
- { config_id: 0, config_row_index: 1, conditional_value_index: 0, selected_value: DESIRED_VALUE_CONFIG,
378
- weighted_value_index: nil, selected_index: nil } => 1
379
- }
380
- }
381
-
382
- assert_example_contexts client, [
383
- Prefab::Context.new(user: { key: 'abc', email: 'example@prefab.cloud' }),
384
- Prefab::Context.new(user: { key: 'def', email: 'example@hotmail.com' })
385
- ]
386
- end
387
-
388
- def test_get_log_level
389
- config = PrefabProto::Config.new(
390
- id: 999,
391
- key: 'log-level',
392
- config_type: PrefabProto::ConfigType::LOG_LEVEL,
393
- rows: [
394
- PrefabProto::ConfigRow.new(
395
- values: [
396
- PrefabProto::ConditionalValue.new(
397
- criteria: [PrefabProto::Criterion.new(operator: PrefabProto::Criterion::CriterionOperator::ALWAYS_TRUE)],
398
- value: PrefabProto::ConfigValue.new(log_level: PrefabProto::LogLevel::INFO)
399
- )
400
- ]
401
- )
402
- ]
403
- )
404
-
405
- client = new_client(config: config, project_env_id: PROJECT_ENV_ID,
406
- collect_evaluation_summaries: true, allow_telemetry_in_local_mode: true)
407
-
408
- assert_equal :INFO, client.get(config.key, IRRELEVANT)
409
-
410
- # nothing is summarized for log levels
411
- assert_summary client, {}
412
- end
413
-
414
- def test_fork_includes_logger_context_keys
415
- client = new_client
416
- client.log.add_context_keys "user.name"
417
-
418
- forked = client.fork
419
-
420
- assert forked.log.context_keys.to_a == %w(user.name)
421
- end
422
-
423
- private
424
-
425
- def basic_value_config
426
- PrefabProto::Config.new(
427
- id: 123,
428
- key: KEY,
429
- config_type: PrefabProto::ConfigType::CONFIG,
430
- rows: [
431
- DEFAULT_ROW,
432
- PrefabProto::ConfigRow.new(
433
- project_env_id: PROJECT_ENV_ID,
434
- values: [
435
- PrefabProto::ConditionalValue.new(
436
- criteria: [PrefabProto::Criterion.new(operator: PrefabProto::Criterion::CriterionOperator::ALWAYS_TRUE)],
437
- value: DESIRED_VALUE_CONFIG
438
- )
439
- ]
440
- )
441
- ]
442
- )
443
- end
444
- end