prefab-cloud-ruby 0.24.5 → 0.24.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/VERSION +1 -1
  4. data/compile_protos.sh +7 -0
  5. data/lib/prefab/client.rb +17 -4
  6. data/lib/prefab/config_client.rb +8 -9
  7. data/lib/prefab/config_value_unwrapper.rb +20 -9
  8. data/lib/prefab/context.rb +39 -7
  9. data/lib/prefab/context_shape_aggregator.rb +1 -1
  10. data/lib/prefab/criteria_evaluator.rb +24 -16
  11. data/lib/prefab/evaluated_keys_aggregator.rb +1 -1
  12. data/lib/prefab/evaluation.rb +48 -0
  13. data/lib/prefab/evaluation_summary_aggregator.rb +85 -0
  14. data/lib/prefab/example_contexts_aggregator.rb +76 -0
  15. data/lib/prefab/exponential_backoff.rb +5 -0
  16. data/lib/prefab/log_path_aggregator.rb +1 -1
  17. data/lib/prefab/logger_client.rb +12 -13
  18. data/lib/prefab/options.rb +27 -14
  19. data/lib/prefab/periodic_sync.rb +30 -13
  20. data/lib/prefab/rate_limit_cache.rb +41 -0
  21. data/lib/prefab/resolved_config_presenter.rb +2 -4
  22. data/lib/prefab/weighted_value_resolver.rb +1 -1
  23. data/lib/prefab-cloud-ruby.rb +5 -2
  24. data/lib/prefab_pb.rb +11 -1
  25. data/prefab-cloud-ruby.gemspec +14 -5
  26. data/test/support/common_helpers.rb +105 -0
  27. data/test/support/mock_base_client.rb +44 -0
  28. data/test/support/mock_config_client.rb +19 -0
  29. data/test/support/mock_config_loader.rb +1 -0
  30. data/test/test_client.rb +257 -2
  31. data/test/test_config_resolver.rb +25 -24
  32. data/test/test_config_value_unwrapper.rb +22 -32
  33. data/test/test_context_shape_aggregator.rb +0 -1
  34. data/test/test_criteria_evaluator.rb +179 -133
  35. data/test/test_evaluation_summary_aggregator.rb +162 -0
  36. data/test/test_example_contexts_aggregator.rb +238 -0
  37. data/test/test_helper.rb +5 -131
  38. data/test/test_local_config_parser.rb +2 -2
  39. data/test/test_logger.rb +5 -5
  40. data/test/test_options.rb +8 -0
  41. data/test/test_rate_limit_cache.rb +44 -0
  42. data/test/test_weighted_value_resolver.rb +13 -7
  43. metadata +13 -4
  44. data/lib/prefab/evaluated_configs_aggregator.rb +0 -60
  45. data/test/test_evaluated_configs_aggregator.rb +0 -254
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.5
4
+ version: 0.24.6
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-10 00:00:00.000000000 Z
11
+ date: 2023-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -211,8 +211,10 @@ 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_configs_aggregator.rb
215
214
  - lib/prefab/evaluated_keys_aggregator.rb
215
+ - lib/prefab/evaluation.rb
216
+ - lib/prefab/evaluation_summary_aggregator.rb
217
+ - lib/prefab/example_contexts_aggregator.rb
216
218
  - lib/prefab/exponential_backoff.rb
217
219
  - lib/prefab/feature_flag_client.rb
218
220
  - lib/prefab/http_connection.rb
@@ -225,6 +227,7 @@ files:
225
227
  - lib/prefab/noop_stats.rb
226
228
  - lib/prefab/options.rb
227
229
  - lib/prefab/periodic_sync.rb
230
+ - lib/prefab/rate_limit_cache.rb
228
231
  - lib/prefab/resolved_config_presenter.rb
229
232
  - lib/prefab/sse_logger.rb
230
233
  - lib/prefab/time_helpers.rb
@@ -236,6 +239,10 @@ files:
236
239
  - test/.prefab.unit_tests.config.yaml
237
240
  - test/integration_test.rb
238
241
  - test/integration_test_helpers.rb
