prefab-cloud-ruby 0.13.0 → 0.13.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -0
  3. data/Gemfile +4 -11
  4. data/Gemfile.lock +32 -52
  5. data/README.md +1 -19
  6. data/Rakefile +0 -1
  7. data/VERSION +1 -1
  8. data/compile_protos.sh +0 -3
  9. data/lib/prefab/auth_interceptor.rb +0 -1
  10. data/lib/prefab/cancellable_interceptor.rb +0 -1
  11. data/lib/prefab/client.rb +36 -49
  12. data/lib/prefab/config_client.rb +73 -145
  13. data/lib/prefab/config_loader.rb +13 -98
  14. data/lib/prefab/config_resolver.rb +49 -56
  15. data/lib/prefab/feature_flag_client.rb +11 -129
  16. data/lib/prefab/logger_client.rb +8 -10
  17. data/lib/prefab/murmer3.rb +0 -1
  18. data/lib/prefab/noop_cache.rb +0 -1
  19. data/lib/prefab/noop_stats.rb +0 -1
  20. data/lib/prefab/ratelimit_client.rb +0 -1
  21. data/lib/prefab-cloud-ruby.rb +0 -10
  22. data/lib/prefab_pb.rb +132 -214
  23. data/lib/prefab_services_pb.rb +6 -35
  24. data/prefab-cloud-ruby.gemspec +11 -30
  25. data/test/.prefab.test.config.yaml +1 -27
  26. data/test/test_config_loader.rb +25 -39
  27. data/test/test_config_resolver.rb +38 -134
  28. data/test/test_feature_flag_client.rb +35 -277
  29. data/test/test_helper.rb +4 -70
  30. data/test/test_logger.rb +29 -23
  31. metadata +15 -70
  32. data/.github/workflows/ruby.yml +0 -39
  33. data/.tool-versions +0 -1
  34. data/CODEOWNERS +0 -1
  35. data/lib/prefab/config_helper.rb +0 -29
  36. data/lib/prefab/error.rb +0 -6
  37. data/lib/prefab/errors/initialization_timeout_error.rb +0 -13
  38. data/lib/prefab/errors/invalid_api_key_error.rb +0 -19
  39. data/lib/prefab/errors/missing_default_error.rb +0 -13
  40. data/lib/prefab/internal_logger.rb +0 -29
  41. data/lib/prefab/options.rb +0 -82
  42. data/run_test_harness_server.sh +0 -8
  43. data/test/harness_server.rb +0 -64
  44. data/test/test_client.rb +0 -91
  45. data/test/test_config_client.rb +0 -56
@@ -1,48 +1,36 @@
1
- # frozen_string_literal: true
2
1
  module Prefab
3
2
  class ConfigClient
4
- include Prefab::ConfigHelper
5
-
6
3
  RECONNECT_WAIT = 5
7
4
  DEFAULT_CHECKPOINT_FREQ_SEC = 60
8
5
  DEFAULT_S3CF_BUCKET = 'http://d2j4ed6ti5snnd.cloudfront.net'
9
- SSE_READ_TIMEOUT = 300
10
6
 
11
7
  def initialize(base_client, timeout)
12
8
  @base_client = base_client
13
- @options = base_client.options
14
- @base_client.log_internal Logger::DEBUG, "Initialize ConfigClient"
15
9
  @timeout = timeout
16
-
17
- @stream_lock = Concurrent::ReadWriteLock.new
10
+ @initialization_lock = Concurrent::ReadWriteLock.new
18
11
 
19
12
  @checkpoint_freq_secs = DEFAULT_CHECKPOINT_FREQ_SEC
20
13
 
21
14
  @config_loader = Prefab::ConfigLoader.new(@base_client)
22
15
  @config_resolver = Prefab::ConfigResolver.new(@base_client, @config_loader)
23
16
 
24
- @initialization_lock = Concurrent::ReadWriteLock.new
25
- @base_client.log_internal Logger::DEBUG, "Initialize ConfigClient: AcquireWriteLock"
26
17
  @initialization_lock.acquire_write_lock
