prefab-cloud-ruby 0.24.5 → 0.24.6

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 (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
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Prefab
4
+ # This class contains all the options that can be passed to the Prefab client.
4
5
  class Options
5
6
  attr_reader :api_key
6
7
  attr_reader :logdev
@@ -17,7 +18,6 @@ module Prefab
17
18
  attr_reader :prefab_config_classpath_dir
18
19
  attr_reader :prefab_envs
19
20
  attr_reader :collect_sync_interval
20
- attr_reader :shape_sync_interval
21
21
 
22
22
  DEFAULT_LOG_FORMATTER = proc { |severity, datetime, progname, msg|
23
23
  "#{severity.ljust(5)} #{datetime}:#{' ' if progname}#{progname} #{msg}\n"
@@ -50,7 +50,8 @@ module Prefab
50
50
  DEFAULT_MAX_PATHS = 1_000
51
51
  DEFAULT_MAX_CONTEXT_KEYS = 100_000
52
52
  DEFAULT_MAX_KEYS = 100_000
53
- DEFAULT_MAX_EVALS = 100_000
53
+ DEFAULT_MAX_EXAMPLE_CONTEXTS = 100_000
54
+ DEFAULT_MAX_EVAL_SUMMARIES = 100_000
54
55
 
55
56
  private def init(
56
57
  api_key: ENV['PREFAB_API_KEY'],
@@ -77,9 +78,10 @@ module Prefab
77
78
  collect_max_shapes: DEFAULT_MAX_CONTEXT_KEYS,
78
79
  collect_keys: false,
79
80
  collect_max_keys: DEFAULT_MAX_KEYS,
80
- collect_evaluations: false,
81
- collect_max_evaluations: DEFAULT_MAX_EVALS,
82
- shape_sync_interval: nil
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
83
85
  )
84
86
  @api_key = api_key
85
87
  @logdev = logdev
@@ -103,9 +105,10 @@ module Prefab
103
105
  @collect_max_shapes = collect_max_shapes
104
106
  @collect_keys = collect_keys
105
107
  @collect_max_keys = collect_max_keys
106
- @shape_sync_interval = shape_sync_interval
107
- @collect_evaluations = collect_evaluations
108
- @collect_max_evaluations = collect_max_evaluations
108
+ @collect_example_contexts = collect_example_contexts
109
+ @collect_max_example_contexts = collect_max_example_contexts
110
+ @collect_evaluation_summaries = collect_evaluation_summaries
111
+ @collect_max_evaluation_summaries = collect_max_evaluation_summaries
109
112
  end
110
113
 
111
114
  def initialize(options = {})
@@ -117,27 +120,33 @@ module Prefab
117
120
  end
118
121
 
119
122
  def collect_max_paths
120
- return 0 if !@collect_logs || local_only?
123
+ return 0 unless telemetry_allowed?(@collect_logs)
121
124
 
122
125
  @collect_max_paths
123
126
  end
124
127
 
125
128
  def collect_max_shapes
126
- return 0 if !@collect_shapes || local_only?
129
+ return 0 unless telemetry_allowed?(@collect_shapes)
127
130
 
128
131
  @collect_max_shapes
129
132
  end
130
133
 
131
134
  def collect_max_keys
132
- return 0 if !@collect_keys || local_only?
135
+ return 0 unless telemetry_allowed?(@collect_keys)
133
136
 
134
137
  @collect_max_keys
135
138
  end
136
139
 
137
- def collect_max_evaluations
138
- return 0 if !@collect_evaluations || local_only?
140
+ def collect_max_example_contexts
141
+ return 0 unless telemetry_allowed?(@collect_example_contexts)
139
142
 
140
- @collect_max_evaluations
143
+ @collect_max_example_contexts
144
+ end
145
+
146
+ def collect_max_evaluation_summaries
147
+ return 0 unless telemetry_allowed?(@collect_evaluation_summaries)
148
+
149
+ @collect_max_evaluation_summaries
141
150
  end
142
151
 
143
152
  # https://api.prefab.cloud -> https://api-prefab-cloud.global.ssl.fastly.net
@@ -147,6 +156,10 @@ module Prefab
147
156
 
148
157
  private
149
158
 
159
+ def telemetry_allowed?(option)
160
+ option && !local_only? || option == :force
161
+ end
162
+
150
163
  def remove_trailing_slash(url)
151
164
  url.end_with?('/') ? url[0..-2] : url
152
165
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Prefab
2
4
  module PeriodicSync
3
5
  def sync
@@ -14,25 +16,20 @@ module Prefab
14
16
  def prepare_data
15
17
  to_ship = @data.dup
16
18
  @data.clear
19
+
20
+ on_prepare_data
21
+
17
22
  to_ship
18
23
  end
19
24
 
25
+ def on_prepare_data
26
+ # noop -- override as you wish
27
+ end
28
+
20
29
  def start_periodic_sync(sync_interval)
21
30
  @start_at = Prefab::TimeHelpers.now_in_ms
22
31
 
23
- @sync_interval = if sync_interval.is_a?(Numeric)
24
- proc { sync_interval }
25
- else
26
- sync_interval || ExponentialBackoff.new(initial_delay: 8, max_delay: 60 * 10)
27
- end
28
-
29
- @pool = Concurrent::ThreadPoolExecutor.new(
30
- fallback_policy: :discard,
31
- max_queue: 5,
32
- max_threads: 4,
33
- min_threads: 1,
34
- name: @name
35
- )
32
+ @sync_interval = calculate_sync_interval(sync_interval)
36
33
 
37
34
  Thread.new do
38
35
  log_internal "Initialized #{@name} instance_hash=#{@client.instance_hash}"
@@ -47,5 +44,25 @@ module Prefab
47
44
  def log_internal(message)
48
45
  @client.log.log_internal message, @name, nil, ::Logger::DEBUG
49
46
  end
47
+
48
+ def pool
49
+ @pool ||= Concurrent::ThreadPoolExecutor.new(
50
+ fallback_policy: :discard,
51
+ max_queue: 5,
52
+ max_threads: 4,
53
+ min_threads: 1,
54
+ name: @name
55
+ )
56
+ end
57
+
58
+ private
59
+
60
+ def calculate_sync_interval(sync_interval)
61
+ if sync_interval.is_a?(Numeric)
62
+ proc { sync_interval }
63
+ else
64
+ sync_interval || ExponentialBackoff.new(initial_delay: 8, max_delay: 60 * 5)
65
+ end
66
+ end
50
67
  end
51
68
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Prefab
4
+ # A key-based rate limiter that considers a key to be fresh if it has been
5
+ # seen within the last `duration` seconds.
6
+ #
7
+ # This is used to rate limit the number of times we send a given context
8
+ # to the server.
9
+ #
10
+ # Because expected usage is to immediately `set` on a `fresh?` miss, we do
11
+ # not prune the data structure on `fresh?` calls. Instead, we manually invoke
12
+ # `prune` periodically from the cache consumer.
13
+ class RateLimitCache
14
+ attr_reader :data
15
+
16
+ def initialize(duration)
17
+ @data = Concurrent::Map.new
18
+ @duration = duration
19
+ end
20
+
21
+ def fresh?(key)
22
+ timestamp = @data[key]
23
+
24
+ return false unless timestamp
25
+ return false if Time.now.utc.to_i - timestamp > @duration
26
+
27
+ true
28
+ end
29
+
30
+ def set(key)
31
+ @data[key] = Time.now.utc.to_i
32
+ end
33
+
34
+ def prune
35
+ now = Time.now.utc.to_i
36
+ @data.each_pair do |key, (timestamp, _)|
37
+ @data.delete(key) if now - timestamp > @duration
38
+ end
39
+ end
40
+ end
41
+ end
@@ -44,8 +44,7 @@ module Prefab
44
44
  if v.nil?
45
45
  hash[k] = ConfigRow.new(k, nil, nil, nil)
46
46
  else
47
- config = @resolver.evaluate(v[:config])
48
- value = Prefab::ConfigValueUnwrapper.unwrap(config, k, Prefab::Context.new)
47
+ value = @resolver.evaluate(v[:config])&.unwrapped_value
49
48
  hash[k] = ConfigRow.new(k, value, v[:match], v[:source])
50
49
  end
51
50
  end
@@ -66,8 +65,7 @@ module Prefab
66
65
  if v.nil?
67
66
  elements << 'tombstone'
68
67
  else
69
- config = @resolver.evaluate(v[:config], {})
70
- value = Prefab::ConfigValueUnwrapper.unwrap(config, k, Prefab::Context.new)
68
+ value = @resolver.evaluate(v[:config])&.unwrapped_value
71
69
  elements << value.to_s.slice(0..34).ljust(35)
72
70
  elements << value.class.to_s.slice(0..6).ljust(7)
73
71
  elements << "Match: #{v[:match]}".slice(0..29).ljust(30)
@@ -15,7 +15,7 @@ module Prefab
15
15
 
16
16
  index = variant_index(percent)
17
17
 
18
- @weights[index]
18
+ [@weights[index], index]
19
19
  end
20
20
 
21
21
  def user_percent
@@ -13,16 +13,19 @@ require 'ld-eventsource'
13
13
  require 'prefab_pb'
14
14
  require 'prefab/time_helpers'
15
15
  require 'prefab/error'
16
+ require 'prefab/evaluation'
16
17
  require 'prefab/exponential_backoff'
17
18
  require 'prefab/errors/initialization_timeout_error'
18
19
  require 'prefab/errors/invalid_api_key_error'
19
20
  require 'prefab/errors/missing_default_error'
20
21
  require 'prefab/options'
21
22
  require 'prefab/internal_logger'
22
- require 'prefab/log_path_aggregator'
23
+ require 'prefab/rate_limit_cache'
23
24
  require 'prefab/context_shape_aggregator'
24
- require 'prefab/evaluated_configs_aggregator'
25
25
  require 'prefab/evaluated_keys_aggregator'
26
+ require 'prefab/example_contexts_aggregator'
27
+ require 'prefab/evaluation_summary_aggregator'
28
+ require 'prefab/log_path_aggregator'
26
29
  require 'prefab/sse_logger'
27
30
  require 'prefab/weighted_value_resolver'
28
31
  require 'prefab/config_value_wrapper'
data/lib/prefab_pb.rb CHANGED
@@ -5,7 +5,7 @@
5
5
  require 'google/protobuf'
6
6
 
7
7
 
8
- descriptor_data = "\n\x0cprefab.proto\x12\x06prefab\"W\n\x14\x43onfigServicePointer\x12\x12\n\nproject_id\x18\x01 \x01(\x03\x12\x13\n\x0bstart_at_id\x18\x02 \x01(\x03\x12\x16\n\x0eproject_env_id\x18\x03 \x01(\x03\"\xca\x02\n\x0b\x43onfigValue\x12\r\n\x03int\x18\x01 \x01(\x03H\x00\x12\x10\n\x06string\x18\x02 \x01(\tH\x00\x12\x0f\n\x05\x62ytes\x18\x03 \x01(\x0cH\x00\x12\x10\n\x06\x64ouble\x18\x04 \x01(\x01H\x00\x12\x0e\n\x04\x62ool\x18\x05 \x01(\x08H\x00\x12\x31\n\x0fweighted_values\x18\x06 \x01(\x0b\x32\x16.prefab.WeightedValuesH\x00\x12\x33\n\x10limit_definition\x18\x07 \x01(\x0b\x32\x17.prefab.LimitDefinitionH\x00\x12%\n\tlog_level\x18\t \x01(\x0e\x32\x10.prefab.LogLevelH\x00\x12)\n\x0bstring_list\x18\n \x01(\x0b\x32\x12.prefab.StringListH\x00\x12%\n\tint_range\x18\x0b \x01(\x0b\x32\x10.prefab.IntRangeH\x00\x42\x06\n\x04type\"B\n\x08IntRange\x12\x12\n\x05start\x18\x01 \x01(\x03H\x00\x88\x01\x01\x12\x10\n\x03\x65nd\x18\x02 \x01(\x03H\x01\x88\x01\x01\x42\x08\n\x06_startB\x06\n\x04_end\"\x1c\n\nStringList\x12\x0e\n\x06values\x18\x01 \x03(\t\"C\n\rWeightedValue\x12\x0e\n\x06weight\x18\x01 \x01(\x05\x12\"\n\x05value\x18\x02 \x01(\x0b\x32\x13.prefab.ConfigValue\"~\n\x0eWeightedValues\x12.\n\x0fweighted_values\x18\x01 \x03(\x0b\x32\x15.prefab.WeightedValue\x12\"\n\x15hash_by_property_name\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x18\n\x16_hash_by_property_name\"h\n\x07\x43onfigs\x12\x1f\n\x07\x63onfigs\x18\x01 \x03(\x0b\x32\x0e.prefab.Config\x12<\n\x16\x63onfig_service_pointer\x18\x02 \x01(\x0b\x32\x1c.prefab.ConfigServicePointer\"\xf7\x01\n\x06\x43onfig\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x12\n\nproject_id\x18\x02 \x01(\x03\x12\x0b\n\x03key\x18\x03 \x01(\t\x12%\n\nchanged_by\x18\x04 \x01(\x0b\x32\x11.prefab.ChangedBy\x12\x1f\n\x04rows\x18\x05 \x03(\x0b\x32\x11.prefab.ConfigRow\x12-\n\x10\x61llowable_values\x18\x06 \x03(\x0b\x32\x13.prefab.ConfigValue\x12\'\n\x0b\x63onfig_type\x18\x07 \x01(\x0e\x32\x12.prefab.ConfigType\x12\x14\n\x07\x64raftId\x18\x08 \x01(\x03H\x00\x88\x01\x01\x42\n\n\x08_draftId\"+\n\tChangedBy\x12\x0f\n\x07user_id\x18\x01 \x01(\x03\x12\r\n\x05\x65mail\x18\x02 \x01(\t\"\xe4\x01\n\tConfigRow\x12\x1b\n\x0eproject_env_id\x18\x01 \x01(\x03H\x00\x88\x01\x01\x12(\n\x06values\x18\x02 \x03(\x0b\x32\x18.prefab.ConditionalValue\x12\x35\n\nproperties\x18\x03 \x03(\x0b\x32!.prefab.ConfigRow.PropertiesEntry\x1a\x46\n\x0fPropertiesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\"\n\x05value\x18\x02 \x01(\x0b\x32\x13.prefab.ConfigValue:\x02\x38\x01\x42\x11\n\x0f_project_env_id\"[\n\x10\x43onditionalValue\x12#\n\x08\x63riteria\x18\x01 \x03(\x0b\x32\x11.prefab.Criterion\x12\"\n\x05value\x18\x02 \x01(\x0b\x32\x13.prefab.ConfigValue\"\x94\x03\n\tCriterion\x12\x15\n\rproperty_name\x18\x01 \x01(\t\x12\x35\n\x08operator\x18\x02 \x01(\x0e\x32#.prefab.Criterion.CriterionOperator\x12+\n\x0evalue_to_match\x18\x03 \x01(\x0b\x32\x13.prefab.ConfigValue\"\x8b\x02\n\x11\x43riterionOperator\x12\x0b\n\x07NOT_SET\x10\x00\x12\x11\n\rLOOKUP_KEY_IN\x10\x01\x12\x15\n\x11LOOKUP_KEY_NOT_IN\x10\x02\x12\n\n\x06IN_SEG\x10\x03\x12\x0e\n\nNOT_IN_SEG\x10\x04\x12\x0f\n\x0b\x41LWAYS_TRUE\x10\x05\x12\x12\n\x0ePROP_IS_ONE_OF\x10\x06\x12\x16\n\x12PROP_IS_NOT_ONE_OF\x10\x07\x12\x19\n\x15PROP_ENDS_WITH_ONE_OF\x10\x08\x12!\n\x1dPROP_DOES_NOT_END_WITH_ONE_OF\x10\t\x12\x16\n\x12HIERARCHICAL_MATCH\x10\n\x12\x10\n\x0cIN_INT_RANGE\x10\x0b\"\x89\x01\n\x07Loggers\x12\x1f\n\x07loggers\x18\x01 \x03(\x0b\x32\x0e.prefab.Logger\x12\x10\n\x08start_at\x18\x02 \x01(\x03\x12\x0e\n\x06\x65nd_at\x18\x03 \x01(\x03\x12\x15\n\rinstance_hash\x18\x04 \x01(\t\x12\x16\n\tnamespace\x18\x05 \x01(\tH\x00\x88\x01\x01\x42\x0c\n\n_namespace\"\xd9\x01\n\x06Logger\x12\x13\n\x0blogger_name\x18\x01 \x01(\t\x12\x13\n\x06traces\x18\x02 \x01(\x03H\x00\x88\x01\x01\x12\x13\n\x06\x64\x65\x62ugs\x18\x03 \x01(\x03H\x01\x88\x01\x01\x12\x12\n\x05infos\x18\x04 \x01(\x03H\x02\x88\x01\x01\x12\x12\n\x05warns\x18\x05 \x01(\x03H\x03\x88\x01\x01\x12\x13\n\x06\x65rrors\x18\x06 \x01(\x03H\x04\x88\x01\x01\x12\x13\n\x06\x66\x61tals\x18\x07 \x01(\x03H\x05\x88\x01\x01\x42\t\n\x07_tracesB\t\n\x07_debugsB\x08\n\x06_infosB\x08\n\x06_warnsB\t\n\x07_errorsB\t\n\x07_fatals\"\x16\n\x14LoggerReportResponse\"\xdb\x03\n\rLimitResponse\x12\x0e\n\x06passed\x18\x01 \x01(\x08\x12\x12\n\nexpires_at\x18\x02 \x01(\x03\x12\x16\n\x0e\x65nforced_group\x18\x03 \x01(\t\x12\x16\n\x0e\x63urrent_bucket\x18\x04 \x01(\x03\x12\x14\n\x0cpolicy_group\x18\x05 \x01(\t\x12;\n\x0bpolicy_name\x18\x06 \x01(\x0e\x32&.prefab.LimitResponse.LimitPolicyNames\x12\x14\n\x0cpolicy_limit\x18\x07 \x01(\x05\x12\x0e\n\x06\x61mount\x18\x08 \x01(\x03\x12\x16\n\x0elimit_reset_at\x18\t \x01(\x03\x12\x39\n\x0csafety_level\x18\n \x01(\x0e\x32#.prefab.LimitDefinition.SafetyLevel\"\xa9\x01\n\x10LimitPolicyNames\x12\x0b\n\x07NOT_SET\x10\x00\x12\x14\n\x10SECONDLY_ROLLING\x10\x01\x12\x14\n\x10MINUTELY_ROLLING\x10\x03\x12\x12\n\x0eHOURLY_ROLLING\x10\x05\x12\x11\n\rDAILY_ROLLING\x10\x07\x12\x13\n\x0fMONTHLY_ROLLING\x10\x08\x12\x0c\n\x08INFINITE\x10\t\x12\x12\n\x0eYEARLY_ROLLING\x10\n\"\x99\x02\n\x0cLimitRequest\x12\x12\n\naccount_id\x18\x01 \x01(\x03\x12\x16\n\x0e\x61\x63quire_amount\x18\x02 \x01(\x05\x12\x0e\n\x06groups\x18\x03 \x03(\t\x12:\n\x0elimit_combiner\x18\x04 \x01(\x0e\x32\".prefab.LimitRequest.LimitCombiner\x12\x1e\n\x16\x61llow_partial_response\x18\x05 \x01(\x08\x12\x39\n\x0csafety_level\x18\x06 \x01(\x0e\x32#.prefab.LimitDefinition.SafetyLevel\"6\n\rLimitCombiner\x12\x0b\n\x07NOT_SET\x10\x00\x12\x0b\n\x07MINIMUM\x10\x01\x12\x0b\n\x07MAXIMUM\x10\x02\"/\n\nContextSet\x12!\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x0f.prefab.Context\"\x96\x01\n\x07\x43ontext\x12\x11\n\x04type\x18\x01 \x01(\tH\x00\x88\x01\x01\x12+\n\x06values\x18\x02 \x03(\x0b\x32\x1b.prefab.Context.ValuesEntry\x1a\x42\n\x0bValuesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\"\n\x05value\x18\x02 \x01(\x0b\x32\x13.prefab.ConfigValue:\x02\x38\x01\x42\x07\n\x05_type\"\x93\x01\n\x08Identity\x12\x13\n\x06lookup\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x34\n\nattributes\x18\x02 \x03(\x0b\x32 .prefab.Identity.AttributesEntry\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\t\n\x07_lookup\"\x89\x01\n\x11\x43lientConfigValue\x12\x10\n\x03int\x18\x01 \x01(\x03H\x00\x88\x01\x01\x12\x13\n\x06string\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x64ouble\x18\x03 \x01(\x01H\x02\x88\x01\x01\x12\x11\n\x04\x62ool\x18\x04 \x01(\x08H\x03\x88\x01\x01\x42\x06\n\x04_intB\t\n\x07_stringB\t\n\x07_doubleB\x07\n\x05_bool\"\x94\x01\n\x11\x43onfigEvaluations\x12\x35\n\x06values\x18\x01 \x03(\x0b\x32%.prefab.ConfigEvaluations.ValuesEntry\x1aH\n\x0bValuesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12(\n\x05value\x18\x02 \x01(\x0b\x32\x19.prefab.ClientConfigValue:\x02\x38\x01\"\xa8\x02\n\x0fLimitDefinition\x12;\n\x0bpolicy_name\x18\x02 \x01(\x0e\x32&.prefab.LimitResponse.LimitPolicyNames\x12\r\n\x05limit\x18\x03 \x01(\x05\x12\r\n\x05\x62urst\x18\x04 \x01(\x05\x12\x12\n\naccount_id\x18\x05 \x01(\x03\x12\x15\n\rlast_modified\x18\x06 \x01(\x03\x12\x12\n\nreturnable\x18\x07 \x01(\x08\x12\x39\n\x0csafety_level\x18\x08 \x01(\x0e\x32#.prefab.LimitDefinition.SafetyLevel\"@\n\x0bSafetyLevel\x12\x0b\n\x07NOT_SET\x10\x00\x12\x12\n\x0eL4_BEST_EFFORT\x10\x04\x12\x10\n\x0cL5_BOMBPROOF\x10\x05\"@\n\x10LimitDefinitions\x12,\n\x0b\x64\x65\x66initions\x18\x01 \x03(\x0b\x32\x17.prefab.LimitDefinition\"\x8a\x01\n\x0f\x42ufferedRequest\x12\x12\n\naccount_id\x18\x01 \x01(\x03\x12\x0e\n\x06method\x18\x02 \x01(\t\x12\x0b\n\x03uri\x18\x03 \x01(\t\x12\x0c\n\x04\x62ody\x18\x04 \x01(\t\x12\x14\n\x0climit_groups\x18\x05 \x03(\t\x12\x14\n\x0c\x63ontent_type\x18\x06 \x01(\t\x12\x0c\n\x04\x66ifo\x18\x07 \x01(\x08\"\x94\x01\n\x0c\x42\x61tchRequest\x12\x12\n\naccount_id\x18\x01 \x01(\x03\x12\x0e\n\x06method\x18\x02 \x01(\t\x12\x0b\n\x03uri\x18\x03 \x01(\t\x12\x0c\n\x04\x62ody\x18\x04 \x01(\t\x12\x14\n\x0climit_groups\x18\x05 \x03(\t\x12\x16\n\x0e\x62\x61tch_template\x18\x06 \x01(\t\x12\x17\n\x0f\x62\x61tch_separator\x18\x07 \x01(\t\" \n\rBasicResponse\x12\x0f\n\x07message\x18\x01 \x01(\t\"3\n\x10\x43reationResponse\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x0e\n\x06new_id\x18\x02 \x01(\x03\"h\n\x07IdBlock\x12\x12\n\nproject_id\x18\x01 \x01(\x03\x12\x16\n\x0eproject_env_id\x18\x02 \x01(\x03\x12\x15\n\rsequence_name\x18\x03 \x01(\t\x12\r\n\x05start\x18\x04 \x01(\x03\x12\x0b\n\x03\x65nd\x18\x05 \x01(\x03\"a\n\x0eIdBlockRequest\x12\x12\n\nproject_id\x18\x01 \x01(\x03\x12\x16\n\x0eproject_env_id\x18\x02 \x01(\x03\x12\x15\n\rsequence_name\x18\x03 \x01(\t\x12\x0c\n\x04size\x18\x04 \x01(\x03\"\x8a\x01\n\x0c\x43ontextShape\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x39\n\x0b\x66ield_types\x18\x02 \x03(\x0b\x32$.prefab.ContextShape.FieldTypesEntry\x1a\x31\n\x0f\x46ieldTypesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01\"[\n\rContextShapes\x12$\n\x06shapes\x18\x01 \x03(\x0b\x32\x14.prefab.ContextShape\x12\x16\n\tnamespace\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0c\n\n_namespace\"C\n\rEvaluatedKeys\x12\x0c\n\x04keys\x18\x01 \x03(\t\x12\x16\n\tnamespace\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0c\n\n_namespace\"\x93\x01\n\x0f\x45valuatedConfig\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x16\n\x0e\x63onfig_version\x18\x02 \x01(\x03\x12#\n\x06result\x18\x03 \x01(\x0b\x32\x13.prefab.ConfigValue\x12#\n\x07\x63ontext\x18\x04 \x01(\x0b\x32\x12.prefab.ContextSet\x12\x11\n\ttimestamp\x18\x05 \x01(\x03\"<\n\x10\x45valuatedConfigs\x12(\n\x07\x63onfigs\x18\x01 \x03(\x0b\x32\x17.prefab.EvaluatedConfig*u\n\nConfigType\x12\x17\n\x13NOT_SET_CONFIG_TYPE\x10\x00\x12\n\n\x06\x43ONFIG\x10\x01\x12\x10\n\x0c\x46\x45\x41TURE_FLAG\x10\x02\x12\r\n\tLOG_LEVEL\x10\x03\x12\x0b\n\x07SEGMENT\x10\x04\x12\x14\n\x10LIMIT_DEFINITION\x10\x05*a\n\x08LogLevel\x12\x15\n\x11NOT_SET_LOG_LEVEL\x10\x00\x12\t\n\x05TRACE\x10\x01\x12\t\n\x05\x44\x45\x42UG\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\x08\n\x04WARN\x10\x05\x12\t\n\x05\x45RROR\x10\x06\x12\t\n\x05\x46\x41TAL\x10\t*G\n\tOnFailure\x12\x0b\n\x07NOT_SET\x10\x00\x12\x10\n\x0cLOG_AND_PASS\x10\x01\x12\x10\n\x0cLOG_AND_FAIL\x10\x02\x12\t\n\x05THROW\x10\x03\x42\x1d\n\x13\x63loud.prefab.domainB\x06Prefabb\x06proto3"
8
+ descriptor_data = "\n\x0cprefab.proto\x12\x06prefab\"W\n\x14\x43onfigServicePointer\x12\x12\n\nproject_id\x18\x01 \x01(\x03\x12\x13\n\x0bstart_at_id\x18\x02 \x01(\x03\x12\x16\n\x0eproject_env_id\x18\x03 \x01(\x03\"\xca\x02\n\x0b\x43onfigValue\x12\r\n\x03int\x18\x01 \x01(\x03H\x00\x12\x10\n\x06string\x18\x02 \x01(\tH\x00\x12\x0f\n\x05\x62ytes\x18\x03 \x01(\x0cH\x00\x12\x10\n\x06\x64ouble\x18\x04 \x01(\x01H\x00\x12\x0e\n\x04\x62ool\x18\x05 \x01(\x08H\x00\x12\x31\n\x0fweighted_values\x18\x06 \x01(\x0b\x32\x16.prefab.WeightedValuesH\x00\x12\x33\n\x10limit_definition\x18\x07 \x01(\x0b\x32\x17.prefab.LimitDefinitionH\x00\x12%\n\tlog_level\x18\t \x01(\x0e\x32\x10.prefab.LogLevelH\x00\x12)\n\x0bstring_list\x18\n \x01(\x0b\x32\x12.prefab.StringListH\x00\x12%\n\tint_range\x18\x0b \x01(\x0b\x32\x10.prefab.IntRangeH\x00\x42\x06\n\x04type\"B\n\x08IntRange\x12\x12\n\x05start\x18\x01 \x01(\x03H\x00\x88\x01\x01\x12\x10\n\x03\x65nd\x18\x02 \x01(\x03H\x01\x88\x01\x01\x42\x08\n\x06_startB\x06\n\x04_end\"\x1c\n\nStringList\x12\x0e\n\x06values\x18\x01 \x03(\t\"C\n\rWeightedValue\x12\x0e\n\x06weight\x18\x01 \x01(\x05\x12\"\n\x05value\x18\x02 \x01(\x0b\x32\x13.prefab.ConfigValue\"~\n\x0eWeightedValues\x12.\n\x0fweighted_values\x18\x01 \x03(\x0b\x32\x15.prefab.WeightedValue\x12\"\n\x15hash_by_property_name\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x18\n\x16_hash_by_property_name\"h\n\x07\x43onfigs\x12\x1f\n\x07\x63onfigs\x18\x01 \x03(\x0b\x32\x0e.prefab.Config\x12<\n\x16\x63onfig_service_pointer\x18\x02 \x01(\x0b\x32\x1c.prefab.ConfigServicePointer\"\xf7\x01\n\x06\x43onfig\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x12\n\nproject_id\x18\x02 \x01(\x03\x12\x0b\n\x03key\x18\x03 \x01(\t\x12%\n\nchanged_by\x18\x04 \x01(\x0b\x32\x11.prefab.ChangedBy\x12\x1f\n\x04rows\x18\x05 \x03(\x0b\x32\x11.prefab.ConfigRow\x12-\n\x10\x61llowable_values\x18\x06 \x03(\x0b\x32\x13.prefab.ConfigValue\x12\'\n\x0b\x63onfig_type\x18\x07 \x01(\x0e\x32\x12.prefab.ConfigType\x12\x14\n\x07\x64raftId\x18\x08 \x01(\x03H\x00\x88\x01\x01\x42\n\n\x08_draftId\"+\n\tChangedBy\x12\x0f\n\x07user_id\x18\x01 \x01(\x03\x12\r\n\x05\x65mail\x18\x02 \x01(\t\"\xe4\x01\n\tConfigRow\x12\x1b\n\x0eproject_env_id\x18\x01 \x01(\x03H\x00\x88\x01\x01\x12(\n\x06values\x18\x02 \x03(\x0b\x32\x18.prefab.ConditionalValue\x12\x35\n\nproperties\x18\x03 \x03(\x0b\x32!.prefab.ConfigRow.PropertiesEntry\x1a\x46\n\x0fPropertiesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\"\n\x05value\x18\x02 \x01(\x0b\x32\x13.prefab.ConfigValue:\x02\x38\x01\x42\x11\n\x0f_project_env_id\"[\n\x10\x43onditionalValue\x12#\n\x08\x63riteria\x18\x01 \x03(\x0b\x32\x11.prefab.Criterion\x12\"\n\x05value\x18\x02 \x01(\x0b\x32\x13.prefab.ConfigValue\"\x94\x03\n\tCriterion\x12\x15\n\rproperty_name\x18\x01 \x01(\t\x12\x35\n\x08operator\x18\x02 \x01(\x0e\x32#.prefab.Criterion.CriterionOperator\x12+\n\x0evalue_to_match\x18\x03 \x01(\x0b\x32\x13.prefab.ConfigValue\"\x8b\x02\n\x11\x43riterionOperator\x12\x0b\n\x07NOT_SET\x10\x00\x12\x11\n\rLOOKUP_KEY_IN\x10\x01\x12\x15\n\x11LOOKUP_KEY_NOT_IN\x10\x02\x12\n\n\x06IN_SEG\x10\x03\x12\x0e\n\nNOT_IN_SEG\x10\x04\x12\x0f\n\x0b\x41LWAYS_TRUE\x10\x05\x12\x12\n\x0ePROP_IS_ONE_OF\x10\x06\x12\x16\n\x12PROP_IS_NOT_ONE_OF\x10\x07\x12\x19\n\x15PROP_ENDS_WITH_ONE_OF\x10\x08\x12!\n\x1dPROP_DOES_NOT_END_WITH_ONE_OF\x10\t\x12\x16\n\x12HIERARCHICAL_MATCH\x10\n\x12\x10\n\x0cIN_INT_RANGE\x10\x0b\"\x89\x01\n\x07Loggers\x12\x1f\n\x07loggers\x18\x01 \x03(\x0b\x32\x0e.prefab.Logger\x12\x10\n\x08start_at\x18\x02 \x01(\x03\x12\x0e\n\x06\x65nd_at\x18\x03 \x01(\x03\x12\x15\n\rinstance_hash\x18\x04 \x01(\t\x12\x16\n\tnamespace\x18\x05 \x01(\tH\x00\x88\x01\x01\x42\x0c\n\n_namespace\"\xd9\x01\n\x06Logger\x12\x13\n\x0blogger_name\x18\x01 \x01(\t\x12\x13\n\x06traces\x18\x02 \x01(\x03H\x00\x88\x01\x01\x12\x13\n\x06\x64\x65\x62ugs\x18\x03 \x01(\x03H\x01\x88\x01\x01\x12\x12\n\x05infos\x18\x04 \x01(\x03H\x02\x88\x01\x01\x12\x12\n\x05warns\x18\x05 \x01(\x03H\x03\x88\x01\x01\x12\x13\n\x06\x65rrors\x18\x06 \x01(\x03H\x04\x88\x01\x01\x12\x13\n\x06\x66\x61tals\x18\x07 \x01(\x03H\x05\x88\x01\x01\x42\t\n\x07_tracesB\t\n\x07_debugsB\x08\n\x06_infosB\x08\n\x06_warnsB\t\n\x07_errorsB\t\n\x07_fatals\"\x16\n\x14LoggerReportResponse\"\xdb\x03\n\rLimitResponse\x12\x0e\n\x06passed\x18\x01 \x01(\x08\x12\x12\n\nexpires_at\x18\x02 \x01(\x03\x12\x16\n\x0e\x65nforced_group\x18\x03 \x01(\t\x12\x16\n\x0e\x63urrent_bucket\x18\x04 \x01(\x03\x12\x14\n\x0cpolicy_group\x18\x05 \x01(\t\x12;\n\x0bpolicy_name\x18\x06 \x01(\x0e\x32&.prefab.LimitResponse.LimitPolicyNames\x12\x14\n\x0cpolicy_limit\x18\x07 \x01(\x05\x12\x0e\n\x06\x61mount\x18\x08 \x01(\x03\x12\x16\n\x0elimit_reset_at\x18\t \x01(\x03\x12\x39\n\x0csafety_level\x18\n \x01(\x0e\x32#.prefab.LimitDefinition.SafetyLevel\"\xa9\x01\n\x10LimitPolicyNames\x12\x0b\n\x07NOT_SET\x10\x00\x12\x14\n\x10SECONDLY_ROLLING\x10\x01\x12\x14\n\x10MINUTELY_ROLLING\x10\x03\x12\x12\n\x0eHOURLY_ROLLING\x10\x05\x12\x11\n\rDAILY_ROLLING\x10\x07\x12\x13\n\x0fMONTHLY_ROLLING\x10\x08\x12\x0c\n\x08INFINITE\x10\t\x12\x12\n\x0eYEARLY_ROLLING\x10\n\"\x99\x02\n\x0cLimitRequest\x12\x12\n\naccount_id\x18\x01 \x01(\x03\x12\x16\n\x0e\x61\x63quire_amount\x18\x02 \x01(\x05\x12\x0e\n\x06groups\x18\x03 \x03(\t\x12:\n\x0elimit_combiner\x18\x04 \x01(\x0e\x32\".prefab.LimitRequest.LimitCombiner\x12\x1e\n\x16\x61llow_partial_response\x18\x05 \x01(\x08\x12\x39\n\x0csafety_level\x18\x06 \x01(\x0e\x32#.prefab.LimitDefinition.SafetyLevel\"6\n\rLimitCombiner\x12\x0b\n\x07NOT_SET\x10\x00\x12\x0b\n\x07MINIMUM\x10\x01\x12\x0b\n\x07MAXIMUM\x10\x02\"/\n\nContextSet\x12!\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x0f.prefab.Context\"\x96\x01\n\x07\x43ontext\x12\x11\n\x04type\x18\x01 \x01(\tH\x00\x88\x01\x01\x12+\n\x06values\x18\x02 \x03(\x0b\x32\x1b.prefab.Context.ValuesEntry\x1a\x42\n\x0bValuesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\"\n\x05value\x18\x02 \x01(\x0b\x32\x13.prefab.ConfigValue:\x02\x38\x01\x42\x07\n\x05_type\"\x93\x01\n\x08Identity\x12\x13\n\x06lookup\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x34\n\nattributes\x18\x02 \x03(\x0b\x32 .prefab.Identity.AttributesEntry\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\t\n\x07_lookup\"\xc1\x01\n\x11\x43lientConfigValue\x12\x10\n\x03int\x18\x01 \x01(\x03H\x00\x88\x01\x01\x12\x13\n\x06string\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x64ouble\x18\x03 \x01(\x01H\x02\x88\x01\x01\x12\x11\n\x04\x62ool\x18\x04 \x01(\x08H\x03\x88\x01\x01\x12(\n\tlog_level\x18\x05 \x01(\x0e\x32\x10.prefab.LogLevelH\x04\x88\x01\x01\x42\x06\n\x04_intB\t\n\x07_stringB\t\n\x07_doubleB\x07\n\x05_boolB\x0c\n\n_log_level\"\x94\x01\n\x11\x43onfigEvaluations\x12\x35\n\x06values\x18\x01 \x03(\x0b\x32%.prefab.ConfigEvaluations.ValuesEntry\x1aH\n\x0bValuesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12(\n\x05value\x18\x02 \x01(\x0b\x32\x19.prefab.ClientConfigValue:\x02\x38\x01\"\xa8\x02\n\x0fLimitDefinition\x12;\n\x0bpolicy_name\x18\x02 \x01(\x0e\x32&.prefab.LimitResponse.LimitPolicyNames\x12\r\n\x05limit\x18\x03 \x01(\x05\x12\r\n\x05\x62urst\x18\x04 \x01(\x05\x12\x12\n\naccount_id\x18\x05 \x01(\x03\x12\x15\n\rlast_modified\x18\x06 \x01(\x03\x12\x12\n\nreturnable\x18\x07 \x01(\x08\x12\x39\n\x0csafety_level\x18\x08 \x01(\x0e\x32#.prefab.LimitDefinition.SafetyLevel\"@\n\x0bSafetyLevel\x12\x0b\n\x07NOT_SET\x10\x00\x12\x12\n\x0eL4_BEST_EFFORT\x10\x04\x12\x10\n\x0cL5_BOMBPROOF\x10\x05\"@\n\x10LimitDefinitions\x12,\n\x0b\x64\x65\x66initions\x18\x01 \x03(\x0b\x32\x17.prefab.LimitDefinition\"\x8a\x01\n\x0f\x42ufferedRequest\x12\x12\n\naccount_id\x18\x01 \x01(\x03\x12\x0e\n\x06method\x18\x02 \x01(\t\x12\x0b\n\x03uri\x18\x03 \x01(\t\x12\x0c\n\x04\x62ody\x18\x04 \x01(\t\x12\x14\n\x0climit_groups\x18\x05 \x03(\t\x12\x14\n\x0c\x63ontent_type\x18\x06 \x01(\t\x12\x0c\n\x04\x66ifo\x18\x07 \x01(\x08\"\x94\x01\n\x0c\x42\x61tchRequest\x12\x12\n\naccount_id\x18\x01 \x01(\x03\x12\x0e\n\x06method\x18\x02 \x01(\t\x12\x0b\n\x03uri\x18\x03 \x01(\t\x12\x0c\n\x04\x62ody\x18\x04 \x01(\t\x12\x14\n\x0climit_groups\x18\x05 \x03(\t\x12\x16\n\x0e\x62\x61tch_template\x18\x06 \x01(\t\x12\x17\n\x0f\x62\x61tch_separator\x18\x07 \x01(\t\" \n\rBasicResponse\x12\x0f\n\x07message\x18\x01 \x01(\t\"3\n\x10\x43reationResponse\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x0e\n\x06new_id\x18\x02 \x01(\x03\"h\n\x07IdBlock\x12\x12\n\nproject_id\x18\x01 \x01(\x03\x12\x16\n\x0eproject_env_id\x18\x02 \x01(\x03\x12\x15\n\rsequence_name\x18\x03 \x01(\t\x12\r\n\x05start\x18\x04 \x01(\x03\x12\x0b\n\x03\x65nd\x18\x05 \x01(\x03\"a\n\x0eIdBlockRequest\x12\x12\n\nproject_id\x18\x01 \x01(\x03\x12\x16\n\x0eproject_env_id\x18\x02 \x01(\x03\x12\x15\n\rsequence_name\x18\x03 \x01(\t\x12\x0c\n\x04size\x18\x04 \x01(\x03\"\x8a\x01\n\x0c\x43ontextShape\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x39\n\x0b\x66ield_types\x18\x02 \x03(\x0b\x32$.prefab.ContextShape.FieldTypesEntry\x1a\x31\n\x0f\x46ieldTypesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01\"[\n\rContextShapes\x12$\n\x06shapes\x18\x01 \x03(\x0b\x32\x14.prefab.ContextShape\x12\x16\n\tnamespace\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0c\n\n_namespace\"C\n\rEvaluatedKeys\x12\x0c\n\x04keys\x18\x01 \x03(\t\x12\x16\n\tnamespace\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0c\n\n_namespace\"\x93\x01\n\x0f\x45valuatedConfig\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x16\n\x0e\x63onfig_version\x18\x02 \x01(\x03\x12#\n\x06result\x18\x03 \x01(\x0b\x32\x13.prefab.ConfigValue\x12#\n\x07\x63ontext\x18\x04 \x01(\x0b\x32\x12.prefab.ContextSet\x12\x11\n\ttimestamp\x18\x05 \x01(\x03\"<\n\x10\x45valuatedConfigs\x12(\n\x07\x63onfigs\x18\x01 \x03(\x0b\x32\x17.prefab.EvaluatedConfig\"\xb1\x03\n\x17\x43onfigEvaluationCounter\x12\r\n\x05\x63ount\x18\x01 \x01(\x03\x12\x11\n\tconfig_id\x18\x02 \x01(\x03\x12\x1b\n\x0eselected_index\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x30\n\x0eselected_value\x18\x04 \x01(\x0b\x32\x13.prefab.ConfigValueH\x01\x88\x01\x01\x12\x1d\n\x10\x63onfig_row_index\x18\x05 \x01(\rH\x02\x88\x01\x01\x12$\n\x17\x63onditional_value_index\x18\x06 \x01(\rH\x03\x88\x01\x01\x12!\n\x14weighted_value_index\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x36\n\x06reason\x18\x08 \x01(\x0e\x32&.prefab.ConfigEvaluationCounter.Reason\"\x15\n\x06Reason\x12\x0b\n\x07UNKNOWN\x10\x00\x42\x11\n\x0f_selected_indexB\x11\n\x0f_selected_valueB\x13\n\x11_config_row_indexB\x1a\n\x18_conditional_value_indexB\x17\n\x15_weighted_value_index\"{\n\x17\x43onfigEvaluationSummary\x12\x0b\n\x03key\x18\x01 \x01(\t\x12 \n\x04type\x18\x02 \x01(\x0e\x32\x12.prefab.ConfigType\x12\x31\n\x08\x63ounters\x18\x03 \x03(\x0b\x32\x1f.prefab.ConfigEvaluationCounter\"k\n\x19\x43onfigEvaluationSummaries\x12\r\n\x05start\x18\x01 \x01(\x03\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x03\x12\x32\n\tsummaries\x18\x03 \x03(\x0b\x32\x1f.prefab.ConfigEvaluationSummary\"\xb5\x01\n\x0eTelemetryEvent\x12\x36\n\tsummaries\x18\x02 \x01(\x0b\x32!.prefab.ConfigEvaluationSummariesH\x00\x12\x33\n\x10\x65xample_contexts\x18\x03 \x01(\x0b\x32\x17.prefab.ExampleContextsH\x00\x12+\n\x0c\x63lient_stats\x18\x04 \x01(\x0b\x32\x13.prefab.ClientStatsH\x00\x42\t\n\x07payload\"P\n\x0fTelemetryEvents\x12\x15\n\rinstance_hash\x18\x01 \x01(\t\x12&\n\x06\x65vents\x18\x02 \x03(\x0b\x32\x16.prefab.TelemetryEvent\"*\n\x17TelemetryEventsResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\";\n\x0f\x45xampleContexts\x12(\n\x08\x65xamples\x18\x01 \x03(\x0b\x32\x16.prefab.ExampleContext\"K\n\x0e\x45xampleContext\x12\x11\n\ttimestamp\x18\x01 \x01(\x03\x12&\n\ncontextSet\x18\x02 \x01(\x0b\x32\x12.prefab.ContextSet\"F\n\x0b\x43lientStats\x12\r\n\x05start\x18\x01 \x01(\x03\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x03\x12\x1b\n\x13\x64ropped_event_count\x18\x03 \x01(\x04*u\n\nConfigType\x12\x17\n\x13NOT_SET_CONFIG_TYPE\x10\x00\x12\n\n\x06\x43ONFIG\x10\x01\x12\x10\n\x0c\x46\x45\x41TURE_FLAG\x10\x02\x12\r\n\tLOG_LEVEL\x10\x03\x12\x0b\n\x07SEGMENT\x10\x04\x12\x14\n\x10LIMIT_DEFINITION\x10\x05*a\n\x08LogLevel\x12\x15\n\x11NOT_SET_LOG_LEVEL\x10\x00\x12\t\n\x05TRACE\x10\x01\x12\t\n\x05\x44\x45\x42UG\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\x08\n\x04WARN\x10\x05\x12\t\n\x05\x45RROR\x10\x06\x12\t\n\x05\x46\x41TAL\x10\t*G\n\tOnFailure\x12\x0b\n\x07NOT_SET\x10\x00\x12\x10\n\x0cLOG_AND_PASS\x10\x01\x12\x10\n\x0cLOG_AND_FAIL\x10\x02\x12\t\n\x05THROW\x10\x03\x42\x1d\n\x13\x63loud.prefab.domainB\x06Prefabb\x06proto3"
9
9
 
10
10
  pool = Google::Protobuf::DescriptorPool.generated_pool
11
11
 
@@ -71,6 +71,16 @@ module PrefabProto
71
71
  EvaluatedKeys = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.EvaluatedKeys").msgclass
72
72
  EvaluatedConfig = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.EvaluatedConfig").msgclass
73
73
  EvaluatedConfigs = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.EvaluatedConfigs").msgclass
74
+ ConfigEvaluationCounter = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.ConfigEvaluationCounter").msgclass
75
+ ConfigEvaluationCounter::Reason = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.ConfigEvaluationCounter.Reason").enummodule
76
+ ConfigEvaluationSummary = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.ConfigEvaluationSummary").msgclass
77
+ ConfigEvaluationSummaries = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.ConfigEvaluationSummaries").msgclass
78
+ TelemetryEvent = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.TelemetryEvent").msgclass
79
+ TelemetryEvents = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.TelemetryEvents").msgclass
80
+ TelemetryEventsResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.TelemetryEventsResponse").msgclass
81
+ ExampleContexts = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.ExampleContexts").msgclass
82
+ ExampleContext = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.ExampleContext").msgclass
83
+ ClientStats = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.ClientStats").msgclass
74
84
  ConfigType = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.ConfigType").enummodule
75
85
  LogLevel = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.LogLevel").enummodule
76
86
  OnFailure = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.OnFailure").enummodule
@@ -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.5 ruby lib
5
+ # stub: prefab-cloud-ruby 0.24.6 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "prefab-cloud-ruby".freeze
9
- s.version = "0.24.5"
9
+ s.version = "0.24.6"
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-10"
14
+ s.date = "2023-07-31"
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,8 +52,10 @@ 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_configs_aggregator.rb",
56
55
  "lib/prefab/evaluated_keys_aggregator.rb",
56
+ "lib/prefab/evaluation.rb",
57
+ "lib/prefab/evaluation_summary_aggregator.rb",
58
+ "lib/prefab/example_contexts_aggregator.rb",
57
59
  "lib/prefab/exponential_backoff.rb",
58
60
  "lib/prefab/feature_flag_client.rb",
59
61
  "lib/prefab/http_connection.rb",
@@ -66,6 +68,7 @@ Gem::Specification.new do |s|
66
68
  "lib/prefab/noop_stats.rb",
67
69
  "lib/prefab/options.rb",
68
70
  "lib/prefab/periodic_sync.rb",
71
+ "lib/prefab/rate_limit_cache.rb",
69
72
  "lib/prefab/resolved_config_presenter.rb",
70
73
  "lib/prefab/sse_logger.rb",
71
74
  "lib/prefab/time_helpers.rb",
@@ -77,6 +80,10 @@ Gem::Specification.new do |s|
77
80
  "test/.prefab.unit_tests.config.yaml",
78
81
  "test/integration_test.rb",
79
82
  "test/integration_test_helpers.rb",
83
+ "test/support/common_helpers.rb",
84
+ "test/support/mock_base_client.rb",
85
+ "test/support/mock_config_client.rb",
86
+ "test/support/mock_config_loader.rb",
80
87
  "test/test_client.rb",
81
88
  "test/test_config_client.rb",
82
89
  "test/test_config_loader.rb",
@@ -86,8 +93,9 @@ Gem::Specification.new do |s|
86
93
  "test/test_context_shape.rb",
87
94
  "test/test_context_shape_aggregator.rb",
88
95
  "test/test_criteria_evaluator.rb",
89
- "test/test_evaluated_configs_aggregator.rb",
90
96
  "test/test_evaluated_keys_aggregator.rb",
97
+ "test/test_evaluation_summary_aggregator.rb",
98
+ "test/test_example_contexts_aggregator.rb",
91
99
  "test/test_exponential_backoff.rb",
92
100
  "test/test_feature_flag_client.rb",
93
101
  "test/test_helper.rb",
@@ -96,6 +104,7 @@ Gem::Specification.new do |s|
96
104
  "test/test_log_path_aggregator.rb",
97
105
  "test/test_logger.rb",
98
106
  "test/test_options.rb",
107
+ "test/test_rate_limit_cache.rb",
99
108
  "test/test_weighted_value_resolver.rb"
100
109
  ]
101
110
  s.homepage = "http://github.com/prefab-cloud/prefab-cloud-ruby".freeze
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CommonHelpers
4
+ def with_env(key, value, &block)
5
+ old_value = ENV.fetch(key, nil)
6
+
7
+ ENV[key] = value
8
+ block.call
9
+ ensure
10
+ ENV[key] = old_value
11
+ end
12
+
13
+ DEFAULT_NEW_CLIENT_OPTIONS = {
14
+ prefab_config_override_dir: 'none',
15
+ prefab_config_classpath_dir: 'test',
16
+ prefab_envs: ['unit_tests'],
17
+ prefab_datasources: Prefab::Options::DATASOURCES::LOCAL_ONLY
18
+ }.freeze
19
+
20
+ def new_client(overrides = {})
21
+ config = overrides.delete(:config)
22
+ project_env_id = overrides.delete(:project_env_id)
23
+
24
+ options = Prefab::Options.new(**DEFAULT_NEW_CLIENT_OPTIONS.merge(overrides))
25
+
26
+ Prefab::Client.new(options).tap do |client|
27
+ inject_config(client, config) if config
28
+
29
+ client.resolver.project_env_id = project_env_id if project_env_id
30
+ end
31
+ end
32
+
33
+ def string_list(values)
34
+ PrefabProto::ConfigValue.new(string_list: PrefabProto::StringList.new(values: values))
35
+ end
36
+
37
+ def inject_config(client, config)
38
+ resolver = client.config_client.instance_variable_get('@config_resolver')
39
+ store = resolver.instance_variable_get('@local_store')
40
+
41
+ Array(config).each do |c|
42
+ store[c.key] = { config: c }
43
+ end
44
+ end
45
+
46
+ def inject_project_env_id(client, project_env_id)
47
+ resolver = client.config_client.instance_variable_get('@config_resolver')
48
+ resolver.project_env_id = project_env_id
49
+ end
50
+
51
+ FakeResponse = Struct.new(:status, :body)
52
+
53
+ def wait_for_post_requests(client, max_wait: 2, sleep_time: 0.01)
54
+ requests = []
55
+
56
+ client.define_singleton_method(:post) do |*params|
57
+ requests.push(params)
58
+
59
+ FakeResponse.new(200, '')
60
+ end
61
+
62
+ yield
63
+
64
+ # let the flush thread run
65
+ wait_time = 0
66
+ while requests.empty?
67
+ wait_time += sleep_time
68
+ sleep sleep_time
69
+
70
+ raise "Waited #{max_wait} seconds for the flush thread to run, but it never did" if wait_time > max_wait
71
+ end
72
+
73
+ requests
74
+ end
75
+
76
+ def assert_summary(client, data)
77
+ raise 'Evaluation summary aggregator not enabled' unless client.evaluation_summary_aggregator
78
+
79
+ assert_equal data, client.evaluation_summary_aggregator.data
80
+ end
81
+
82
+ def assert_example_contexts(client, data)
83
+ raise 'Example contexts aggregator not enabled' unless client.example_contexts_aggregator
84
+
85
+ assert_equal data, client.example_contexts_aggregator.data
86
+ end
87
+
88
+ def weighted_values(values_and_weights, hash_by_property_name: 'user.key')
89
+ values = values_and_weights.map do |value, weight|
90
+ weighted_value(value, weight)
91
+ end
92
+
93
+ PrefabProto::WeightedValues.new(weighted_values: values, hash_by_property_name: hash_by_property_name)
94
+ end
95
+
96
+ def weighted_value(string, weight)
97
+ PrefabProto::WeightedValue.new(
98
+ value: PrefabProto::ConfigValue.new(string: string), weight: weight
99
+ )
100
+ end
101
+
102
+ def context(properties)
103
+ Prefab::Context.new(properties)
104
+ end
105
+ end
@@ -0,0 +1,44 @@
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
+ @logger = Prefab::LoggerClient.new($stdout)
13
+ @config_client = MockConfigClient.new
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 log_internal(level, message); end
34
+
35
+ def context_shape_aggregator; end
36
+
37
+ def evaluated_keys_aggregator; end
38
+
39
+ def evaluation_summary_aggregator; end
40
+
41
+ def config_value(key)
42
+ @config_values[key]
43
+ end
44
+ end
@@ -0,0 +1,19 @@
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
@@ -0,0 +1 @@
1
+ # frozen_string_literal: true