prefab-cloud-ruby 0.6.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.tool-versions +1 -0
- data/CODEOWNERS +1 -0
- data/Gemfile +7 -3
- data/Gemfile.lock +46 -11
- data/README.md +10 -1
- data/VERSION +1 -1
- data/compile_protos.sh +3 -0
- data/lib/prefab/client.rb +16 -7
- data/lib/prefab/config_client.rb +57 -20
- data/lib/prefab/config_helper.rb +20 -0
- data/lib/prefab/config_loader.rb +13 -11
- data/lib/prefab/config_resolver.rb +37 -41
- data/lib/prefab/feature_flag_client.rb +100 -11
- data/lib/prefab-cloud-ruby.rb +3 -0
- data/lib/prefab_pb.rb +201 -132
- data/lib/prefab_services_pb.rb +6 -6
- data/prefab-cloud-ruby.gemspec +31 -29
- data/run_test_harness_server.sh +2 -0
- data/test/harness_server.rb +51 -0
- data/test/test_config_loader.rb +20 -20
- data/test/test_config_resolver.rb +119 -37
- data/test/test_feature_flag_client.rb +191 -35
- data/test/test_helper.rb +18 -3
- metadata +77 -18
- data/.ruby-version +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 80a03ec9a75bc4c5d827c746354ff1353fe135a5ab6e31e9beaaf1ca1c27c931
|
4
|
+
data.tar.gz: d4c9f02e6bbb54d8d4884cc2d94179f39e859aafe3966188b1a00f7b7e5ccc48
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 67f8b5ed6f61f01943736f9abbb685c9d7f7fd4c029ff3e051356f6e65936595f18899160fd842c415d86c371b0f9d915d795c6b9adafbdebeadd5b6fc6cc75b
|
7
|
+
data.tar.gz: c56dd76208ad8bbfd9d7add1bbc317a951671c2590cecf93389fbb5170aa9da13898bb3501e396450df2eaa93a67d7041b24fbdc2ca77f2a414056d0cbf471f9
|
data/.tool-versions
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby 3.0.3
|
data/CODEOWNERS
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
* @prefab-cloud/prefabdevs @prefab-cloud/prefabmaintainers @prefab-cloud/prefabadmins
|
data/Gemfile
CHANGED
@@ -2,13 +2,17 @@ source "https://rubygems.org"
|
|
2
2
|
|
3
3
|
gem 'concurrent-ruby', '~> 1.0', '>= 1.0.5'
|
4
4
|
gem 'faraday'
|
5
|
-
gem '
|
5
|
+
gem 'ld-eventsource'
|
6
|
+
gem 'grpc', :platforms => :ruby
|
7
|
+
gem 'google-protobuf', :platforms => :ruby
|
8
|
+
gem 'googleapis-common-protos-types', :platforms => :ruby
|
6
9
|
|
7
10
|
group :development do
|
8
|
-
gem 'grpc-tools',
|
11
|
+
gem 'grpc-tools', :platforms => :ruby
|
9
12
|
gem "shoulda", ">= 0"
|
10
13
|
gem "rdoc", "~> 3.12"
|
11
|
-
gem "bundler"
|
14
|
+
gem "bundler"
|
12
15
|
gem "juwelier", "~> 2.4.9"
|
13
16
|
gem "simplecov", ">= 0"
|
17
|
+
gem 'thin'
|
14
18
|
end
|
data/Gemfile.lock
CHANGED
@@ -10,14 +10,22 @@ GEM
|
|
10
10
|
public_suffix (>= 2.0.2, < 5.0)
|
11
11
|
builder (3.2.4)
|
12
12
|
concurrent-ruby (1.1.8)
|
13
|
+
daemons (1.4.1)
|
13
14
|
descendants_tracker (0.0.4)
|
14
15
|
thread_safe (~> 0.3, >= 0.3.1)
|
15
16
|
docile (1.3.5)
|
17
|
+
domain_name (0.5.20190701)
|
18
|
+
unf (>= 0.0.5, < 1.0.0)
|
19
|
+
eventmachine (1.2.7)
|
16
20
|
faraday (1.3.0)
|
17
21
|
faraday-net_http (~> 1.0)
|
18
22
|
multipart-post (>= 1.2, < 3)
|
19
23
|
ruby2_keywords
|
20
24
|
faraday-net_http (1.0.1)
|
25
|
+
ffi (1.15.5)
|
26
|
+
ffi-compiler (1.0.1)
|
27
|
+
ffi (>= 1.0.0)
|
28
|
+
rake
|
21
29
|
git (1.8.1)
|
22
30
|
rchardet (~> 1.8)
|
23
31
|
github_api (0.19.0)
|
@@ -26,15 +34,23 @@ GEM
|
|
26
34
|
faraday (>= 0.8, < 2)
|
27
35
|
hashie (~> 3.5, >= 3.5.2)
|
28
36
|
oauth2 (~> 1.0)
|
29
|
-
google-protobuf (3.
|
30
|
-
googleapis-common-protos-types (1.0
|
31
|
-
google-protobuf (~> 3.14)
|
32
|
-
grpc (1.36.0)
|
37
|
+
google-protobuf (3.19.3)
|
38
|
+
googleapis-common-protos-types (1.3.0)
|
33
39
|
google-protobuf (~> 3.14)
|
40
|
+
grpc (1.43.1)
|
41
|
+
google-protobuf (~> 3.18)
|
34
42
|
googleapis-common-protos-types (~> 1.0)
|
35
|
-
grpc-tools (1.
|
43
|
+
grpc-tools (1.43.1)
|
36
44
|
hashie (3.6.0)
|
37
45
|
highline (2.0.3)
|
46
|
+
http (5.0.1)
|
47
|
+
addressable (~> 2.3)
|
48
|
+
http-cookie (~> 1.0)
|
49
|
+
http-form_data (~> 2.2)
|
50
|
+
llhttp-ffi (~> 0.3.0)
|
51
|
+
http-cookie (1.0.4)
|
52
|
+
domain_name (~> 0.5)
|
53
|
+
http-form_data (2.3.0)
|
38
54
|
i18n (1.8.9)
|
39
55
|
concurrent-ruby (~> 1.0)
|
40
56
|
json (1.8.6)
|
@@ -53,13 +69,20 @@ GEM
|
|
53
69
|
jwt (2.2.2)
|
54
70
|
kamelcase (0.0.2)
|
55
71
|
semver2 (~> 3)
|
56
|
-
|
72
|
+
ld-eventsource (2.2.0)
|
73
|
+
concurrent-ruby (~> 1.0)
|
74
|
+
http (>= 4.4.1, < 6.0.0)
|
75
|
+
llhttp-ffi (0.3.1)
|
76
|
+
ffi-compiler (~> 1.0)
|
77
|
+
rake (~> 13.0)
|
78
|
+
mini_portile2 (2.7.1)
|
57
79
|
minitest (5.14.4)
|
58
80
|
multi_json (1.15.0)
|
59
81
|
multi_xml (0.6.0)
|
60
82
|
multipart-post (2.1.1)
|
61
|
-
nokogiri (1.
|
62
|
-
mini_portile2 (~> 2.
|
83
|
+
nokogiri (1.13.1)
|
84
|
+
mini_portile2 (~> 2.7.0)
|
85
|
+
racc (~> 1.4)
|
63
86
|
oauth2 (1.4.7)
|
64
87
|
faraday (>= 0.8, < 2.0)
|
65
88
|
jwt (>= 1.0, < 3.0)
|
@@ -68,6 +91,7 @@ GEM
|
|
68
91
|
rack (>= 1.2, < 3)
|
69
92
|
psych (3.3.1)
|
70
93
|
public_suffix (4.0.6)
|
94
|
+
racc (1.6.0)
|
71
95
|
rack (2.2.3)
|
72
96
|
rake (13.0.3)
|
73
97
|
rchardet (1.8.0)
|
@@ -85,23 +109,34 @@ GEM
|
|
85
109
|
docile (~> 1.1)
|
86
110
|
simplecov-html (~> 0.11)
|
87
111
|
simplecov-html (0.12.3)
|
112
|
+
thin (1.8.1)
|
113
|
+
daemons (~> 1.0, >= 1.0.9)
|
114
|
+
eventmachine (~> 1.0, >= 1.0.4)
|
115
|
+
rack (>= 1, < 3)
|
88
116
|
thread_safe (0.3.6)
|
89
117
|
tzinfo (1.2.9)
|
90
118
|
thread_safe (~> 0.1)
|
119
|
+
unf (0.1.4)
|
120
|
+
unf_ext
|
121
|
+
unf_ext (0.0.8)
|
91
122
|
|
92
123
|
PLATFORMS
|
93
124
|
ruby
|
94
125
|
|
95
126
|
DEPENDENCIES
|
96
|
-
bundler
|
127
|
+
bundler
|
97
128
|
concurrent-ruby (~> 1.0, >= 1.0.5)
|
98
129
|
faraday
|
130
|
+
google-protobuf
|
131
|
+
googleapis-common-protos-types
|
99
132
|
grpc
|
100
|
-
grpc-tools
|
133
|
+
grpc-tools
|
101
134
|
juwelier (~> 2.4.9)
|
135
|
+
ld-eventsource
|
102
136
|
rdoc (~> 3.12)
|
103
137
|
shoulda
|
104
138
|
simplecov
|
139
|
+
thin
|
105
140
|
|
106
141
|
BUNDLED WITH
|
107
|
-
|
142
|
+
2.3.5
|
data/README.md
CHANGED
@@ -53,7 +53,16 @@ end
|
|
53
53
|
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
54
54
|
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
55
55
|
|
56
|
+
## Release
|
57
|
+
|
58
|
+
```shell
|
59
|
+
update VERSION
|
60
|
+
bundle exec rake gemspec:generate
|
61
|
+
git commit & push
|
62
|
+
REMOTE_BRANCH=main LOCAL_BRANCH=main bundle exec rake release
|
63
|
+
```
|
64
|
+
|
56
65
|
## Copyright
|
57
66
|
|
58
|
-
Copyright (c)
|
67
|
+
Copyright (c) 2022 Jeff Dwyer. See LICENSE.txt for
|
59
68
|
further details.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.9.0
|
data/compile_protos.sh
CHANGED
data/lib/prefab/client.rb
CHANGED
@@ -8,7 +8,7 @@ module Prefab
|
|
8
8
|
}
|
9
9
|
|
10
10
|
|
11
|
-
attr_reader :
|
11
|
+
attr_reader :project_id, :shared_cache, :stats, :namespace, :interceptor, :api_key, :environment, :prefab_api_url
|
12
12
|
|
13
13
|
def initialize(api_key: ENV['PREFAB_API_KEY'],
|
14
14
|
logdev: nil,
|
@@ -19,26 +19,31 @@ module Prefab
|
|
19
19
|
log_formatter: DEFAULT_LOG_FORMATTER
|
20
20
|
)
|
21
21
|
raise "No API key. Set PREFAB_API_KEY env var" if api_key.nil? || api_key.empty?
|
22
|
+
raise "PREFAB_API_KEY format invalid. Expecting 123-development-yourapikey-SDK" unless api_key.count("-") == 3
|
22
23
|
@logdev = (logdev || $stdout)
|
23
24
|
@log_formatter = log_formatter
|
24
25
|
@local = local
|
25
26
|
@stats = (stats || NoopStats.new)
|
26
27
|
@shared_cache = (shared_cache || NoopCache.new)
|
27
28
|
@api_key = api_key
|
28
|
-
@
|
29
|
+
@project_id = api_key.split("-")[0].to_i
|
30
|
+
@environment = api_key.split("-")[1]
|
29
31
|
@namespace = namespace
|
30
32
|
@interceptor = Prefab::AuthInterceptor.new(api_key)
|
31
33
|
@stubs = {}
|
32
|
-
|
34
|
+
@prefab_api_url = ENV["PREFAB_API_URL"] || 'https://api.prefab.cloud'
|
35
|
+
@prefab_grpc_url = ENV["PREFAB_GRPC_URL"] || 'grpc.prefab.cloud:443'
|
36
|
+
log_internal Logger::INFO, "Prefab Initializing in environment: '#{@environment}' and namespace: '#{@namespace}'"
|
37
|
+
log_internal Logger::INFO, "Prefab Connecting to: #{@prefab_api_url} and #{@prefab_grpc_url} Secure: #{http_secure?}"
|
33
38
|
at_exit do
|
34
39
|
channel.destroy
|
35
40
|
end
|
36
41
|
end
|
37
42
|
|
38
43
|
def channel
|
39
|
-
credentials =
|
40
|
-
|
41
|
-
@_channel ||= GRPC::Core::Channel.new(
|
44
|
+
credentials = http_secure? ? creds : :this_channel_is_insecure
|
45
|
+
log_internal Logger::DEBUG, "GRPC Channel #{@prefab_grpc_url} #{credentials}"
|
46
|
+
@_channel ||= GRPC::Core::Channel.new(@prefab_grpc_url, nil, credentials)
|
42
47
|
end
|
43
48
|
|
44
49
|
def config_client(timeout: 5.0)
|
@@ -88,7 +93,7 @@ module Prefab
|
|
88
93
|
end
|
89
94
|
|
90
95
|
def cache_key(post_fix)
|
91
|
-
"prefab:#{
|
96
|
+
"prefab:#{project_id}:#{post_fix}"
|
92
97
|
end
|
93
98
|
|
94
99
|
def reset!
|
@@ -98,6 +103,10 @@ module Prefab
|
|
98
103
|
|
99
104
|
private
|
100
105
|
|
106
|
+
def http_secure?
|
107
|
+
ENV["PREFAB_CLOUD_HTTP"] != "true"
|
108
|
+
end
|
109
|
+
|
101
110
|
def stub_for(service, timeout)
|
102
111
|
@stubs["#{service}_#{timeout}"] ||= service::Stub.new(nil,
|
103
112
|
nil,
|
data/lib/prefab/config_client.rb
CHANGED
@@ -6,6 +6,7 @@ module Prefab
|
|
6
6
|
|
7
7
|
def initialize(base_client, timeout)
|
8
8
|
@base_client = base_client
|
9
|
+
@base_client.log_internal Logger::DEBUG, "Initialize ConfigClient"
|
9
10
|
@timeout = timeout
|
10
11
|
@initialization_lock = Concurrent::ReadWriteLock.new
|
11
12
|
|
@@ -14,23 +15,27 @@ module Prefab
|
|
14
15
|
@config_loader = Prefab::ConfigLoader.new(@base_client)
|
15
16
|
@config_resolver = Prefab::ConfigResolver.new(@base_client, @config_loader)
|
16
17
|
|
18
|
+
@base_client.log_internal Logger::DEBUG, "Initialize ConfigClient: AcquireWriteLock"
|
17
19
|
@initialization_lock.acquire_write_lock
|
20
|
+
@base_client.log_internal Logger::DEBUG, "Initialize ConfigClient: AcquiredWriteLock"
|
18
21
|
|
19
22
|
@cancellable_interceptor = Prefab::CancellableInterceptor.new(@base_client)
|
20
23
|
|
21
24
|
@s3_cloud_front = ENV["PREFAB_S3CF_BUCKET"] || DEFAULT_S3CF_BUCKET
|
25
|
+
|
22
26
|
load_checkpoint
|
23
27
|
start_checkpointing_thread
|
24
28
|
end
|
25
29
|
|
26
30
|
def start_streaming
|
27
31
|
@streaming = true
|
28
|
-
|
32
|
+
# start_grpc_streaming_connection_thread(@config_loader.highwater_mark)
|
33
|
+
start_sse_streaming_connection_thread(@config_loader.highwater_mark)
|
29
34
|
end
|
30
35
|
|
31
|
-
def get(
|
36
|
+
def get(key)
|
32
37
|
@initialization_lock.with_read_lock do
|
33
|
-
@config_resolver.get(
|
38
|
+
@config_resolver.get(key)
|
34
39
|
end
|
35
40
|
end
|
36
41
|
|
@@ -58,8 +63,14 @@ module Prefab
|
|
58
63
|
end
|
59
64
|
|
60
65
|
def self.value_to_delta(key, config_value, namespace = nil)
|
61
|
-
Prefab::
|
62
|
-
|
66
|
+
Prefab::Config.new(key: [namespace, key].compact.join(":"),
|
67
|
+
rows: [Prefab::ConfigRow.new(value: config_value)])
|
68
|
+
end
|
69
|
+
|
70
|
+
def get_config_obj(key)
|
71
|
+
@initialization_lock.with_read_lock do
|
72
|
+
@config_resolver.get_config(key)
|
73
|
+
end
|
63
74
|
end
|
64
75
|
|
65
76
|
private
|
@@ -86,11 +97,14 @@ module Prefab
|
|
86
97
|
end
|
87
98
|
|
88
99
|
def load_checkpoint_from_config
|
89
|
-
|
90
|
-
|
100
|
+
@base_client.log_internal Logger::DEBUG, "Load Checkpoint From Config"
|
101
|
+
|
102
|
+
config_req = Prefab::ConfigServicePointer.new(start_at_id: @config_loader.highwater_mark)
|
103
|
+
|
91
104
|
resp = stub.get_all_config(config_req)
|
92
|
-
|
93
|
-
resp
|
105
|
+
@base_client.log_internal Logger::DEBUG, "Got Response #{resp}"
|
106
|
+
load_configs(resp, :api)
|
107
|
+
resp.configs.each do |delta|
|
94
108
|
@config_loader.set(delta)
|
95
109
|
end
|
96
110
|
@config_resolver.update
|
@@ -105,17 +119,16 @@ module Prefab
|
|
105
119
|
url = "#{@s3_cloud_front}/#{@base_client.api_key.gsub("|", "/")}"
|
106
120
|
resp = Faraday.get url
|
107
121
|
if resp.status == 200
|
108
|
-
|
109
|
-
|
122
|
+
configs = Prefab::Configs.decode(resp.body)
|
123
|
+
load_configs(configs, :s3)
|
110
124
|
else
|
111
125
|
@base_client.log_internal Logger::INFO, "No S3 checkpoint. Response #{resp.status} Plan may not support this."
|
112
126
|
end
|
113
127
|
end
|
114
128
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
@config_loader.set(delta)
|
129
|
+
def load_configs(configs, source)
|
130
|
+
configs.configs.each do |config|
|
131
|
+
@config_loader.set(config)
|
119
132
|
end
|
120
133
|
@base_client.log_internal Logger::INFO, "Found checkpoint with highwater id #{@config_loader.highwater_mark} from #{source}"
|
121
134
|
@base_client.stats.increment("prefab.config.checkpoint.load")
|
@@ -150,11 +163,35 @@ module Prefab
|
|
150
163
|
end
|
151
164
|
end
|
152
165
|
|
166
|
+
|
167
|
+
def start_sse_streaming_connection_thread(start_at_id)
|
168
|
+
auth = "#{@base_client.project_id}:#{@base_client.api_key}"
|
169
|
+
|
170
|
+
auth_string = Base64.strict_encode64(auth)
|
171
|
+
headers = {
|
172
|
+
"x-prefab-start-at-id": start_at_id,
|
173
|
+
"Authorization": "Basic #{auth_string}",
|
174
|
+
}
|
175
|
+
url = "#{@base_client.prefab_api_url}/api/v1/sse/config"
|
176
|
+
@base_client.log_internal Logger::INFO, "SSE Streaming Connect to #{url}"
|
177
|
+
SSE::Client.new(url, headers: headers) do |client|
|
178
|
+
client.on_event do |event|
|
179
|
+
configs = Prefab::Configs.decode(Base64.decode64(event.data))
|
180
|
+
@base_client.log_internal Logger::INFO, "SSE received configs."
|
181
|
+
@base_client.log_internal Logger::DEBUG, "SSE received configs: #{configs}"
|
182
|
+
configs.configs.each do |config|
|
183
|
+
@config_loader.set(config)
|
184
|
+
end
|
185
|
+
@config_resolver.update
|
186
|
+
finish_init!(:streaming)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
153
191
|
# Setup a streaming connection to the API
|
154
192
|
# Save new config values into the loader
|
155
|
-
def
|
156
|
-
config_req = Prefab::ConfigServicePointer.new(
|
157
|
-
start_at_id: start_at_id)
|
193
|
+
def start_grpc_streaming_connection_thread(start_at_id)
|
194
|
+
config_req = Prefab::ConfigServicePointer.new(start_at_id: start_at_id)
|
158
195
|
@base_client.log_internal Logger::DEBUG, "start api connection thread #{start_at_id}"
|
159
196
|
@base_client.stats.increment("prefab.config.api.start")
|
160
197
|
|
@@ -168,8 +205,8 @@ module Prefab
|
|
168
205
|
begin
|
169
206
|
resp = stub.get_config(config_req)
|
170
207
|
resp.each do |r|
|
171
|
-
r.
|
172
|
-
@config_loader.set(
|
208
|
+
r.configs.each do |config|
|
209
|
+
@config_loader.set(config)
|
173
210
|
end
|
174
211
|
@config_resolver.update
|
175
212
|
finish_init!(:streaming)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Prefab
|
2
|
+
module ConfigHelper
|
3
|
+
def value_of(config_value)
|
4
|
+
case config_value.type
|
5
|
+
when :string
|
6
|
+
config_value.string
|
7
|
+
when :int
|
8
|
+
config_value.int
|
9
|
+
when :double
|
10
|
+
config_value.double
|
11
|
+
when :bool
|
12
|
+
config_value.bool
|
13
|
+
when :feature_flag
|
14
|
+
config_value.feature_flag
|
15
|
+
when :segment
|
16
|
+
config_value.segment
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/prefab/config_loader.rb
CHANGED
@@ -14,24 +14,24 @@ module Prefab
|
|
14
14
|
def calc_config
|
15
15
|
rtn = @classpath_config.clone
|
16
16
|
@api_config.each_key do |k|
|
17
|
-
rtn[k] = @api_config[k]
|
17
|
+
rtn[k] = @api_config[k]
|
18
18
|
end
|
19
19
|
rtn = rtn.merge(@local_overrides)
|
20
20
|
rtn
|
21
21
|
end
|
22
22
|
|
23
|
-
def set(
|
23
|
+
def set(config)
|
24
24
|
# don't overwrite newer values
|
25
|
-
if @api_config[
|
25
|
+
if @api_config[config.key] && @api_config[config.key].id > config.id
|
26
26
|
return
|
27
27
|
end
|
28
28
|
|
29
|
-
if
|
30
|
-
@api_config.delete(
|
29
|
+
if config.rows.empty?
|
30
|
+
@api_config.delete(config.key)
|
31
31
|
else
|
32
|
-
@api_config[
|
32
|
+
@api_config[config.key] = config
|
33
33
|
end
|
34
|
-
@highwater_mark = [
|
34
|
+
@highwater_mark = [config.id, @highwater_mark].max
|
35
35
|
end
|
36
36
|
|
37
37
|
def rm(key)
|
@@ -39,11 +39,11 @@ module Prefab
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def get_api_deltas
|
42
|
-
|
42
|
+
configs = Prefab::Configs.new
|
43
43
|
@api_config.each_value do |config_value|
|
44
|
-
|
44
|
+
configs.configs << config_value
|
45
45
|
end
|
46
|
-
|
46
|
+
configs
|
47
47
|
end
|
48
48
|
|
49
49
|
private
|
@@ -63,7 +63,9 @@ module Prefab
|
|
63
63
|
Dir.glob(glob).each do |file|
|
64
64
|
yaml = load(file)
|
65
65
|
yaml.each do |k, v|
|
66
|
-
rtn[k] = Prefab::
|
66
|
+
rtn[k] = Prefab::Config.new(key: k, rows: [
|
67
|
+
Prefab::ConfigRow.new(value: Prefab::ConfigValue.new(value_from(v)))
|
68
|
+
])
|
67
69
|
end
|
68
70
|
end
|
69
71
|
rtn
|
@@ -1,11 +1,12 @@
|
|
1
1
|
module Prefab
|
2
2
|
class ConfigResolver
|
3
|
+
include Prefab::ConfigHelper
|
3
4
|
NAMESPACE_DELIMITER = ".".freeze
|
4
|
-
NAME_KEY_DELIMITER = ":".freeze
|
5
5
|
|
6
6
|
def initialize(base_client, config_loader)
|
7
7
|
@lock = Concurrent::ReadWriteLock.new
|
8
8
|
@local_store = {}
|
9
|
+
@environment = base_client.environment
|
9
10
|
@namespace = base_client.namespace
|
10
11
|
@config_loader = config_loader
|
11
12
|
make_local
|
@@ -16,19 +17,22 @@ module Prefab
|
|
16
17
|
@lock.with_read_lock do
|
17
18
|
@local_store.each do |k, v|
|
18
19
|
value = v[:value]
|
19
|
-
str << "|#{k}|
|
20
|
+
str << "|#{k}| from #{v[:match]} |#{value_of(value)}|#{value_of(value).class}\n"
|
20
21
|
end
|
21
22
|
end
|
22
23
|
str
|
23
24
|
end
|
24
25
|
|
25
26
|
def get(property)
|
26
|
-
config =
|
27
|
-
@local_store[property]
|
28
|
-
end
|
27
|
+
config = _get(property)
|
29
28
|
config ? value_of(config[:value]) : nil
|
30
29
|
end
|
31
30
|
|
31
|
+
def get_config(property)
|
32
|
+
config = _get(property)
|
33
|
+
config ? config[:config] : nil
|
34
|
+
end
|
35
|
+
|
32
36
|
def update
|
33
37
|
make_local
|
34
38
|
end
|
@@ -39,21 +43,6 @@ module Prefab
|
|
39
43
|
|
40
44
|
private
|
41
45
|
|
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
|
-
|
57
46
|
# Should client a.b.c see key in namespace a.b? yes
|
58
47
|
# Should client a.b.c see key in namespace a.b.c? yes
|
59
48
|
# Should client a.b.c see key in namespace a.b.d? no
|
@@ -61,36 +50,43 @@ module Prefab
|
|
61
50
|
#
|
62
51
|
def starts_with_ns?(key_namespace, client_namespace)
|
63
52
|
zipped = key_namespace.split(NAMESPACE_DELIMITER).zip(client_namespace.split(NAMESPACE_DELIMITER))
|
64
|
-
zipped.map do |k, c|
|
65
|
-
(k.nil? || k.empty?) ||
|
66
|
-
end
|
53
|
+
mapped = zipped.map do |k, c|
|
54
|
+
(k.nil? || k.empty?) || k == c
|
55
|
+
end
|
56
|
+
[mapped.all?, mapped.size]
|
67
57
|
end
|
68
58
|
|
69
59
|
def make_local
|
70
60
|
store = {}
|
71
|
-
@config_loader.calc_config.each do |
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
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 }
|
61
|
+
@config_loader.calc_config.each do |key, config|
|
62
|
+
sortable = config.rows.map do |row|
|
63
|
+
if !row.env_key.empty?
|
64
|
+
if row.env_key == @environment
|
65
|
+
if !row.namespace.empty?
|
66
|
+
(starts_with, count) = starts_with_ns?(row.namespace, @namespace)
|
67
|
+
# rubocop:disable BlockNesting
|
68
|
+
{ sortable: 2 + count, match: row.namespace, value: row.value, config: config} if starts_with
|
69
|
+
else
|
70
|
+
{ sortable: 1, match: row.env_key, value: row.value, config: config}
|
71
|
+
end
|
72
|
+
end
|
73
|
+
else
|
74
|
+
{ sortable: 0, match: "default", value: row.value, config: config}
|
88
75
|
end
|
89
|
-
end
|
76
|
+
end.compact
|
77
|
+
to_store = sortable.sort_by { |h| h[:sortable] }.last
|
78
|
+
store[key] = to_store
|
90
79
|
end
|
80
|
+
|
91
81
|
@lock.with_write_lock do
|
92
82
|
@local_store = store
|
93
83
|
end
|
94
84
|
end
|
85
|
+
|
86
|
+
def _get(property)
|
87
|
+
@lock.with_read_lock do
|
88
|
+
@local_store[property]
|
89
|
+
end
|
90
|
+
end
|
95
91
|
end
|
96
92
|
end
|