27
- @base_client.log_internal Logger::DEBUG, "Initialize ConfigClient: AcquiredWriteLock"
28
- @initialized_future = Concurrent::Future.execute { @initialization_lock.acquire_read_lock }
29
18
 
30
19
  @cancellable_interceptor = Prefab::CancellableInterceptor.new(@base_client)
31
20
 
32
21
  @s3_cloud_front = ENV["PREFAB_S3CF_BUCKET"] || DEFAULT_S3CF_BUCKET
33
-
34
- if @options.local_only?
35
- finish_init!(:local_only)
36
- else
37
- load_checkpoint
38
- start_checkpointing_thread
39
- start_streaming
40
- end
22
+ load_checkpoint
23
+ start_checkpointing_thread
41
24
  end
42
25
 
43
26
  def start_streaming
44
- @stream_lock.with_write_lock do
45
- start_sse_streaming_connection_thread(@config_loader.highwater_mark) if @streaming_thread.nil?
27
+ @streaming = true
28
+ start_api_connection_thread(@config_loader.highwater_mark)
29
+ end
30
+
31
+ def get(prop)
32
+ @initialization_lock.with_read_lock do
33
+ @config_resolver.get(prop)
46
34
  end
47
35
  end
48
36
 
@@ -55,7 +43,7 @@ module Prefab
55
43
 
56
44
  @base_client.request Prefab::ConfigService, :upsert, req_options: { timeout: @timeout }, params: upsert_req
57
45
  @base_client.stats.increment("prefab.config.upsert")
58
- @config_loader.set(config_delta, :upsert)
46
+ @config_loader.set(config_delta)
59
47
  @config_loader.rm(previous_key) if previous_key&.present?
60
48
  @config_resolver.update
61
49
  end
@@ -70,48 +58,12 @@ module Prefab
70
58
  end
71
59
 
72
60
  def self.value_to_delta(key, config_value, namespace = nil)
73
- Prefab::Config.new(key: [namespace, key].compact.join(":"),
74
- rows: [Prefab::ConfigRow.new(value: config_value)])
75
- end
76
-
77
- def get(key, default=Prefab::Client::NO_DEFAULT_PROVIDED)
78
- config = _get(key)
79
- config ? value_of(config[:value]) : handle_default(key, default)
80
- end
81
-
82
- def get_config_obj(key)
83
- config = _get(key)
84
- config ? config[:config] : nil
61
+ Prefab::ConfigDelta.new(key: [namespace, key].compact.join(":"),
62
+ value: config_value)
85
63
  end
86
64
 
87
65
  private
88
66
 
89
- def handle_default(key, default)
90
- if default != Prefab::Client::NO_DEFAULT_PROVIDED
91
- return default
92
- end
93
-
94
- if @options.on_no_default == Prefab::Options::ON_NO_DEFAULT::RAISE
95
- raise Prefab::Errors::MissingDefaultError.new(key)
96
- end
97
-
98
- nil
99
- end
100
-
101
- def _get(key)
102
- # wait timeout sec for the initalization to be complete
103
- @initialized_future.value(@options.initialization_timeout_sec)
104
- if @initialized_future.incomplete?
105
- if @options.on_init_failure == Prefab::Options::ON_INITIALIZATION_FAILURE::RETURN
106
- @base_client.log_internal Logger::WARN, "Couldn't Initialize In #{@options.initialization_timeout_sec}. Key #{key}. Returning what we have"
107
- @initialization_lock.release_write_lock
108
- else
109
- raise Prefab::Errors::InitializationTimeoutError.new(@options.initialization_timeout_sec, key)
110
- end
111
- end
112
- @config_resolver._get(key)
113
- end
114
-
115
67
  def stub
116
68
  @_stub = Prefab::ConfigService::Stub.new(nil,
117
69
  nil,
@@ -119,92 +71,53 @@ module Prefab
119
71
  interceptors: [@base_client.interceptor, @cancellable_interceptor])
120
72
  end
121
73
 
