prefab-cloud-ruby 0 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.ruby-version +1 -0
- data/Gemfile +9 -22
- data/Gemfile.lock +88 -160
- data/LICENSE.txt +1 -1
- data/Rakefile +14 -14
- data/VERSION +1 -1
- data/lib/prefab/auth_interceptor.rb +25 -0
- data/lib/prefab/client.rb +34 -139
- data/lib/prefab/config_client.rb +23 -275
- data/lib/prefab/config_loader.rb +27 -60
- data/lib/prefab/config_resolver.rb +40 -53
- data/lib/prefab/noop_cache.rb +13 -0
- data/lib/prefab/noop_stats.rb +8 -0
- data/lib/prefab/prefab_pb.rb +39 -0
- data/lib/prefab/prefab_services_pb.rb +37 -0
- data/lib/prefab/ratelimit_client.rb +58 -0
- data/lib/prefab/ratelimit_pb.rb +125 -0
- data/lib/prefab/store.rb +29 -0
- data/lib/prefab_client.rb +35 -0
- metadata +36 -198
- data/.envrc.sample +0 -3
- data/.github/workflows/ruby.yml +0 -46
- data/.gitmodules +0 -3
- data/.rubocop.yml +0 -13
- data/.tool-versions +0 -1
- data/CHANGELOG.md +0 -169
- data/CODEOWNERS +0 -1
- data/README.md +0 -94
- data/bin/console +0 -21
- data/compile_protos.sh +0 -18
- data/lib/prefab/config_client_presenter.rb +0 -18
- data/lib/prefab/config_value_unwrapper.rb +0 -115
- data/lib/prefab/config_value_wrapper.rb +0 -18
- data/lib/prefab/context.rb +0 -179
- data/lib/prefab/context_shape.rb +0 -20
- data/lib/prefab/context_shape_aggregator.rb +0 -65
- data/lib/prefab/criteria_evaluator.rb +0 -136
- data/lib/prefab/encryption.rb +0 -65
- data/lib/prefab/error.rb +0 -6
- data/lib/prefab/errors/env_var_parse_error.rb +0 -11
- data/lib/prefab/errors/initialization_timeout_error.rb +0 -13
- data/lib/prefab/errors/invalid_api_key_error.rb +0 -19
- data/lib/prefab/errors/missing_default_error.rb +0 -13
- data/lib/prefab/errors/missing_env_var_error.rb +0 -11
- data/lib/prefab/errors/uninitialized_error.rb +0 -13
- data/lib/prefab/evaluation.rb +0 -52
- data/lib/prefab/evaluation_summary_aggregator.rb +0 -87
- data/lib/prefab/example_contexts_aggregator.rb +0 -78
- data/lib/prefab/exponential_backoff.rb +0 -21
- data/lib/prefab/feature_flag_client.rb +0 -42
- data/lib/prefab/http_connection.rb +0 -41
- data/lib/prefab/internal_logger.rb +0 -16
- data/lib/prefab/local_config_parser.rb +0 -151
- data/lib/prefab/log_path_aggregator.rb +0 -69
- data/lib/prefab/logger_client.rb +0 -264
- data/lib/prefab/murmer3.rb +0 -50
- data/lib/prefab/options.rb +0 -208
- data/lib/prefab/periodic_sync.rb +0 -69
- data/lib/prefab/prefab.rb +0 -56
- data/lib/prefab/rate_limit_cache.rb +0 -41
- data/lib/prefab/resolved_config_presenter.rb +0 -86
- data/lib/prefab/time_helpers.rb +0 -7
- data/lib/prefab/weighted_value_resolver.rb +0 -42
- data/lib/prefab/yaml_config_parser.rb +0 -34
- data/lib/prefab-cloud-ruby.rb +0 -57
- data/lib/prefab_pb.rb +0 -93
- data/prefab-cloud-ruby.gemspec +0 -155
- data/test/.prefab.default.config.yaml +0 -2
- data/test/.prefab.unit_tests.config.yaml +0 -28
- data/test/integration_test.rb +0 -150
- data/test/integration_test_helpers.rb +0 -151
- data/test/support/common_helpers.rb +0 -180
- data/test/support/mock_base_client.rb +0 -42
- data/test/support/mock_config_client.rb +0 -19
- data/test/support/mock_config_loader.rb +0 -1
- data/test/test_client.rb +0 -444
- data/test/test_config_client.rb +0 -109
- data/test/test_config_loader.rb +0 -117
- data/test/test_config_resolver.rb +0 -430
- data/test/test_config_value_unwrapper.rb +0 -224
- data/test/test_config_value_wrapper.rb +0 -42
- data/test/test_context.rb +0 -203
- data/test/test_context_shape.rb +0 -50
- data/test/test_context_shape_aggregator.rb +0 -147
- data/test/test_criteria_evaluator.rb +0 -726
- data/test/test_encryption.rb +0 -16
- data/test/test_evaluation_summary_aggregator.rb +0 -162
- data/test/test_example_contexts_aggregator.rb +0 -238
- data/test/test_exponential_backoff.rb +0 -18
- data/test/test_feature_flag_client.rb +0 -48
- data/test/test_helper.rb +0 -17
- data/test/test_integration.rb +0 -58
- data/test/test_local_config_parser.rb +0 -147
- data/test/test_log_path_aggregator.rb +0 -62
- data/test/test_logger.rb +0 -621
- data/test/test_logger_initialization.rb +0 -12
- data/test/test_options.rb +0 -75
- data/test/test_prefab.rb +0 -12
- data/test/test_rate_limit_cache.rb +0 -44
- data/test/test_weighted_value_resolver.rb +0 -71
data/lib/prefab/config_client.rb
CHANGED
@@ -1,292 +1,40 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
module Prefab
|
4
2
|
class ConfigClient
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
LOGGING_KEY_PREFIX = "#{Prefab::LoggerClient::BASE_KEY}#{Prefab::LoggerClient::SEP}".freeze
|
11
|
-
LOG = Prefab::InternalLogger.new(ConfigClient)
|
12
|
-
|
13
|
-
def initialize(base_client, timeout)
|
14
|
-
@base_client = base_client
|
15
|
-
@options = base_client.options
|
16
|
-
LOG.debug 'Initialize ConfigClient'
|
17
|
-
@timeout = timeout
|
18
|
-
|
19
|
-
@stream_lock = Concurrent::ReadWriteLock.new
|
20
|
-
|
21
|
-
@checkpoint_freq_secs = DEFAULT_CHECKPOINT_FREQ_SEC
|
22
|
-
|
23
|
-
@config_loader = Prefab::ConfigLoader.new(@base_client)
|
24
|
-
@config_resolver = Prefab::ConfigResolver.new(@base_client, @config_loader)
|
25
|
-
|
26
|
-
@initialization_lock = Concurrent::ReadWriteLock.new
|
27
|
-
LOG.debug 'Initialize ConfigClient: AcquireWriteLock'
|
28
|
-
@initialization_lock.acquire_write_lock
|
29
|
-
LOG.debug 'Initialize ConfigClient: AcquiredWriteLock'
|
30
|
-
@initialized_future = Concurrent::Future.execute { @initialization_lock.acquire_read_lock }
|
31
|
-
|
32
|
-
if @options.local_only?
|
33
|
-
finish_init!(:local_only, nil)
|
34
|
-
elsif @options.datafile?
|
35
|
-
load_json_file(@options.datafile)
|
36
|
-
else
|
37
|
-
load_checkpoint
|
38
|
-
start_checkpointing_thread
|
39
|
-
start_streaming
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def start_streaming
|
44
|
-
@stream_lock.with_write_lock do
|
45
|
-
start_sse_streaming_connection_thread(@config_loader.highwater_mark) if @streaming_thread.nil?
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def to_s
|
50
|
-
@config_resolver.to_s
|
51
|
-
end
|
52
|
-
|
53
|
-
def resolver
|
54
|
-
@config_resolver
|
3
|
+
def initialize(config_service, client)
|
4
|
+
@client = client
|
5
|
+
@config_service = config_service
|
6
|
+
@config_resolver = EzConfig::ConfigResolver.new(client)
|
7
|
+
boot_resolver(@config_service)
|
55
8
|
end
|
56
9
|
|
57
|
-
def
|
58
|
-
|
59
|
-
rows: [PrefabProto::ConfigRow.new(value: config_value)])
|
10
|
+
def get(prop)
|
11
|
+
@config_resolver.get(prop)
|
60
12
|
end
|
61
13
|
|
62
|
-
def
|
63
|
-
|
64
|
-
|
65
|
-
if !context.blank? && @base_client.example_contexts_aggregator
|
66
|
-
@base_client.example_contexts_aggregator.record(context)
|
67
|
-
end
|
68
|
-
|
69
|
-
evaluation = _get(key, context)
|
70
|
-
|
71
|
-
@base_client.context_shape_aggregator&.push(context)
|
72
|
-
|
73
|
-
if evaluation
|
74
|
-
evaluation.report_and_return(@base_client.evaluation_summary_aggregator)
|
75
|
-
else
|
76
|
-
handle_default(key, default)
|
77
|
-
end
|
14
|
+
def set(config_value)
|
15
|
+
@config_service.upsert(config_value)
|
78
16
|
end
|
79
17
|
|
80
18
|
private
|
81
19
|
|
82
|
-
def
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
def handle_default(key, default)
|
87
|
-
return default if default != NO_DEFAULT_PROVIDED
|
88
|
-
|
89
|
-
raise Prefab::Errors::MissingDefaultError, key if @options.on_no_default == Prefab::Options::ON_NO_DEFAULT::RAISE
|
90
|
-
|
91
|
-
nil
|
92
|
-
end
|
93
|
-
|
94
|
-
def _get(key, properties)
|
95
|
-
# wait timeout sec for the initialization to be complete
|
96
|
-
@initialized_future.value(@options.initialization_timeout_sec)
|
97
|
-
if @initialized_future.incomplete?
|
98
|
-
unless @options.on_init_failure == Prefab::Options::ON_INITIALIZATION_FAILURE::RETURN
|
99
|
-
raise Prefab::Errors::InitializationTimeoutError.new(@options.initialization_timeout_sec, key)
|
100
|
-
end
|
101
|
-
|
102
|
-
LOG.warn("Couldn't Initialize In #{@options.initialization_timeout_sec}. Key #{key}. Returning what we have")
|
103
|
-
@initialization_lock.release_write_lock
|
104
|
-
end
|
105
|
-
|
106
|
-
@config_resolver.get key, properties
|
107
|
-
end
|
108
|
-
|
109
|
-
def load_checkpoint
|
110
|
-
success = load_checkpoint_api_cdn
|
111
|
-
|
112
|
-
return if success
|
113
|
-
|
114
|
-
success = load_checkpoint_api
|
115
|
-
|
116
|
-
return if success
|
117
|
-
|
118
|
-
success = load_cache
|
119
|
-
|
120
|
-
return if success
|
121
|
-
|
122
|
-
LOG.warn 'No success loading checkpoints'
|
123
|
-
end
|
124
|
-
|
125
|
-
def load_checkpoint_api_cdn
|
126
|
-
conn = Prefab::HttpConnection.new("#{@options.url_for_api_cdn}/api/v1/configs/0", @base_client.api_key)
|
127
|
-
load_url(conn, :remote_cdn_api)
|
128
|
-
end
|
129
|
-
|
130
|
-
def load_checkpoint_api
|
131
|
-
conn = Prefab::HttpConnection.new("#{@options.prefab_api_url}/api/v1/configs/0", @base_client.api_key)
|
132
|
-
load_url(conn, :remote_api)
|
133
|
-
end
|
134
|
-
|
135
|
-
def load_url(conn, source)
|
136
|
-
resp = conn.get('')
|
137
|
-
if resp.status == 200
|
138
|
-
configs = PrefabProto::Configs.decode(resp.body)
|
139
|
-
load_configs(configs, source)
|
140
|
-
cache_configs(configs)
|
141
|
-
true
|
142
|
-
else
|
143
|
-
LOG.info "Checkpoint #{source} failed to load. Response #{resp.status}"
|
144
|
-
false
|
145
|
-
end
|
146
|
-
rescue Faraday::ConnectionFailed => e
|
147
|
-
if @initialization_lock.write_locked?
|
148
|
-
LOG.warn "Connection Fail loading #{source} checkpoint."
|
149
|
-
else
|
150
|
-
LOG.debug "Connection Fail loading #{source} checkpoint."
|
151
|
-
end
|
152
|
-
false
|
153
|
-
rescue StandardError => e
|
154
|
-
LOG.warn "Unexpected #{source} problem loading checkpoint #{e} #{conn}"
|
155
|
-
LOG.debug e.backtrace
|
156
|
-
false
|
157
|
-
end
|
158
|
-
|
159
|
-
def load_configs(configs, source)
|
160
|
-
project_id = configs.config_service_pointer.project_id
|
161
|
-
project_env_id = configs.config_service_pointer.project_env_id
|
162
|
-
@config_resolver.project_env_id = project_env_id
|
163
|
-
starting_highwater_mark = @config_loader.highwater_mark
|
164
|
-
|
165
|
-
default_contexts = configs.default_context&.contexts&.map do |context|
|
166
|
-
[
|
167
|
-
context.type,
|
168
|
-
context.values.keys.map do |k|
|
169
|
-
[k, Prefab::ConfigValueUnwrapper.new(context.values[k], @config_resolver).unwrap]
|
170
|
-
end.to_h
|
171
|
-
]
|
172
|
-
end.to_h
|
173
|
-
|
174
|
-
@config_resolver.default_context = default_contexts || {}
|
175
|
-
|
176
|
-
configs.configs.each do |config|
|
177
|
-
@config_loader.set(config, source)
|
178
|
-
end
|
179
|
-
if @config_loader.highwater_mark > starting_highwater_mark
|
180
|
-
LOG.debug("Found new checkpoint with highwater id #{@config_loader.highwater_mark} from #{source} in project #{project_id} environment: #{project_env_id} and namespace: '#{@namespace}'")
|
181
|
-
else
|
182
|
-
LOG.debug("Checkpoint with highwater id #{@config_loader.highwater_mark} from #{source}. No changes.")
|
183
|
-
end
|
184
|
-
@config_resolver.update
|
185
|
-
finish_init!(source, project_id)
|
186
|
-
end
|
187
|
-
|
188
|
-
def cache_path
|
189
|
-
return @cache_path unless @cache_path.nil?
|
190
|
-
@cache_path ||= calc_cache_path
|
191
|
-
FileUtils.mkdir_p(File.dirname(@cache_path))
|
192
|
-
@cache_path
|
193
|
-
end
|
194
|
-
|
195
|
-
def calc_cache_path
|
196
|
-
file_name = "prefab.cache.#{@base_client.options.api_key_id}.json"
|
197
|
-
dir = ENV.fetch('XDG_CACHE_HOME', File.join(Dir.home, '.cache'))
|
198
|
-
File.join(dir, file_name)
|
199
|
-
end
|
200
|
-
|
201
|
-
def cache_configs(configs)
|
202
|
-
return unless @options.use_local_cache && !@options.is_fork
|
203
|
-
File.open(cache_path, "w") do |f|
|
204
|
-
f.flock(File::LOCK_EX)
|
205
|
-
f.write(PrefabProto::Configs.encode_json(configs))
|
206
|
-
end
|
207
|
-
LOG.debug "Cached configs to #{cache_path}"
|
208
|
-
rescue => e
|
209
|
-
LOG.debug "Failed to cache configs to #{cache_path} #{e}"
|
210
|
-
end
|
211
|
-
|
212
|
-
def load_cache
|
213
|
-
return false unless @options.use_local_cache
|
214
|
-
File.open(cache_path) do |f|
|
215
|
-
f.flock(File::LOCK_SH)
|
216
|
-
configs = PrefabProto::Configs.decode_json(f.read)
|
217
|
-
load_configs(configs, :cache)
|
218
|
-
|
219
|
-
hours_old = ((Time.now - File.mtime(f)) / 60 / 60).round(2)
|
220
|
-
if hours_old > STALE_CACHE_WARN_HOURS
|
221
|
-
LOG.info "Stale Cache Load: #{hours_old} hours old"
|
222
|
-
end
|
223
|
-
true
|
224
|
-
end
|
225
|
-
rescue => e
|
226
|
-
LOG.debug "Failed to read cached configs at #{cache_path}. #{e}"
|
227
|
-
false
|
228
|
-
end
|
229
|
-
|
230
|
-
def load_json_file(file)
|
231
|
-
File.open(file) do |f|
|
232
|
-
f.flock(File::LOCK_SH)
|
233
|
-
configs = PrefabProto::Configs.decode_json(f.read)
|
234
|
-
load_configs(configs, :datafile)
|
235
|
-
end
|
236
|
-
end
|
20
|
+
def boot_resolver(config_service)
|
21
|
+
config_req = Prefab::ConfigServicePointer.new(account_id: 1,
|
22
|
+
start_at_id: 0)
|
237
23
|
|
238
|
-
# A thread that checks for a checkpoint
|
239
|
-
def start_checkpointing_thread
|
240
24
|
Thread.new do
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
end
|
252
|
-
|
253
|
-
def finish_init!(source, project_id)
|
254
|
-
return unless @initialization_lock.write_locked?
|
255
|
-
|
256
|
-
LOG.debug "Unlocked Config via #{source}"
|
257
|
-
@initialization_lock.release_write_lock
|
258
|
-
|
259
|
-
Prefab::LoggerClient.instance.config_client = self
|
260
|
-
presenter = Prefab::ConfigClientPresenter.new(
|
261
|
-
size: @config_resolver.local_store.size,
|
262
|
-
source: source,
|
263
|
-
project_id: project_id,
|
264
|
-
project_env_id: @config_resolver.project_env_id,
|
265
|
-
api_key_id: @base_client.options.api_key_id
|
266
|
-
)
|
267
|
-
LOG.info presenter.to_s
|
268
|
-
LOG.debug to_s
|
269
|
-
end
|
270
|
-
|
271
|
-
def start_sse_streaming_connection_thread(start_at_id)
|
272
|
-
auth = "#{AUTH_USER}:#{@base_client.api_key}"
|
273
|
-
auth_string = Base64.strict_encode64(auth)
|
274
|
-
headers = {
|
275
|
-
'x-prefab-start-at-id' => start_at_id,
|
276
|
-
'Authorization' => "Basic #{auth_string}",
|
277
|
-
'X-PrefabCloud-Client-Version' => "prefab-cloud-ruby-#{Prefab::VERSION}"
|
278
|
-
}
|
279
|
-
url = "#{@base_client.prefab_api_url}/api/v1/sse/config"
|
280
|
-
LOG.debug "SSE Streaming Connect to #{url} start_at #{start_at_id}"
|
281
|
-
@streaming_thread = SSE::Client.new(url,
|
282
|
-
headers: headers,
|
283
|
-
read_timeout: SSE_READ_TIMEOUT,
|
284
|
-
logger: Prefab::SseLogger.new) do |client|
|
285
|
-
client.on_event do |event|
|
286
|
-
configs = PrefabProto::Configs.decode(Base64.decode64(event.data))
|
287
|
-
load_configs(configs, :sse)
|
25
|
+
begin
|
26
|
+
resp = config_service.get_config(config_req)
|
27
|
+
resp.each do |r|
|
28
|
+
r.deltas.each do |delta|
|
29
|
+
@config_resolver.set(delta)
|
30
|
+
end
|
31
|
+
@config_resolver.update
|
32
|
+
end
|
33
|
+
rescue => e
|
34
|
+
@client.logger.warn(e)
|
288
35
|
end
|
289
36
|
end
|
290
37
|
end
|
291
38
|
end
|
292
39
|
end
|
40
|
+
|
data/lib/prefab/config_loader.rb
CHANGED
@@ -1,84 +1,51 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
module Prefab
|
1
|
+
require 'yaml'
|
2
|
+
module EzConfig
|
4
3
|
class ConfigLoader
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
@base_client = base_client
|
11
|
-
@prefab_options = base_client.options
|
12
|
-
@highwater_mark = 0
|
13
|
-
@classpath_config = load_classpath_config
|
14
|
-
@local_overrides = load_local_overrides
|
4
|
+
def initialize(logger)
|
5
|
+
@logger = logger
|
6
|
+
load_ez_config
|
7
|
+
load_project_config
|
8
|
+
load_local_overrides
|
15
9
|
@api_config = Concurrent::Map.new
|
10
|
+
@immutable_config = @ez_config.merge(@project_config)
|
16
11
|
end
|
17
12
|
|
18
13
|
def calc_config
|
19
|
-
rtn = @
|
14
|
+
rtn = @immutable_config.clone
|
20
15
|
@api_config.each_key do |k|
|
21
16
|
rtn[k] = @api_config[k]
|
22
17
|
end
|
23
|
-
rtn.merge(@local_overrides)
|
18
|
+
rtn = rtn.merge(@local_overrides)
|
19
|
+
rtn
|
24
20
|
end
|
25
21
|
|
26
|
-
def set(
|
27
|
-
|
28
|
-
return if @api_config[config.key] && @api_config[config.key][:config].id >= config.id
|
29
|
-
|
30
|
-
if config.rows.empty?
|
31
|
-
@api_config.delete(config.key)
|
32
|
-
else
|
33
|
-
if @api_config[config.key]
|
34
|
-
LOG.debug(
|
35
|
-
"Replace #{config.key} with value from #{source} #{@api_config[config.key][:config].id} -> #{config.id}")
|
36
|
-
end
|
37
|
-
@api_config[config.key] = { source: source, config: config }
|
38
|
-
end
|
39
|
-
@highwater_mark = [config.id, @highwater_mark].max
|
22
|
+
def set(delta)
|
23
|
+
@api_config[delta.key] = delta.value
|
40
24
|
end
|
41
25
|
|
42
|
-
|
43
|
-
@api_config.delete key
|
44
|
-
end
|
26
|
+
private
|
45
27
|
|
46
|
-
def
|
47
|
-
|
48
|
-
@api_config.each_value do |config_value|
|
49
|
-
configs.configs << config_value[:config]
|
50
|
-
end
|
51
|
-
configs
|
28
|
+
def load_ez_config
|
29
|
+
@ez_config = load(".ezconfig.yaml")
|
52
30
|
end
|
53
31
|
|
54
|
-
|
55
|
-
|
56
|
-
def load_classpath_config
|
57
|
-
return {} if @prefab_options.datafile?
|
58
|
-
classpath_dir = @prefab_options.prefab_config_classpath_dir
|
59
|
-
rtn = load_glob(File.join(classpath_dir, '.prefab.default.config.yaml'))
|
60
|
-
@prefab_options.prefab_envs.each do |env|
|
61
|
-
rtn = rtn.merge load_glob(File.join(classpath_dir, ".prefab.#{env}.config.yaml"))
|
62
|
-
end
|
63
|
-
rtn
|
32
|
+
def load_project_config
|
33
|
+
@project_config = load(".projectconfig.yaml")
|
64
34
|
end
|
65
35
|
|
66
36
|
def load_local_overrides
|
67
|
-
|
68
|
-
override_dir = @prefab_options.prefab_config_override_dir
|
69
|
-
rtn = load_glob(File.join(override_dir, '.prefab.default.config.yaml'))
|
70
|
-
@prefab_options.prefab_envs.each do |env|
|
71
|
-
rtn = rtn.merge load_glob(File.join(override_dir, ".prefab.#{env}.config.yaml"))
|
72
|
-
end
|
73
|
-
rtn
|
37
|
+
@local_overrides = load(".ezconfig.overrides.yaml")
|
74
38
|
end
|
75
39
|
|
76
|
-
def
|
77
|
-
|
78
|
-
|
79
|
-
|
40
|
+
def load(filename)
|
41
|
+
if File.exist? filename
|
42
|
+
|
43
|
+
YAML.load_file(filename)
|
44
|
+
else
|
45
|
+
@logger.info "No file #{filename}"
|
46
|
+
{}
|
80
47
|
end
|
81
|
-
rtn
|
82
48
|
end
|
49
|
+
|
83
50
|
end
|
84
51
|
end
|
@@ -1,76 +1,63 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
module Prefab
|
1
|
+
module EzConfig
|
4
2
|
class ConfigResolver
|
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
|
9
3
|
|
10
|
-
def initialize(
|
4
|
+
def initialize(client)
|
11
5
|
@lock = Concurrent::ReadWriteLock.new
|
12
6
|
@local_store = {}
|
13
|
-
@
|
14
|
-
@
|
15
|
-
@base_client = base_client
|
16
|
-
@on_update = nil
|
17
|
-
@default_context = {}
|
7
|
+
@namespace = client.namespace
|
8
|
+
@config_loader = EzConfig::ConfigLoader.new(client.logger)
|
18
9
|
make_local
|
19
10
|
end
|
20
11
|
|
21
|
-
def
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
def get(key, properties = NO_DEFAULT_PROVIDED)
|
34
|
-
@lock.with_read_lock do
|
35
|
-
raw_config = raw(key)
|
36
|
-
|
37
|
-
return nil unless raw_config
|
38
|
-
|
39
|
-
evaluate(raw_config, properties)
|
12
|
+
def get(property)
|
13
|
+
value = @lock.with_read_lock do
|
14
|
+
@local_store[property][:value]
|
15
|
+
end
|
16
|
+
case value.type
|
17
|
+
when :string then
|
18
|
+
puts "SSString"
|
19
|
+
value.string
|
20
|
+
when :int then
|
21
|
+
puts "INT"
|
22
|
+
value.int
|
40
23
|
end
|
41
24
|
end
|
42
25
|
|
43
|
-
def
|
44
|
-
|
45
|
-
project_env_id: @project_env_id,
|
46
|
-
resolver: self,
|
47
|
-
namespace: @base_client.options.namespace,
|
48
|
-
base_client: @base_client).evaluate(make_context(properties))
|
26
|
+
def set(delta)
|
27
|
+
@config_loader.set(delta)
|
49
28
|
end
|
50
29
|
|
51
30
|
def update
|
52
31
|
make_local
|
53
|
-
|
54
|
-
@on_update ? @on_update.call : nil
|
55
|
-
end
|
56
|
-
|
57
|
-
def on_update(&block)
|
58
|
-
@on_update = block
|
59
|
-
end
|
60
|
-
|
61
|
-
def make_context(properties)
|
62
|
-
if properties == NO_DEFAULT_PROVIDED || properties.nil?
|
63
|
-
Context.current
|
64
|
-
else
|
65
|
-
Context.merge_with_current(properties)
|
66
|
-
end.merge_default(default_context || {})
|
67
32
|
end
|
68
33
|
|
69
34
|
private
|
70
35
|
|
71
36
|
def make_local
|
37
|
+
store = {}
|
38
|
+
@config_loader.calc_config.each do |prop, value|
|
39
|
+
property = prop
|
40
|
+
namespace = ""
|
41
|
+
split = prop.split(":")
|
42
|
+
|
43
|
+
if split.size > 1
|
44
|
+
property = split[1..-1].join
|
45
|
+
namespace = split[0]
|
46
|
+
end
|
47
|
+
|
48
|
+
if (namespace == "") || namespace.start_with?(@namespace)
|
49
|
+
existing = store[property]
|
50
|
+
if existing.nil?
|
51
|
+
store[property] = { namespace: namespace, value: value }
|
52
|
+
elsif existing[:namespace].split(".").size < namespace.split(".").size
|
53
|
+
store[property] = { namespace: namespace, value: value }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
puts "prop #{property} namespace #{namespace} value #{value}"
|
58
|
+
end
|
72
59
|
@lock.with_write_lock do
|
73
|
-
@local_store =
|
60
|
+
@local_store = store
|
74
61
|
end
|
75
62
|
end
|
76
63
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
2
|
+
# source: prefab.proto
|
3
|
+
|
4
|
+
require 'google/protobuf'
|
5
|
+
|
6
|
+
require 'google/protobuf/wrappers_pb'
|
7
|
+
require 'ratelimit_pb'
|
8
|
+
Google::Protobuf::DescriptorPool.generated_pool.build do
|
9
|
+
add_message "prefab.ConfigServicePointer" do
|
10
|
+
optional :account_id, :int64, 1
|
11
|
+
optional :start_at_id, :int64, 2
|
12
|
+
end
|
13
|
+
add_message "prefab.ConfigDelta" do
|
14
|
+
optional :account_id, :int64, 1
|
15
|
+
optional :id, :int64, 2
|
16
|
+
optional :key, :string, 3
|
17
|
+
optional :value, :message, 4, "prefab.ConfigValue"
|
18
|
+
end
|
19
|
+
add_message "prefab.ConfigValue" do
|
20
|
+
oneof :type do
|
21
|
+
optional :int, :int64, 1
|
22
|
+
optional :string, :string, 2
|
23
|
+
optional :bytes, :bytes, 3
|
24
|
+
optional :double, :double, 4
|
25
|
+
optional :bool, :bool, 5
|
26
|
+
optional :feature_flag, :message, 6, "it.ratelim.data.FeatureFlag"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
add_message "prefab.ConfigDeltas" do
|
30
|
+
repeated :deltas, :message, 1, "prefab.ConfigDelta"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module Prefab
|
35
|
+
ConfigServicePointer = Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.ConfigServicePointer").msgclass
|
36
|
+
ConfigDelta = Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.ConfigDelta").msgclass
|
37
|
+
ConfigValue = Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.ConfigValue").msgclass
|
38
|
+
ConfigDeltas = Google::Protobuf::DescriptorPool.generated_pool.lookup("prefab.ConfigDeltas").msgclass
|
39
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
2
|
+
# Source: prefab.proto for package 'prefab'
|
3
|
+
|
4
|
+
require 'grpc'
|
5
|
+
require 'prefab_pb'
|
6
|
+
|
7
|
+
module Prefab
|
8
|
+
module RateLimitService
|
9
|
+
class Service
|
10
|
+
|
11
|
+
include GRPC::GenericService
|
12
|
+
|
13
|
+
self.marshal_class_method = :encode
|
14
|
+
self.unmarshal_class_method = :decode
|
15
|
+
self.service_name = 'prefab.RateLimitService'
|
16
|
+
|
17
|
+
rpc :LimitCheck, It::Ratelim::Data::LimitRequest, It::Ratelim::Data::LimitResponse
|
18
|
+
end
|
19
|
+
|
20
|
+
Stub = Service.rpc_stub_class
|
21
|
+
end
|
22
|
+
module ConfigService
|
23
|
+
class Service
|
24
|
+
|
25
|
+
include GRPC::GenericService
|
26
|
+
|
27
|
+
self.marshal_class_method = :encode
|
28
|
+
self.unmarshal_class_method = :decode
|
29
|
+
self.service_name = 'prefab.ConfigService'
|
30
|
+
|
31
|
+
rpc :GetConfig, ConfigServicePointer, stream(ConfigDeltas)
|
32
|
+
rpc :Upsert, ConfigDelta, ConfigServicePointer
|
33
|
+
end
|
34
|
+
|
35
|
+
Stub = Service.rpc_stub_class
|
36
|
+
end
|
37
|
+
end
|