prefab-cloud-ruby 1.0.1 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eb91b8be5a01c3bab3312d277a27117c0449713623e1605863486c3809cff0cd
4
- data.tar.gz: 3deaeb9fcb458ef461771865463c5163f8b5c6833a45d1e7af4ec3474f5d04bd
3
+ metadata.gz: 4207ba2ec83b02534caa1d8f23b75c2120c8d277253312a5bd98898c38baf156
4
+ data.tar.gz: 8698e869c65017759a56c01f3ff57bc16b9597cc4d1edc219fc282f0ae371e0c
5
5
  SHA512:
6
- metadata.gz: 2555fc0364fef33c11e3e7866a00ab5df766e859c73baedbb39712ddceb07ec96d0216c5e79dd7336e40951de85b773100864356fd24423f3257a8e3ab40e073
7
- data.tar.gz: 44c9813ede3c95db9820e0247c71c7e4a1e0e21b5981d074025baf546e5f3e7de6362744acedd831437e3b10ca747cdb73ec596ec1394b82ff7e70481ab57a52
6
+ metadata.gz: 31eb4a0e5e132dd3136a48f9afa5f7d7decb73eccb43cc74329e9fc2b3c5a5b7109bc53a3c468e8272ae0d4ad04e3f85fb516eb16b6590325e23046e6f1c9d7b
7
+ data.tar.gz: 9ea6c49f4ffe05d15654143c98dc7f74d405ac00bd919d3ae43547d9aa3ba4c3adcfa54626a0c393fa766cb420af29231032f07dd4da1c53d71c12da2cfc273a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.1.1 - 2023-10-11
4
+
5
+ - Migrate happy-path client-initialization logging to `DEBUG` level rather than `INFO` (#144)
6
+ - Add `ConfigClientPresenter` for logging out stats upon successful client initialization (#144)
7
+ - Add support for default context (#146)
8
+
9
+ ## 1.1.0 - 2023-09-18
10
+
11
+ - Add support for structured logging (#143)
12
+ - Ability to pass a hash of key/value context pairs to any of the user-facing log methods
13
+
3
14
  ## 1.0.1 - 2023-08-17
4
15
 
5
16
  - Bug fix for StringList w/ ExampleContextsAggregator (#141)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.1
1
+ 1.1.1
data/lib/prefab/client.rb CHANGED
@@ -16,13 +16,13 @@ module Prefab
16
16
  @instance_hash = UUID.new.generate
17
17
 
18
18
  if @options.local_only?
19
- log_internal ::Logger::INFO, 'Prefab Running in Local Mode'
19
+ log_internal ::Logger::DEBUG, 'Prefab Running in Local Mode'
20
20
  else
21
21
  @api_key = @options.api_key
22
22
  raise Prefab::Errors::InvalidApiKeyError, @api_key if @api_key.nil? || @api_key.empty? || api_key.count('-') < 1
23
23
 
24
24
  @prefab_api_url = @options.prefab_api_url
25
- log_internal ::Logger::INFO, "Prefab Connecting to: #{@prefab_api_url}"
25
+ log_internal ::Logger::DEBUG, "Prefab Connecting to: #{@prefab_api_url}"
26
26
  end
27
27
 
28
28
  context.clear
@@ -99,8 +99,8 @@ module Prefab
99
99
  resolver.on_update(&block)
100
100
  end
101
101
 
102
- def log_internal(level, msg, path = nil)
103
- log.log_internal msg, path, nil, level
102
+ def log_internal(level, msg, path = nil, **tags)
103
+ log.log_internal msg, path, nil, level, tags
104
104
  end
105
105
 
106
106
  def enabled?(feature_name, jit_context = NO_DEFAULT_PROVIDED)
@@ -28,7 +28,7 @@ module Prefab
28
28
  @initialized_future = Concurrent::Future.execute { @initialization_lock.acquire_read_lock }
29
29
 
30
30
  if @options.local_only?
31
- finish_init!(:local_only)
31
+ finish_init!(:local_only, nil)
32
32
  else
33
33
  load_checkpoint
34
34
  start_checkpointing_thread
@@ -146,18 +146,29 @@ module Prefab
146
146
  @config_resolver.project_env_id = project_env_id
147
147
  starting_highwater_mark = @config_loader.highwater_mark
148
148
 
149
+ default_contexts = configs.default_context&.contexts&.map do |context|
150
+ [
151
+ context.type,
152
+ context.values.keys.map do |k|
153
+ [k, Prefab::ConfigValueUnwrapper.new(context.values[k]).unwrap]
154
+ end.to_h
155
+ ]
156
+ end.to_h
157
+
158
+ @config_resolver.default_context = default_contexts || {}
159
+
149
160
  configs.configs.each do |config|
150
161
  @config_loader.set(config, source)
151
162
  end
152
163
  if @config_loader.highwater_mark > starting_highwater_mark
153
- @base_client.log_internal ::Logger::INFO,
164
+ @base_client.log_internal ::Logger::DEBUG,
154
165
  "Found new checkpoint with highwater id #{@config_loader.highwater_mark} from #{source} in project #{project_id} environment: #{project_env_id} and namespace: '#{@namespace}'"
155
166
  else
156
167
  @base_client.log_internal ::Logger::DEBUG,
157
168
  "Checkpoint with highwater id #{@config_loader.highwater_mark} from #{source}. No changes.", 'load_configs'
158
169
  end
159
170
  @config_resolver.update
160
- finish_init!(source)
171
+ finish_init!(source, project_id)
161
172
  end
162
173
 
163
174
  # A thread that checks for a checkpoint
@@ -170,18 +181,26 @@ module Prefab
170
181
  delta = @checkpoint_freq_secs - (Time.now - started_at)
171
182
  sleep(delta) if delta > 0
172
183
  rescue StandardError => e
173
- @base_client.log_internal ::Logger::INFO, "Issue Checkpointing #{e.message}"
184
+ @base_client.log_internal ::Logger::DEBUG, "Issue Checkpointing #{e.message}"
174
185
  end
175
186
  end
176
187
  end
177
188
 
178
- def finish_init!(source)
189
+ def finish_init!(source, project_id)
179
190
  return unless @initialization_lock.write_locked?
180
191
 
181
- @base_client.log_internal ::Logger::INFO, "Unlocked Config via #{source}"
192
+ @base_client.log_internal ::Logger::DEBUG, "Unlocked Config via #{source}"
182
193
  @initialization_lock.release_write_lock
183
194
  @base_client.log.config_client = self
184
- @base_client.log_internal ::Logger::INFO, to_s
195
+ presenter = Prefab::ConfigClientPresenter.new(
196
+ size: @config_resolver.local_store.size,
197
+ source: source,
198
+ project_id: project_id,
199
+ project_env_id: @config_resolver.project_env_id,
200
+ api_key: @base_client.options.api_key
201
+ )
202
+ @base_client.log_internal ::Logger::INFO, presenter.to_s
203
+ @base_client.log_internal ::Logger::DEBUG, to_s
185
204
  end
186
205
 
187
206
  def start_sse_streaming_connection_thread(start_at_id)
@@ -193,7 +212,7 @@ module Prefab
193
212
  'X-PrefabCloud-Client-Version' => "prefab-cloud-ruby-#{Prefab::VERSION}"
194
213
  }
195
214
  url = "#{@base_client.prefab_api_url}/api/v1/sse/config"
196
- @base_client.log_internal ::Logger::INFO, "SSE Streaming Connect to #{url} start_at #{start_at_id}"
215
+ @base_client.log_internal ::Logger::DEBUG, "SSE Streaming Connect to #{url} start_at #{start_at_id}"
197
216
  @streaming_thread = SSE::Client.new(url,
198
217
  headers: headers,
199
218
  read_timeout: SSE_READ_TIMEOUT,
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Prefab
4
+ class ConfigClientPresenter
5
+ def initialize(size:, source:, project_id:, project_env_id:, api_key:)
6
+ @size = size
7
+ @source = source
8
+ @project_id = project_id
9
+ @project_env_id = project_env_id
10
+ @api_key_id = api_key&.split("-")&.first
11
+ end
12
+
13
+ def to_s
14
+ "Configuration Loaded count=#{@size} source=#{@source} project=#{@project_id} project-env=#{@project_env_id} prefab.api-key-id=#{@api_key_id}"
15
+ end
16
+ end
17
+ end
18
+
@@ -3,6 +3,9 @@
3
3
  module Prefab
4
4
  class ConfigResolver
5
5
  attr_accessor :project_env_id # this will be set by the config_client when it gets an API response
6
+ attr_reader :local_store
7
+
8
+ attr_accessor :default_context
6
9
 
7
10
  def initialize(base_client, config_loader)
8
11
  @lock = Concurrent::ReadWriteLock.new
@@ -11,6 +14,7 @@ module Prefab
11
14
  @project_env_id = 0 # we don't know this yet, it is set from the API results
12
15
  @base_client = base_client
13
16
  @on_update = nil
17
+ @default_context = {}
14
18
  make_local
15
19
  end
16
20
 
@@ -61,7 +65,7 @@ module Prefab
61
65
  properties
62
66
  else
63
67
  Context.merge_with_current(properties)
64
- end
68
+ end.merge_default(default_context || {})
65
69
  end
66
70
 
67
71
  private
@@ -122,6 +122,14 @@ module Prefab
122
122
  contexts[name.to_s] || NamedContext.new(name, {})
123
123
  end
124
124
 
125
+ def merge_default(defaults)
126
+ defaults.keys.each do |name|
127
+ set(name, context(name).merge!(defaults[name]))
128
+ end
129
+
130
+ self
131
+ end
132
+
125
133
  def to_proto(namespace)
126
134
  prefab_context = {
127
135
  'current-time' => ConfigValueWrapper.wrap(Prefab::TimeHelpers.now_in_ms)
@@ -54,7 +54,7 @@ module Prefab
54
54
  end
55
55
  )
56
56
 
57
- result = @client.post('/api/v1/context-shapes', shapes)
57
+ result = post('/api/v1/context-shapes', shapes)
58
58
 
59
59
  log_internal "Uploaded #{to_ship.values.size} shapes: #{result.status}"
60
60
  end
@@ -55,7 +55,7 @@ module Prefab
55
55
  summaries: summaries(to_ship)
56
56
  )
57
57
 
58
- result = @client.post('/api/v1/telemetry', events(summaries_proto))
58
+ result = post('/api/v1/telemetry', events(summaries_proto))
59
59
 
60
60
  log_internal "Uploaded #{to_ship.size} summaries: #{result.status}"
61
61
  end
@@ -45,7 +45,7 @@ module Prefab
45
45
  pool.post do
46
46
  log_internal "Flushing #{to_ship.size} examples"
47
47
 
48
- result = @client.post('/api/v1/telemetry', events(to_ship))
48
+ result = post('/api/v1/telemetry', events(to_ship))
49
49
 
50
50
  log_internal "Uploaded #{to_ship.size} examples: #{result.status}"
51
51
  end
@@ -25,6 +25,9 @@ module Prefab
25
25
 
26
26
  @data = Concurrent::Map.new
27
27
 
28
+ @last_data_sent = nil
29
+ @last_request = nil
30
+
28
31
  start_periodic_sync(sync_interval)
29
32
  end
30
33
 
@@ -55,7 +58,7 @@ module Prefab
55
58
  namespace: @client.namespace
56
59
  )
57
60
 
58
- result = @client.post('/api/v1/known-loggers', loggers)
61
+ result = post('/api/v1/known-loggers', loggers)
59
62
 
60
63
  log_internal "Uploaded #{to_ship.size} paths: #{result.status}"
61
64
  end
@@ -27,26 +27,26 @@ module Prefab
27
27
  @log_path_aggregator = log_path_aggregator
28
28
  end
29
29
 
30
- def add_internal(severity, message, progname, loc, &block)
30
+ def add_internal(severity, message, progname, loc, log_context={}, &block)
31
31
  path_loc = get_loc_path(loc)
32
32
  path = @prefix + path_loc
33
33
 
34
34
  @log_path_aggregator&.push(path_loc, severity)
35
35
 
36
- log(message, path, progname, severity, &block)
36
+ log(message, path, progname, severity, log_context, &block)
37
37
  end
38
38
 
39
- def log_internal(message, path, progname, severity, &block)
39
+ def log_internal(message, path, progname, severity, log_context={}, &block)
40
40
  path = if path
41
41
  "#{INTERNAL_PREFIX}.#{path}"
42
42
  else
43
43
  INTERNAL_PREFIX
44
44
  end
45
45
 
46
- log(message, path, progname, severity, &block)
46
+ log(message, path, progname, severity, log_context, &block)
47
47
  end
48
48
 
49
- def log(message, path, progname, severity)
49
+ def log(message, path, progname, severity, log_context={})
50
50
  severity ||= ::Logger::UNKNOWN
51
51
 
52
52
  return true if @logdev.nil? || severity < level_of(path) || @silences[local_log_id]
@@ -63,29 +63,29 @@ module Prefab
63
63
  end
64
64
 
65
65
  @logdev.write(
66
- format_message(format_severity(severity), Time.now, progname, message, path)
66
+ format_message(format_severity(severity), Time.now, progname, message, path, log_context)
67
67
  )
68
68
  true
69
69
  end
70
70
 
71
- def debug(progname = nil, &block)
72
- add_internal(DEBUG, nil, progname, caller_locations(1, 1)[0], &block)
71
+ def debug(progname = nil, **log_context, &block)
72
+ add_internal(DEBUG, nil, progname, caller_locations(1, 1)[0], log_context, &block)
73
73
  end
74
74
 
75
- def info(progname = nil, &block)
76
- add_internal(INFO, nil, progname, caller_locations(1, 1)[0], &block)
75
+ def info(progname = nil, **log_context, &block)
76
+ add_internal(INFO, nil, progname, caller_locations(1, 1)[0], log_context, &block)
77
77
  end
78
78
 
79
- def warn(progname = nil, &block)
80
- add_internal(WARN, nil, progname, caller_locations(1, 1)[0], &block)
79
+ def warn(progname = nil, **log_context, &block)
80
+ add_internal(WARN, nil, progname, caller_locations(1, 1)[0], log_context, &block)
81
81
  end
82
82
 
83
- def error(progname = nil, &block)
84
- add_internal(ERROR, nil, progname, caller_locations(1, 1)[0], &block)
83
+ def error(progname = nil, **log_context, &block)
84
+ add_internal(ERROR, nil, progname, caller_locations(1, 1)[0], log_context, &block)
85
85
  end
86
86
 
87
- def fatal(progname = nil, &block)
88
- add_internal(FATAL, nil, progname, caller_locations(1, 1)[0], &block)
87
+ def fatal(progname = nil, **log_context, &block)
88
+ add_internal(FATAL, nil, progname, caller_locations(1, 1)[0], log_context, &block)
89
89
  end
90
90
 
91
91
  def debug?
@@ -168,18 +168,17 @@ module Prefab
168
168
  path
169
169
  end
170
170
 
171
- def format_message(severity, datetime, progname, msg, path = nil)
171
+ def format_message(severity, datetime, progname, msg, path = nil, log_context={})
172
172
  formatter = (@formatter || @default_formatter)
173
173
 
174
- if formatter.arity == 5
175
- formatter.call(severity, datetime, progname, msg, path)
176
- else
177
- formatter.call(severity, datetime, join_path_and_progname(path, progname), msg)
178
- end
179
- end
180
-
181
- def join_path_and_progname(path, progname)
182
- (progname.nil? || progname.empty?) ? path : "#{progname}: #{path}"
174
+ formatter.call(
175
+ severity: severity,
176
+ datetime: datetime,
177
+ progname: progname,
178
+ path: path,
179
+ message: msg,
180
+ log_context: log_context
181
+ )
183
182
  end
184
183
  end
185
184
 
@@ -17,17 +17,23 @@ module Prefab
17
17
  attr_reader :prefab_envs
18
18
  attr_reader :collect_sync_interval
19
19
 
20
- DEFAULT_LOG_FORMATTER = proc { |severity, datetime, progname, msg|
21
- "#{severity.ljust(5)} #{datetime}:#{' ' if progname}#{progname} #{msg}\n"
20
+ DEFAULT_LOG_FORMATTER = proc { |data|
21
+ severity = data[:severity]
22
+ datetime = data[:datetime]
23
+ progname = data[:progname]
24
+ path = data[:path]
25
+ msg = data[:message]
26
+ log_context = data[:log_context]
27
+
28
+ progname = (progname.nil? || progname.empty?) ? path : "#{progname}: #{path}"
29
+
30
+ formatted_log_context = log_context.sort.map{|k, v| "#{k}=#{v}" }.join(" ")
31
+ "#{severity.ljust(5)} #{datetime}:#{' ' if progname}#{progname} #{msg}#{log_context.any? ? " " + formatted_log_context : ""}\n"
22
32
  }
23
- JSON_LOG_FORMATTER = proc { |severity, datetime, progname, msg, path|
24
- {
25
- type: severity,
26
- time: datetime,
27
- progname: progname,
28
- message: msg,
29
- path: path
30
- }.compact.to_json << "\n"
33
+
34
+ JSON_LOG_FORMATTER = proc { |data|
35
+ log_context = data.delete(:log_context)
36
+ data.merge(log_context).compact.to_json << "\n"
31
37
  }
32
38
 
33
39
  module ON_INITIALIZATION_FAILURE
@@ -26,6 +26,10 @@ module Prefab
26
26
  # noop -- override as you wish
27
27
  end
28
28
 
29
+ def post(url, data)
30
+ @client.post(url, data)
31
+ end
32
+
29
33
  def start_periodic_sync(sync_interval)
30
34
  @start_at = Prefab::TimeHelpers.now_in_ms
31
35
 
@@ -40,6 +40,7 @@ require 'prefab/http_connection'
40
40
  require 'prefab/context'
41
41
  require 'prefab/logger_client'
42
42
  require 'prefab/client'
43
+ require 'prefab/config_client_presenter'
43
44
  require 'prefab/config_client'
44
45
  require 'prefab/feature_flag_client'
45
46
  require 'prefab/murmer3'
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\"\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"
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\"X\n\x0e\x41piKeyMetadata\x12\x13\n\x06key_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07user_id\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_key_idB\n\n\x08_user_idJ\x04\x08\x02\x10\x03\"\xf8\x01\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\x12\x34\n\x0f\x61pikey_metadata\x18\x03 \x01(\x0b\x32\x16.prefab.ApiKeyMetadataH\x00\x88\x01\x01\x12\x30\n\x0f\x64\x65\x66\x61ult_context\x18\x04 \x01(\x0b\x32\x12.prefab.ContextSetH\x01\x88\x01\x01\x42\x12\n\x10_apikey_metadataB\x12\n\x10_default_context\"\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\x12\x12\n\napi_key_id\x18\x03 \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\"\xa4\x02\n\x11\x43onfigEvaluations\x12\x35\n\x06values\x18\x01 \x03(\x0b\x32%.prefab.ConfigEvaluations.ValuesEntry\x12\x34\n\x0f\x61pikey_metadata\x18\x02 \x01(\x0b\x32\x16.prefab.ApiKeyMetadataH\x00\x88\x01\x01\x12\x30\n\x0f\x64\x65\x66\x61ult_context\x18\x03 \x01(\x0b\x32\x12.prefab.ContextSetH\x01\x88\x01\x01\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\x42\x12\n\x10_apikey_metadataB\x12\n\x10_default_context\"\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\"\xc4\x03\n\x17\x43onfigEvaluationCounter\x12\r\n\x05\x63ount\x18\x01 \x01(\x03\x12\x16\n\tconfig_id\x18\x02 \x01(\x03H\x00\x88\x01\x01\x12\x1b\n\x0eselected_index\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x30\n\x0eselected_value\x18\x04 \x01(\x0b\x32\x13.prefab.ConfigValueH\x02\x88\x01\x01\x12\x1d\n\x10\x63onfig_row_index\x18\x05 \x01(\rH\x03\x88\x01\x01\x12$\n\x17\x63onditional_value_index\x18\x06 \x01(\rH\x04\x88\x01\x01\x12!\n\x14weighted_value_index\x18\x07 \x01(\rH\x05\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\x0c\n\n_config_idB\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\"Z\n\x15LoggersTelemetryEvent\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\"\xe7\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\x12\x30\n\x07loggers\x18\x05 \x01(\x0b\x32\x1d.prefab.LoggersTelemetryEventH\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*\x82\x01\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\x12\x0b\n\x07\x44\x45LETED\x10\x06*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
 
@@ -38,6 +38,7 @@ module PrefabProto
38
38
  StringList = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.StringList").msgclass
39
39
  WeightedValue = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.WeightedValue").msgclass
40
40
  WeightedValues = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.WeightedValues").msgclass
41
+ ApiKeyMetadata = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.ApiKeyMetadata").msgclass
41
42
  Configs = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.Configs").msgclass
42
43
  Config = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.Config").msgclass
43
44
  ChangedBy = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.ChangedBy").msgclass
@@ -75,6 +76,7 @@ module PrefabProto
75
76
  ConfigEvaluationCounter::Reason = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.ConfigEvaluationCounter.Reason").enummodule
76
77
  ConfigEvaluationSummary = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.ConfigEvaluationSummary").msgclass
77
78
  ConfigEvaluationSummaries = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.ConfigEvaluationSummaries").msgclass
79
+ LoggersTelemetryEvent = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.LoggersTelemetryEvent").msgclass
78
80
  TelemetryEvent = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.TelemetryEvent").msgclass
79
81
  TelemetryEvents = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.TelemetryEvents").msgclass
80
82
  TelemetryEventsResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.TelemetryEventsResponse").msgclass
@@ -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 1.0.1 ruby lib
5
+ # stub: prefab-cloud-ruby 1.1.1 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "prefab-cloud-ruby".freeze
9
- s.version = "1.0.1"
9
+ s.version = "1.1.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-08-17"
14
+ s.date = "2023-10-11"
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]
@@ -40,6 +40,7 @@ Gem::Specification.new do |s|
40
40
  "lib/prefab-cloud-ruby.rb",
41
41
  "lib/prefab/client.rb",
42
42
  "lib/prefab/config_client.rb",
43
+ "lib/prefab/config_client_presenter.rb",
43
44
  "lib/prefab/config_loader.rb",
44
45
  "lib/prefab/config_resolver.rb",
45
46
  "lib/prefab/config_value_unwrapper.rb",
@@ -1,18 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class IntegrationTest
4
- attr_reader :func, :input, :expected, :test_client
4
+ attr_reader :func, :input, :expected, :data, :expected_data, :aggregator, :endpoint, :test_client
5
5
 
6
6
  def initialize(test_data)
7
7
  @client_overrides = parse_client_overrides(test_data['client_overrides'])
8
8
  @func = parse_function(test_data['function'])
9
9
  @input = parse_input(test_data['input'])
10
10
  @expected = parse_expected(test_data['expected'])
11
- @test_client = base_client
11
+ @data = test_data['data']
12
+ @expected_data = test_data['expected_data']
13
+ @aggregator = test_data['aggregator']
14
+ @endpoint = test_data['endpoint']
15
+ @test_client = capture_telemetry(base_client)
12
16
  end
13
17
 
14
18
  def test_type
15
- if @input[0] && @input[0].start_with?('log-level.')
19
+ if @data
20
+ :telemetry
21
+ elsif @input[0] && @input[0].start_with?('log-level.')
16
22
  :log_level
17
23
  elsif @expected[:status] == 'raise'
18
24
  :raise
@@ -23,6 +29,18 @@ class IntegrationTest
23
29
  end
24
30
  end
25
31
 
32
+ def last_data_sent
33
+ test_client.last_data_sent
34
+ end
35
+
36
+ def last_post_result
37
+ test_client.last_post_result
38
+ end
39
+
40
+ def last_post_endpoint
41
+ test_client.last_post_endpoint
42
+ end
43
+
26
44
  private
27
45
 
28
46
  def parse_client_overrides(overrides)
@@ -42,6 +60,8 @@ class IntegrationTest
42
60
  end
43
61
 
44
62
  def parse_input(input)
63
+ return nil if input.nil?
64
+
45
65
  if input['key']
46
66
  parse_config_input(input)
47
67
  elsif input['flag']
@@ -62,6 +82,8 @@ class IntegrationTest
62
82
  end
63
83
 
64
84
  def parse_expected(expected)
85
+ return {} if expected.nil?
86
+
65
87
  {
66
88
  status: expected['status'],
67
89
  error: parse_error_type(expected['error']),
@@ -73,6 +95,7 @@ class IntegrationTest
73
95
  def parse_error_type(error_type)
74
96
  case error_type
75
97
  when 'missing_default' then Prefab::Errors::MissingDefaultError
98
+ when 'initialization_timeout' then Prefab::Errors::InitializationTimeoutError
76
99
  end
77
100
  end
78
101
 
@@ -87,7 +110,34 @@ class IntegrationTest
87
110
  prefab_envs: ['unit_tests'],
88
111
  prefab_datasources: Prefab::Options::DATASOURCES::ALL,
89
112
  api_key: ENV['PREFAB_INTEGRATION_TEST_API_KEY'],
90
- prefab_api_url: 'https://api.staging-prefab.cloud'
113
+ prefab_api_url: 'https://api.staging-prefab.cloud',
91
114
  }.merge(@client_overrides))
92
115
  end
116
+
117
+ def capture_telemetry(client)
118
+ client.define_singleton_method(:post) do |url, data|
119
+ client.instance_variable_set(:@last_data_sent, data)
120
+ client.instance_variable_set(:@last_post_endpoint, url)
121
+
122
+ result = super(url, data)
123
+
124
+ client.instance_variable_set(:@last_post_result, result)
125
+
126
+ result
127
+ end
128
+
129
+ client.define_singleton_method(:last_data_sent) do
130
+ client.instance_variable_get(:@last_data_sent)
131
+ end
132
+
133
+ client.define_singleton_method(:last_post_endpoint) do
134
+ client.instance_variable_get(:@last_post_endpoint)
135
+ end
136
+
137
+ client.define_singleton_method(:last_post_result) do
138
+ client.instance_variable_get(:@last_post_result)
139
+ end
140
+
141
+ client
142
+ end
93
143
  end
@@ -33,4 +33,117 @@ module IntegrationTestHelpers
33
33
  .select { |file| file =~ /\.ya?ml$/ }
34
34
  end
35
35
  end
36
+
37
+ def self.prepare_post_data(it)
38
+ case it.aggregator
39
+ when "log_path"
40
+ aggregator = it.test_client.log_path_aggregator
41
+
42
+ it.data.each do |(path, data)|
43
+ data.each_with_index do |count, severity|
44
+ count.times { aggregator.push(path, severity) }
45
+ end
46
+ end
47
+
48
+ expected_loggers = Hash.new { |h, k| h[k] = PrefabProto::Logger.new }
49
+
50
+ it.expected_data.each do |data|
51
+ data["counts"].each do |(severity, count)|
52
+ expected_loggers[data["logger_name"]][severity] = count
53
+ expected_loggers[data["logger_name"]]["logger_name"] = data["logger_name"]
54
+ end
55
+ end
56
+
57
+ [aggregator, ->(data) { data.loggers }, expected_loggers.values]
58
+ when "context_shape"
59
+ aggregator = it.test_client.context_shape_aggregator
60
+
61
+ context = Prefab::Context.new(it.data)
62
+
63
+ aggregator.push(context)
64
+
65
+ expected = it.expected_data.map do |data|
66
+ PrefabProto::ContextShape.new(
67
+ name: data["name"],
68
+ field_types: data["field_types"]
69
+ )
70
+ end
71
+
72
+ [aggregator, ->(data) { data.shapes }, expected]
73
+ when "evaluation_summary"
74
+ aggregator = it.test_client.evaluation_summary_aggregator
75
+
76
+ aggregator.instance_variable_set("@data", Concurrent::Hash.new)
77
+
78
+ it.data.each do |key|
79
+ it.test_client.get(key)
80
+ end
81
+
82
+ expected_data = []
83
+ it.expected_data.each do |data|
84
+ value = if data["value_type"] == "string_list"
85
+ PrefabProto::StringList.new(values: data["value"])
86
+ else
87
+ data["value"]
88
+ end
89
+ expected_data << PrefabProto::ConfigEvaluationSummary.new(
90
+ key: data["key"],
91
+ type: data["type"].to_sym,
92
+ counters: [
93
+ PrefabProto::ConfigEvaluationCounter.new(
94
+ count: data["count"],
95
+ config_id: 0,
96
+ selected_value: PrefabProto::ConfigValue.new(data["value_type"] => value),
97
+ config_row_index: data["summary"]["config_row_index"],
98
+ conditional_value_index: data["summary"]["conditional_value_index"] || 0,
99
+ weighted_value_index: data["summary"]["weighted_value_index"],
100
+ reason: :UNKNOWN
101
+ )
102
+ ]
103
+ )
104
+ end
105
+
106
+ [aggregator, ->(data) {
107
+ data.events[0].summaries.summaries.each { |e|
108
+ e.counters.each { |c|
109
+ c.config_id = 0
110
+ }
111
+ }
112
+ }, expected_data]
113
+ when "example_contexts"
114
+ aggregator = it.test_client.example_contexts_aggregator
115
+
116
+ it.data.each do |hash|
117
+ aggregator.record(Prefab::Context.new(hash))
118
+ end
119
+
120
+ expected_data = []
121
+ it.expected_data.each do |data|
122
+ expected_data << PrefabProto::ExampleContext.new(
123
+ timestamp: 0,
124
+ contextSet: PrefabProto::ContextSet.new(
125
+ contexts: data.map do |(k, vs)|
126
+ PrefabProto::Context.new(
127
+ type: k,
128
+ values: vs.map do |v|
129
+ [v["key"], PrefabProto::ConfigValue.new(v["value_type"] => v["value"])]
130
+ end.to_h
131
+ )
132
+ end
133
+ )
134
+ )
135
+ end
136
+ [aggregator, ->(data) { data.events[0].example_contexts.examples.each { |e| e.timestamp = 0 } }, expected_data]
137
+ else
138
+ puts "unknown aggregator #{it.aggregator}"
139
+ end
140
+ end
141
+
142
+ def self.with_parent_context_maybe(context, &block)
143
+ if context
144
+ Prefab::Context.with_context(context, &block)
145
+ else
146
+ yield
147
+ end
148
+ end
36
149
  end
@@ -82,6 +82,16 @@ module CommonHelpers
82
82
 
83
83
  FakeResponse = Struct.new(:status, :body)
84
84
 
85
+ def wait_for(condition, max_wait: 2, sleep_time: 0.01)
86
+ wait_time = 0
87
+ while !condition.call
88
+ wait_time += sleep_time
89
+ sleep sleep_time
90
+
91
+ raise "Waited #{max_wait} seconds for the condition to be true, but it never was" if wait_time > max_wait
92
+ end
93
+ end
94
+
85
95
  def wait_for_post_requests(client, max_wait: 2, sleep_time: 0.01)
86
96
  # we use ivars to avoid re-mocking the post method on subsequent calls
87
97
  client.instance_variable_set("@_requests", [])
@@ -99,13 +109,7 @@ module CommonHelpers
99
109
  yield
100
110
 
101
111
  # let the flush thread run
102
- wait_time = 0
103
- while client.instance_variable_get("@_requests").empty?
104
- wait_time += sleep_time
105
- sleep sleep_time
106
-
107
- raise "Waited #{max_wait} seconds for the flush thread to run, but it never did" if wait_time > max_wait
108
- end
112
+ wait_for -> { client.instance_variable_get("@_requests").size > 0 }, max_wait: max_wait, sleep_time: sleep_time
109
113
 
110
114
  client.instance_variable_get("@_requests")
111
115
  end
@@ -16,7 +16,7 @@ class TestIntegration < Minitest::Test
16
16
  define_method(:"test_#{test['name']}_#{test_case['name']}") do
17
17
  it = IntegrationTest.new(test_case)
18
18
 
19
- with_parent_context_maybe(parent_context) do
19
+ IntegrationTestHelpers.with_parent_context_maybe(parent_context) do
20
20
  case it.test_type
21
21
  when :raise
22
22
  err = assert_raises(it.expected[:error]) do
@@ -34,6 +34,19 @@ class TestIntegration < Minitest::Test
34
34
  end
35
35
  when :log_level
36
36
  assert_equal it.expected[:value].to_sym, it.test_client.send(it.func, *it.input)
37
+ when :telemetry
38
+ aggregator, get_actual_data, expected = IntegrationTestHelpers.prepare_post_data(it)
39
+ aggregator.sync
40
+
41
+ wait_for -> { it.last_post_result&.status == 200 }
42
+
43
+ assert it.endpoint == it.last_post_endpoint
44
+
45
+ actual = get_actual_data[it.last_data_sent]
46
+
47
+ expected.all? do |expected|
48
+ assert actual.include?(expected)
49
+ end
37
50
  else
38
51
  raise "Unknown test type: #{it.test_type}"
39
52
  end
@@ -42,14 +55,4 @@ class TestIntegration < Minitest::Test
42
55
  end
43
56
  end
44
57
  end
45
-
46
- private
47
-
48
- def with_parent_context_maybe(context, &block)
49
- if context
50
- Prefab::Context.with_context(context, &block)
51
- else
52
- yield
53
- end
54
- end
55
58
  end
data/test/test_logger.rb CHANGED
@@ -404,6 +404,46 @@ class TestLogger < Minitest::Test
404
404
  assert_logged io, 'ERROR', 'test.test_logger.test_logging_with_a_block', message
405
405
  end
406
406
 
407
+ def test_structured_logging
408
+ prefab, io = captured_logger
409
+ message = 'HELLO'
410
+
411
+ prefab.log.error message, user: "michael", id: 123
412
+
413
+ assert_logged io, 'ERROR', 'test.test_logger.test_structured_logging', "#{message} id=123 user=michael"
414
+ end
415
+
416
+ def test_structured_json_logging
417
+ prefab, io = captured_logger(log_formatter: Prefab::Options::JSON_LOG_FORMATTER)
418
+ message = 'HELLO'
419
+
420
+ prefab.log.error message, user: "michael", id: 123
421
+
422
+ log_data = JSON.parse(io.string)
423
+ assert log_data["message"] == message
424
+ assert log_data["user"] == "michael"
425
+ assert log_data["id"] == 123
426
+ end
427
+
428
+ def test_structured_internal_logging
429
+ prefab, io = captured_logger
430
+
431
+ prefab.log.log_internal('test', 'test.path', '', ::Logger::WARN, user: "michael")
432
+
433
+ assert_logged io, 'WARN', 'cloud.prefab.client.test.path', "test user=michael"
434
+ end
435
+
436
+ def test_structured_block_logger
437
+ prefab, io = captured_logger
438
+ message = 'MY MESSAGE'
439
+
440
+ prefab.log.error user: "michael" do
441
+ message
442
+ end
443
+
444
+ assert_logged io, 'ERROR', 'test.test_logger.test_structured_block_logger', "#{message} user=michael"
445
+ end
446
+
407
447
  private
408
448
 
409
449
  def assert_logged(logged_io, level, path, message)
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: 1.0.1
4
+ version: 1.1.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-08-17 00:00:00.000000000 Z
11
+ date: 2023-10-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -199,6 +199,7 @@ files:
199
199
  - lib/prefab-cloud-ruby.rb
200
200
  - lib/prefab/client.rb
201
201
  - lib/prefab/config_client.rb
202
+ - lib/prefab/config_client_presenter.rb
202
203
  - lib/prefab/config_loader.rb
203
204
  - lib/prefab/config_resolver.rb
204
205
  - lib/prefab/config_value_unwrapper.rb