122
- # try API first, if not, fallback to s3
74
+ # Bootstrap out of the cache
75
+ # returns the high-watermark of what was in the cache
123
76
  def load_checkpoint
124
- success = load_checkpoint_api_cdn
125
-
126
- if success
127
- return
128
- else
129
- @base_client.log_internal Logger::INFO, "LoadCheckpoint: Fallback to GRPC API"
130
- end
131
-
132
- success = load_checkpoint_from_grpc_api
133
-
134
- if success
135
- return
136
- else
137
- @base_client.log_internal Logger::INFO, "LoadCheckpoint: Fallback to S3"
138
- end
139
-
140
- success = load_checkpoint_from_s3
77
+ success = load_checkpoint_from_config
141
78
 
142
79
  if !success
143
- @base_client.log_internal Logger::WARN, "No success loading checkpoints"
80
+ @base_client.log_internal Logger::INFO, "Fallback to S3"
81
+ load_checkpoint_from_s3
144
82
  end
145
- end
146
83
 
147
- def load_checkpoint_from_grpc_api
148
- config_req = Prefab::ConfigServicePointer.new(start_at_id: @config_loader.highwater_mark)
84
+ rescue => e
85
+ @base_client.log_internal Logger::WARN, "Unexpected problem loading checkpoint #{e}"
86
+ end
149
87
 
88
+ def load_checkpoint_from_config
89
+ config_req = Prefab::ConfigServicePointer.new(account_id: @base_client.account_id,
90
+ start_at_id: @config_loader.highwater_mark)
150
91
  resp = stub.get_all_config(config_req)
151
- load_configs(resp, :remote_api_grpc)
92
+ load_deltas(resp, :api)
93
+ resp.deltas.each do |delta|
94
+ @config_loader.set(delta)
95
+ end
96
+ @config_resolver.update
97
+ finish_init!(:api)
152
98
  true
153
- rescue GRPC::Unauthenticated
154
- @base_client.log_internal Logger::WARN, "Unauthenticated"
155
99
  rescue => e
156
- @base_client.log_internal Logger::WARN, "Unexpected grpc_api problem loading checkpoint #{e}"
100
+ @base_client.log_internal Logger::WARN, "Unexpected problem loading checkpoint #{e}"
157
101
  false
158
102
  end
159
103
 
160
- def load_checkpoint_api_cdn
161
- key_hash = Murmur3.murmur3_32(@base_client.api_key)
162
- url = "#{@options.url_for_api_cdn}/api/v1/config/#{@base_client.project_id}/#{key_hash}/0"
163
- conn = if Faraday::VERSION[0].to_i >= 2
164
- Faraday.new(url) do |conn|
165
- conn.request :authorization, :basic, @base_client.project_id, @base_client.api_key
166
- end
167
- else
168
- Faraday.new(url) do |conn|
169
- conn.request :basic_auth, @base_client.project_id, @base_client.api_key
170
- end
171
- end
172
- load_url(conn, :remote_cdn_api)
173
- end
174
-
175
104
  def load_checkpoint_from_s3
176
105
  url = "#{@s3_cloud_front}/#{@base_client.api_key.gsub("|", "/")}"
177
- load_url(Faraday.new(url), :remote_s3)
178
- end
179
-
180
- def load_url(conn, source)
181
- resp = conn.get('')
106
+ resp = Faraday.get url
182
107
  if resp.status == 200
183
- configs = Prefab::Configs.decode(resp.body)
184
- load_configs(configs, source)
185
- true
108
+ deltas = Prefab::ConfigDeltas.decode(resp.body)
109
+ load_deltas(deltas, :s3)
186
110
  else
187
- @base_client.log_internal Logger::INFO, "Checkpoint #{source} failed to load. Response #{resp.status}"
188
- false
111
+ @base_client.log_internal Logger::INFO, "No S3 checkpoint. Response #{resp.status} Plan may not support this."
189
112
  end
190
- rescue => e
191
- @base_client.log_internal Logger::WARN, "Unexpected #{source} problem loading checkpoint #{e} #{conn}"
192
- false
193
113
  end