242
+ - test/support/common_helpers.rb
243
+ - test/support/mock_base_client.rb
244
+ - test/support/mock_config_client.rb
245
+ - test/support/mock_config_loader.rb
239
246
  - test/test_client.rb
240
247
  - test/test_config_client.rb
241
248
  - test/test_config_loader.rb
@@ -245,8 +252,9 @@ files:
245
252
  - test/test_context_shape.rb
246
253
  - test/test_context_shape_aggregator.rb
247
254
  - test/test_criteria_evaluator.rb
248
- - test/test_evaluated_configs_aggregator.rb
249
255
  - test/test_evaluated_keys_aggregator.rb
256
+ - test/test_evaluation_summary_aggregator.rb
257
+ - test/test_example_contexts_aggregator.rb
250
258
  - test/test_exponential_backoff.rb
251
259
  - test/test_feature_flag_client.rb
252
260
  - test/test_helper.rb
@@ -255,6 +263,7 @@ files:
255
263
  - test/test_log_path_aggregator.rb
256
264
  - test/test_logger.rb
257
265
  - test/test_options.rb
266
+ - test/test_rate_limit_cache.rb
258
267
  - test/test_weighted_value_resolver.rb
259
268
  homepage: http://github.com/prefab-cloud/prefab-cloud-ruby
260
269
  licenses:
@@ -1,60 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'periodic_sync'
4
-
5
- module Prefab
6
- class EvaluatedConfigsAggregator
7
- include Prefab::PeriodicSync
8
-
9
- attr_reader :data
10
-
11
- def initialize(client:, max_configs:, sync_interval:)
12
- @max_configs = max_configs
13
- @client = client
14
- @name = 'evaluated_configs_aggregator'
15
-
16
- @data = Concurrent::Array.new
17
-
18
- start_periodic_sync(sync_interval)
19
- end
20
-
21
- def push(evaluation)
22
- return if @data.size >= @max_configs
23
-
24
- @data.push(evaluation)
25
- end
26
-
27
- def prepare_data
28
- to_ship = @data.dup
29
- @data.clear
30
-
31
- to_ship.map { |e| coerce_to_proto(e) }
32
- end
33
-
34
- def coerce_to_proto(evaluation)
35
- config, result, context = evaluation
36
-
37
- PrefabProto::EvaluatedConfig.new(
38
- key: config.key,
39
- config_version: config.id,
40
- result: result,
41
- context: context.to_proto(@client.namespace),
42
- timestamp: Prefab::TimeHelpers.now_in_ms
43
- )
44
- end
45
-
46
- private
47
-
48
- def flush(to_ship, _)
49
- @pool.post do
50
- log_internal "Uploading evaluated configs for #{to_ship.size}"
51
-
52
- configs = PrefabProto::EvaluatedConfigs.new(configs: to_ship)
53
-
54
- result = @client.post('/api/v1/evaluated-configs', configs)
55
-
56
- log_internal "Uploaded #{to_ship.size} configs: #{result.status}"
57
- end
58
- end
59
- end
60
- end
@@ -1,254 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
- require 'timecop'
5
-
6
- class TestEvaluatedConfigsAggregator < Minitest::Test
7
- MAX_WAIT = 2
8
- SLEEP_TIME = 0.01
9
-
10
- def test_push
11
- aggregator = Prefab::EvaluatedConfigsAggregator.new(client: new_client, max_configs: 2, sync_interval: 1000)
12
-
13
- aggregator.push([])
14
- aggregator.push([])
15
-
16
- assert_equal 2, aggregator.data.size
17
-
18
- # we've reached the limit, so no more
19
- aggregator.push([])
20
- assert_equal 2, aggregator.data.size
21
- end
22
-
23
- def test_coerce_to_proto
24
- aggregator = Prefab::EvaluatedConfigsAggregator.new(client: new_client, max_configs: 2, sync_interval: 2)
25
-
26
- Timecop.freeze do
27
- coerced = aggregator.coerce_to_proto([
28
- CONFIG_1,
29
- DESIRED_VALUE,
30
- Prefab::Context.new(CONTEXT)
31
- ])
32
-
33
- assert_equal PrefabProto::EvaluatedConfig.new(
34
- key: CONFIG_1.key,
35
- config_version: CONFIG_1.id,
36
- result: DESIRED_VALUE,
37
- context: PrefabProto::ContextSet.new(
38
- contexts: [
39
- PrefabProto::Context.new(
40
- type: "user",
41
- values: {
42
- "id" => PrefabProto::ConfigValue.new(int: 1),
43
- "email_suffix" => PrefabProto::ConfigValue.new(string: "hotmail.com")
44
- }
45
- ),
46
- PrefabProto::Context.new(
47
- type: "team",
48
- values: {
49
- "id" => PrefabProto::ConfigValue.new(int: 2),
50
- "name" => PrefabProto::ConfigValue.new(string: "team-name")
51
- }
52
- ),
53
- PrefabProto::Context.new(
54
- type: "prefab",
55
- values: {
56
- "current-time" => PrefabProto::ConfigValue.new(int: Prefab::TimeHelpers.now_in_ms),
57
- }
58
- ),
59
- ]
60
- ),
61
- timestamp: Prefab::TimeHelpers.now_in_ms
62
- ), coerced
63
- end
64
- end
65
-
66
- def test_sync
67
- client = new_client(namespace: 'this.is.a.namespace')
68
-
69
- inject_config(client, CONFIG_1)
70
- inject_config(client, CONFIG_2)
71
- inject_project_env_id(client, PROJECT_ENV_ID)
72
-
73
- client.get CONFIG_1.key, 'default', CONTEXT
74
- client.get CONFIG_1.key, 'default', { user: { email_suffix: "example.com" }, device: { mobile: true } }
75
- client.get CONFIG_2.key, 'default', CONTEXT
76
-
77
- # logger items are not reported
78
- client.get "#{Prefab::ConfigClient::LOGGING_KEY_PREFIX}something", 'default', CONTEXT
79
- client.get "#{Prefab::LoggerClient::BASE_KEY}", 'default', CONTEXT
80
-
81
- requests = wait_for_post_requests(client) do
82
- client.evaluated_configs_aggregator.send(:sync)
83
- end
84
-
85
- assert_equal [[
86
- '/api/v1/evaluated-configs',
87
- PrefabProto::EvaluatedConfigs.new(
88
- configs: [
89
- PrefabProto::EvaluatedConfig.new(
90
- key: CONFIG_1.key,
91
- config_version: CONFIG_1.id,
92
- result: DESIRED_VALUE,
93
- context: PrefabProto::ContextSet.new(
94
- contexts: [
95
- PrefabProto::Context.new(
96
- type: "user",
97
- values: {
98
- "id" => PrefabProto::ConfigValue.new(int: 1),
99
- "email_suffix" => PrefabProto::ConfigValue.new(string: "hotmail.com")
100
- }
101
- ),
102
- PrefabProto::Context.new(
103
- type: "team",
104
- values: {
105
- "id" => PrefabProto::ConfigValue.new(int: 2),
106
- "name" => PrefabProto::ConfigValue.new(string: "team-name")
107
- }
108
- ),
109
- PrefabProto::Context.new(
110
- type: "prefab",
111
- values: {
112
- "current-time" => PrefabProto::ConfigValue.new(int: Prefab::TimeHelpers.now_in_ms),
113
- "namespace" => PrefabProto::ConfigValue.new(string: "this.is.a.namespace"),
114
- }
115
- ),
116
- ]
117
- ),
118
- timestamp: Prefab::TimeHelpers.now_in_ms
119
- ),
120
- PrefabProto::EvaluatedConfig.new(
121
- key: CONFIG_1.key,
122
- config_version: CONFIG_1.id,
123
- result: DEFAULT_VALUE,
124
- context: PrefabProto::ContextSet.new(
125
- contexts: [
126
- PrefabProto::Context.new(
127
- type: "user",
128
- values: {
129
- "email_suffix" => PrefabProto::ConfigValue.new(string: "example.com")
130
- }
131
- ),
132
- PrefabProto::Context.new(
133
- type: "device",
134
- values: {
135
- "mobile" => PrefabProto::ConfigValue.new(bool: true),
136
- }
137
- ),
138
- PrefabProto::Context.new(
139
- type: "prefab",
140
- values: {
141
- "current-time" => PrefabProto::ConfigValue.new(int: Prefab::TimeHelpers.now_in_ms),
142
- "namespace" => PrefabProto::ConfigValue.new(string: "this.is.a.namespace"),
143
- }
144
- )
145
-
146
- ]
147
- ),
148
- timestamp: Prefab::TimeHelpers.now_in_ms
149
- ),
150
-
151
- PrefabProto::EvaluatedConfig.new(
152
- key: CONFIG_2.key,
153
- config_version: CONFIG_2.id,
154
- result: DEFAULT_VALUE,
155
- context: PrefabProto::ContextSet.new(
156
- contexts: [
157
- PrefabProto::Context.new(
158
- type: "user",
159
- values: {
160
- "id" => PrefabProto::ConfigValue.new(int: 1),
161
- "email_suffix" => PrefabProto::ConfigValue.new(string: "hotmail.com")
162
- }
163
- ),
164
- PrefabProto::Context.new(
165
- type: "team",
166
- values: {
167
- "id" => PrefabProto::ConfigValue.new(int: 2),
168
- "name" => PrefabProto::ConfigValue.new(string: "team-name")
169
- }
170
- ),
171
- PrefabProto::Context.new(
172
- type: "prefab",
173
- values: {
174
- "current-time" => PrefabProto::ConfigValue.new(int: Prefab::TimeHelpers.now_in_ms),
175
- "namespace" => PrefabProto::ConfigValue.new(string: "this.is.a.namespace"),
176
- }
177
- )
178
- ]
179
- ),
180
- timestamp: Prefab::TimeHelpers.now_in_ms
181
- )
182
-
183
- ]
184
- )
185
- ]], requests
186
- end
187
-
188
- private
189
-
190
- def new_client(overrides = {})
191
- super(**{
192
- prefab_datasources: Prefab::Options::DATASOURCES::ALL,
193
- initialization_timeout_sec: 0,
194
- on_init_failure: Prefab::Options::ON_INITIALIZATION_FAILURE::RETURN,
195
- api_key: '123-development-yourapikey-SDK',
196
- collect_sync_interval: 1000, # we'll trigger sync manually in our test
197
- collect_evaluations: true
198
- }.merge(overrides))
199
- end
200
-
201
- DEFAULT_VALUE = PrefabProto::ConfigValue.new(string: '❌')
202
-
203
- DESIRED_VALUE = PrefabProto::ConfigValue.new(string: "✅")
204
-
205
- DEFAULT_ROW = PrefabProto::ConfigRow.new(
206
- values: [
207
- PrefabProto::ConditionalValue.new(
208
- value: DEFAULT_VALUE
209
- )
210
- ]
211
- )
212
-
213
- PROJECT_ENV_ID = 1
214
-
215
- CONFIG_1 = PrefabProto::Config.new(
216
- id: 1,
217
- key: "key.1",
218
- rows: [
219
- PrefabProto::ConfigRow.new(
220
- project_env_id: PROJECT_ENV_ID,
221
- values: [
222
- PrefabProto::ConditionalValue.new(
223
- criteria: [
224
- PrefabProto::Criterion.new(
225
- operator: PrefabProto::Criterion::CriterionOperator::PROP_IS_ONE_OF,
226
- value_to_match: string_list(['hotmail.com', 'gmail.com']),
227
- property_name: 'user.email_suffix'
228
- )
229
- ],
230
- value: DESIRED_VALUE
231
- )
232
- ]
233
- ),
234
- DEFAULT_ROW
235
- ]
236
- )
237
-
238
- CONFIG_2 = PrefabProto::Config.new(
239
- id: 2,
240
- key: "key.2",
241
- rows: [DEFAULT_ROW,]
242
- )
243
-
244
- CONTEXT = {
245
- user: {
246
- id: 1,
247
- email_suffix: 'hotmail.com'
248
- },
249
- team: {
250
- id: 2,
251
- name: 'team-name'
252
- }
253
- }
254
- end