prefab-cloud-ruby 1.0.1 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 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