194
114
 
195
- def load_configs(configs, source)
196
- project_env_id = configs.config_service_pointer.project_env_id
197
- @config_resolver.project_env_id = project_env_id
198
- starting_highwater_mark = @config_loader.highwater_mark
199
115
 
200
- configs.configs.each do |config|
201
- @config_loader.set(config, source)
202
- end
203
- if @config_loader.highwater_mark > starting_highwater_mark
204
- @base_client.log_internal Logger::INFO, "Found new checkpoint with highwater id #{@config_loader.highwater_mark} from #{source} in project #{@base_client.project_id} environment: #{project_env_id} and namespace: '#{@namespace}'"
205
- else
206
- @base_client.log_internal Logger::DEBUG, "Checkpoint with highwater id #{@config_loader.highwater_mark} from #{source}. No changes.", "prefab.config_client.load_configs"
116
+ def load_deltas(deltas, source)
117
+ deltas.deltas.each do |delta|
118
+ @config_loader.set(delta)
207
119
  end
120
+ @base_client.log_internal Logger::INFO, "Found checkpoint with highwater id #{@config_loader.highwater_mark} from #{source}"
208
121
  @base_client.stats.increment("prefab.config.checkpoint.load")
209
122
  @config_resolver.update
210
123
  finish_init!(source)
@@ -212,7 +125,6 @@ module Prefab
212
125
 
213
126
  # A thread that checks for a checkpoint
214
127
  def start_checkpointing_thread
215
-
216
128
  Thread.new do
217
129
  loop do
218
130
  begin
@@ -232,31 +144,47 @@ module Prefab
232
144
 
233
145
  def finish_init!(source)
234
146
  if @initialization_lock.write_locked?
235
- @base_client.log_internal Logger::INFO, "Unlocked Config via #{source}"
147
+ @base_client.log_internal Logger::DEBUG, "Unlocked Config via #{source}"
236
148
  @initialization_lock.release_write_lock
237
149
  @base_client.log.set_config_client(self)
238
- @base_client.log_internal Logger::INFO, to_s
239
150
  end
240
151
  end
241
152
 
242
- def start_sse_streaming_connection_thread(start_at_id)
243
- auth = "#{@base_client.project_id}:#{@base_client.api_key}"
244
- auth_string = Base64.strict_encode64(auth)
245
- headers = {
246
- "x-prefab-start-at-id": start_at_id,
247
- "Authorization": "Basic #{auth_string}",
248
- }
249
- url = "#{@base_client.prefab_api_url}/api/v1/sse/config"
250
- @base_client.log_internal Logger::INFO, "SSE Streaming Connect to #{url} start_at #{start_at_id}"
251
- @streaming_thread = SSE::Client.new(url,
252
- headers: headers,
253
- read_timeout: SSE_READ_TIMEOUT,
254
- logger: Prefab::InternalLogger.new("prefab.config.sse", @base_client.log)) do |client|
255
- client.on_event do |event|
256
- configs = Prefab::Configs.decode(Base64.decode64(event.data))
257
- load_configs(configs, :sse)
153
+ # Setup a streaming connection to the API
154
+ # Save new config values into the loader
155
+ def start_api_connection_thread(start_at_id)
156
+ config_req = Prefab::ConfigServicePointer.new(account_id: @base_client.account_id,
157
+ start_at_id: start_at_id)
158
+ @base_client.log_internal Logger::DEBUG, "start api connection thread #{start_at_id}"
159
+ @base_client.stats.increment("prefab.config.api.start")
160
+
161
+ @api_connection_thread = Thread.new do
162
+ at_exit do
163
+ @streaming = false
164
+ @cancellable_interceptor.cancel
165
+ end
166
+
167
+ while @streaming do
168
+ begin
169
+ resp = stub.get_config(config_req)
170
+ resp.each do |r|
171
+ r.deltas.each do |delta|
172
+ @config_loader.set(delta)
173
+ end
174
+ @config_resolver.update
175
+ finish_init!(:streaming)
176
+ end
177
+ rescue => e
178
+ if @streaming
179
+ level = e.code == 1 ? Logger::DEBUG : Logger::INFO
180
+ @base_client.log_internal level, ("config client encountered #{e.message} pausing #{RECONNECT_WAIT}")
181
+ reset
182
+ sleep(RECONNECT_WAIT)
183
+ end
184
+ end
258
185
  end
259
186
  end
187
+
260
188
  end
261
189
  end
262
190
  end
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  require 'yaml'
4
2
  module Prefab
5
3
  class ConfigLoader
@@ -7,7 +5,6 @@ module Prefab
7
5
 
8
6
  def initialize(base_client)
9
7
  @base_client = base_client
10
- @prefab_options = base_client.options
11
8
  @highwater_mark = 0
12
9
  @classpath_config = load_classpath_config
13
10
  @local_overrides = load_local_overrides
@@ -17,27 +14,24 @@ module Prefab
17
14
  def calc_config
18
15
  rtn = @classpath_config.clone
19
16
  @api_config.each_key do |k|
20
- rtn[k] = @api_config[k]
17
+ rtn[k] = @api_config[k].value
21
18
  end
22
19
  rtn = rtn.merge(@local_overrides)
23
20
  rtn
24
21
  end
25
22
 
26
- def set(config, source)
23
+ def set(delta)
27
24
  # don't overwrite newer values
28
- if @api_config[config.key] && @api_config[config.key][:config].id >= config.id
25
+ if @api_config[delta.key] && @api_config[delta.key].id > delta.id
29
26
  return
30
27
  end
31
28
 
32
- if config.rows.empty?
33
- @api_config.delete(config.key)
29
+ if delta.value.nil?
30
+ @api_config.delete(delta.key)
34
31
  else
35
- if @api_config[config.key]
36
- @base_client.log_internal Logger::DEBUG, "Replace #{config.key} with value from #{source} #{ @api_config[config.key][:config].id} -> #{config.id}"
37
- end
38
- @api_config[config.key] = { source: source, config: config }
32
+ @api_config[delta.key] = delta
39
33
  end
40
- @highwater_mark = [config.id, @highwater_mark].max
34
+ @highwater_mark = [delta.id, @highwater_mark].max
41
35
  end
42
36
 
43
37
  def rm(key)
@@ -45,22 +39,22 @@ module Prefab
45
39
  end
46
40
 
47
41
  def get_api_deltas
48
- configs = Prefab::Configs.new
42
+ deltas = Prefab::ConfigDeltas.new
49
43
  @api_config.each_value do |config_value|
50
- configs.configs << config_value[:config]
44
+ deltas.deltas << config_value
51
45
  end
52
- configs
46
+ deltas
53
47
  end
54
48
 
55
49
  private
56
50
 
57
51
  def load_classpath_config
58
- classpath_dir = @prefab_options.prefab_config_classpath_dir
52
+ classpath_dir = ENV['PREFAB_CONFIG_CLASSPATH_DIR'] || "."
59
53
  load_glob(File.join(classpath_dir, ".prefab*config.yaml"))
60
54
  end
61
55
 
62
56
  def load_local_overrides
63
- override_dir = @prefab_options.prefab_config_override_dir
57
+ override_dir = ENV['PREFAB_CONFIG_OVERRIDE_DIR'] || Dir.home
64
58
  load_glob(File.join(override_dir, ".prefab*config.yaml"))
65
59
  end
66
60
 
@@ -69,39 +63,7 @@ module Prefab
69
63
  Dir.glob(glob).each do |file|
70
64
  yaml = load(file)
71
65
  yaml.each do |k, v|
72
- if v.class == Hash
73
- v.each do |env_k, env_v|
74
- if k == @prefab_options.defaults_env
75
- if env_v.class == Hash && env_v['feature_flag']
76
- rtn[env_k] = feature_flag_config(file, k, env_k, env_v)
77
- else
78
- rtn[env_k] = {
79
- source: file,
80
- match: k,
81
- config: Prefab::Config.new(
82
- key: env_k,
83
- rows: [
84
- Prefab::ConfigRow.new(value: Prefab::ConfigValue.new(value_from(env_v)))
85
- ]
86
- )
87
- }
88
- end
89
- else
90
- next
91
- end
92
- end
93
- else
94
- rtn[k] = {
95
- source: file,
96
- match: "default",
97
- config: Prefab::Config.new(
98
- key: k,
99
- rows: [
100
- Prefab::ConfigRow.new(value: Prefab::ConfigValue.new(value_from(v)))
101
- ]
102
- )
103
- }
104
- end
66
+ rtn[k] = Prefab::ConfigValue.new(value_from(v))
105
67
  end
106
68
  end
107
69
  rtn
@@ -129,52 +91,5 @@ module Prefab
129
91
  { double: raw }
130
92
  end
131
93
  end
132
-
133
- def feature_flag_config(file, k, env_k, env_v)
134
- criteria = Prefab::Criteria.new(operator: 'ALWAYS_TRUE')
135
-
136
- if env_v['criteria']
137
- criteria = Prefab::Criteria.new(criteria_values(env_v['criteria']))
138
- end
139
-
140
- row = Prefab::ConfigRow.new(
141
- value: Prefab::ConfigValue.new(
142
- feature_flag: Prefab::FeatureFlag.new(
143
- active: true,
144
- inactive_variant_idx: -1, # not supported
145
- rules: [
146
- Prefab::Rule.new(
147
- variant_weights: [
148
- Prefab::VariantWeight.new(variant_idx: 0, weight: 1000)
149
- ],
150
- criteria: criteria
151
- )
152
- ]
153
- )
154
- )
155
- )
156
-
157
- unless env_v.has_key?('value')
158
- raise Prefab::Error, "Feature flag config `#{env_k}` #{file} must have a `value`"
159
- end
160
-
161
- {
162
- source: file,
163
- match: k,
164
- config: Prefab::Config.new(
165
- key: env_k,
166
- variants: [Prefab::FeatureFlagVariant.new(value_from(env_v['value']))],
167
- rows: [row]
168
- )
169
- }
170
- end
171
-
172
- def criteria_values(criteria_hash)
173
- if RUBY_VERSION < '2.7'
174
- criteria_hash.transform_keys(&:to_sym)
175
- else
176
- criteria_hash
177
- end
178
- end
179
94
  end
180
95
  end
@@ -1,62 +1,59 @@
1
- # frozen_string_literal: true
2
1
  module Prefab
3
2
  class ConfigResolver
4
- include Prefab::ConfigHelper
5
- NAMESPACE_DELIMITER = "."
6
-
7
- attr_accessor :project_env_id # this will be set by the config_client when it gets an API response
3
+ NAMESPACE_DELIMITER = ".".freeze
4
+ NAME_KEY_DELIMITER = ":".freeze
8
5
 
9
6
  def initialize(base_client, config_loader)
10
7
  @lock = Concurrent::ReadWriteLock.new
11
8
  @local_store = {}
12
- @namespace = base_client.options.namespace
9
+ @namespace = base_client.namespace
13
10
  @config_loader = config_loader
14
- @project_env_id = 0
15
11
  make_local
16
12
  end
17
13
 
18
14
  def to_s
19
- str = "\n"
15
+ str = ""
20
16
  @lock.with_read_lock do
21
17
  @local_store.each do |k, v|
22
- elements = [k.slice(0..49).ljust(50)]
23
- if v.nil?
24
- elements << "tombstone"
25
- else
26
- value = v[:value]
27
- elements << value_of(value).to_s.slice(0..34).ljust(35)
28
- elements << value_of(value).class.to_s.slice(0..6).ljust(7)
29
- elements << "Match: #{v[:match]}".slice(0..29).ljust(30)
30
- elements << "Source: #{v[:source]}"
31
- end
32
- str += elements.join(" | ") << "\n"
18
+ value = v[:value]
19
+ str << "|#{k}| in #{v[:namespace]} |#{value_of(value)}|#{value_of(value).class}\n"
33
20
  end
34
21
  end
35
22
  str
36
23
  end
37
24
 
38
25
  def get(property)
39
- config = _get(property)
40
- config ? value_of(config[:value]) : nil
41
- end
42
-
43
- def get_config(property)
44
- config = _get(property)
45
- config ? config[:config] : nil
46
- end
47
-
48
- def _get(key)
49
- @lock.with_read_lock do
50
- @local_store[key]
26
+ config = @lock.with_read_lock do
27
+ @local_store[property]
51
28
  end
29
+ config ? value_of(config[:value]) : nil
52
30
  end
53
31
 
54
32
  def update
55
33
  make_local
56
34
  end
57
35
 
36
+ def export_api_deltas
37
+ @config_loader.get_api_deltas
38
+ end
39
+
58
40
  private
59
41
 
42
+ def value_of(config_value)
43
+ case config_value.type
44
+ when :string
45
+ config_value.string
46
+ when :int
47
+ config_value.int
48
+ when :double
49
+ config_value.double
50
+ when :bool
51
+ config_value.bool
52
+ when :feature_flag
53
+ config_value.feature_flag
54
+ end
55
+ end
56
+
60
57
  # Should client a.b.c see key in namespace a.b? yes
61
58
  # Should client a.b.c see key in namespace a.b.c? yes
62
59
  # Should client a.b.c see key in namespace a.b.d? no
@@ -64,37 +61,33 @@ module Prefab
64
61
  #
65
62
  def starts_with_ns?(key_namespace, client_namespace)
66
63
  zipped = key_namespace.split(NAMESPACE_DELIMITER).zip(client_namespace.split(NAMESPACE_DELIMITER))
67
- mapped = zipped.map do |k, c|
68
- (k.nil? || k.empty?) || k == c
69
- end
70
- [mapped.all?, mapped.size]
64
+ zipped.map do |k, c|
65
+ (k.nil? || k.empty?) || c == k
66
+ end.all?
71
67
  end
72
68
 
73
69
  def make_local
74
70
  store = {}
75
- @config_loader.calc_config.each do |key, config_resolver_obj|
76
- config = config_resolver_obj[:config]
77
- sortable = config.rows.map do |row|
78
- if row.project_env_id != 0
79
- if row.project_env_id == @project_env_id
80
- if !row.namespace.empty?
81
- (starts_with, count) = starts_with_ns?(row.namespace, @namespace)
82
- # rubocop:disable BlockNesting
83
- { sortable: 2 + count, match: "nm:#{row.namespace}", value: row.value, config: config} if starts_with
84
- else
85
- { sortable: 1, match: "env:#{row.project_env_id}", value: row.value, config: config}
86
- end
87
- end
88
- else
89
- match = config_resolver_obj[:match] || "default"
90
- { sortable: 0, match: match, value: row.value, config: config}
71
+ @config_loader.calc_config.each do |prop, value|
72
+ property = prop
73
+ key_namespace = ""
74
+
75
+ split = prop.split(NAME_KEY_DELIMITER)
76
+
77
+ if split.size > 1
78
+ property = split[1..-1].join(NAME_KEY_DELIMITER)
79
+ key_namespace = split[0]
80
+ end
81
+
82
+ if starts_with_ns?(key_namespace, @namespace)
83
+ existing = store[property]
84
+ if existing.nil?
85
+ store[property] = { namespace: key_namespace, value: value }
86
+ elsif existing[:namespace].split(NAMESPACE_DELIMITER).size < key_namespace.split(NAMESPACE_DELIMITER).size
87
+ store[property] = { namespace: key_namespace, value: value }
91
88
  end
92
- end.compact
93
- to_store = sortable.sort_by { |h| h[:sortable] }.last
94
- to_store[:source] = config_resolver_obj[:source]
95
- store[key] = to_store
89
+ end
96
90
  end
97
-
98
91
  @lock.with_write_lock do
99
92
  @local_store = store
100
93
  end