prefab-cloud-ruby 0.5.0 → 0.7.0
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/.tool-versions +1 -0
- data/CODEOWNERS +1 -0
- data/Gemfile +6 -3
- data/Gemfile.lock +63 -47
- data/README.md +1 -1
- data/VERSION +1 -1
- data/compile_protos.sh +3 -0
- data/lib/prefab/cancellable_interceptor.rb +18 -0
- data/lib/prefab/client.rb +7 -4
- data/lib/prefab/config_client.rb +27 -15
- data/lib/prefab/config_helper.rb +20 -0
- data/lib/prefab/config_loader.rb +3 -3
- data/lib/prefab/config_resolver.rb +38 -34
- data/lib/prefab/feature_flag_client.rb +99 -11
- data/lib/prefab-cloud-ruby.rb +1 -0
- data/lib/prefab_pb.rb +206 -132
- data/lib/prefab_services_pb.rb +6 -6
- data/prefab-cloud-ruby.gemspec +30 -30
- data/run_test_harness_server.sh +2 -0
- data/test/harness_server.rb +46 -0
- data/test/test_config_loader.rb +12 -12
- data/test/test_config_resolver.rb +107 -37
- data/test/test_feature_flag_client.rb +191 -35
- data/test/test_helper.rb +13 -3
- metadata +70 -25
- 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: a36d24a94355b3b7a2b3b889734e1f6f6ccafc7dcebfb11ff11d77ba92ea7828
|
|
4
|
+
data.tar.gz: 326b3166d4fc2589ebb2550114b2cc198617620daa70ecfb6b9fe394c9dd79a5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fb73839190f23e05540688572b8f47c1300ae9181bdecef0f24fdecb9a9e73ace1c88937e7c228601c20ff68405876c21b977e45bd691ba33f59d71dd4811718
|
|
7
|
+
data.tar.gz: 792f04aa032c2f9975444a191ddbb8f727e256d12ec2fc4b3c26819bff4a425deabffb3ff262221e7fe90ed4c1d1422d7a2cb447530264cde494b4e149de7e78
|
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,16 @@ source "https://rubygems.org"
|
|
|
2
2
|
|
|
3
3
|
gem 'concurrent-ruby', '~> 1.0', '>= 1.0.5'
|
|
4
4
|
gem 'faraday'
|
|
5
|
-
gem 'grpc',
|
|
5
|
+
gem 'grpc', :platforms => :ruby
|
|
6
|
+
gem 'google-protobuf', :platforms => :ruby
|
|
7
|
+
gem 'googleapis-common-protos-types', :platforms => :ruby
|
|
6
8
|
|
|
7
9
|
group :development do
|
|
8
|
-
gem 'grpc-tools',
|
|
10
|
+
gem 'grpc-tools', :platforms => :ruby
|
|
9
11
|
gem "shoulda", ">= 0"
|
|
10
12
|
gem "rdoc", "~> 3.12"
|
|
11
|
-
gem "bundler"
|
|
13
|
+
gem "bundler"
|
|
12
14
|
gem "juwelier", "~> 2.4.9"
|
|
13
15
|
gem "simplecov", ">= 0"
|
|
16
|
+
gem 'thin'
|
|
14
17
|
end
|
data/Gemfile.lock
CHANGED
|
@@ -1,37 +1,43 @@
|
|
|
1
1
|
GEM
|
|
2
2
|
remote: https://rubygems.org/
|
|
3
3
|
specs:
|
|
4
|
-
activesupport (5.2.
|
|
4
|
+
activesupport (5.2.4.5)
|
|
5
5
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
6
6
|
i18n (>= 0.7, < 2)
|
|
7
7
|
minitest (~> 5.1)
|
|
8
8
|
tzinfo (~> 1.1)
|
|
9
|
-
addressable (2.
|
|
10
|
-
public_suffix (>= 2.0.2, <
|
|
11
|
-
builder (3.2.
|
|
12
|
-
concurrent-ruby (1.
|
|
9
|
+
addressable (2.7.0)
|
|
10
|
+
public_suffix (>= 2.0.2, < 5.0)
|
|
11
|
+
builder (3.2.4)
|
|
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
|
-
docile (1.3.
|
|
16
|
-
|
|
16
|
+
docile (1.3.5)
|
|
17
|
+
eventmachine (1.2.7)
|
|
18
|
+
faraday (1.3.0)
|
|
19
|
+
faraday-net_http (~> 1.0)
|
|
17
20
|
multipart-post (>= 1.2, < 3)
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
ruby2_keywords
|
|
22
|
+
faraday-net_http (1.0.1)
|
|
23
|
+
git (1.8.1)
|
|
24
|
+
rchardet (~> 1.8)
|
|
25
|
+
github_api (0.19.0)
|
|
20
26
|
addressable (~> 2.4)
|
|
21
27
|
descendants_tracker (~> 0.0.4)
|
|
22
|
-
faraday (
|
|
28
|
+
faraday (>= 0.8, < 2)
|
|
23
29
|
hashie (~> 3.5, >= 3.5.2)
|
|
24
30
|
oauth2 (~> 1.0)
|
|
25
|
-
google-protobuf (3.
|
|
26
|
-
googleapis-common-protos-types (1.0
|
|
27
|
-
google-protobuf (~> 3.
|
|
28
|
-
grpc (1.
|
|
29
|
-
google-protobuf (~> 3.
|
|
30
|
-
googleapis-common-protos-types (~> 1.0
|
|
31
|
-
grpc-tools (1.
|
|
31
|
+
google-protobuf (3.19.3)
|
|
32
|
+
googleapis-common-protos-types (1.3.0)
|
|
33
|
+
google-protobuf (~> 3.14)
|
|
34
|
+
grpc (1.43.1)
|
|
35
|
+
google-protobuf (~> 3.18)
|
|
36
|
+
googleapis-common-protos-types (~> 1.0)
|
|
37
|
+
grpc-tools (1.43.1)
|
|
32
38
|
hashie (3.6.0)
|
|
33
|
-
highline (2.0.
|
|
34
|
-
i18n (1.
|
|
39
|
+
highline (2.0.3)
|
|
40
|
+
i18n (1.8.9)
|
|
35
41
|
concurrent-ruby (~> 1.0)
|
|
36
42
|
json (1.8.6)
|
|
37
43
|
juwelier (2.4.9)
|
|
@@ -46,57 +52,67 @@ GEM
|
|
|
46
52
|
rake
|
|
47
53
|
rdoc
|
|
48
54
|
semver2
|
|
49
|
-
jwt (2.
|
|
55
|
+
jwt (2.2.2)
|
|
50
56
|
kamelcase (0.0.2)
|
|
51
57
|
semver2 (~> 3)
|
|
52
|
-
mini_portile2 (2.
|
|
53
|
-
minitest (5.
|
|
54
|
-
multi_json (1.
|
|
58
|
+
mini_portile2 (2.7.1)
|
|
59
|
+
minitest (5.14.4)
|
|
60
|
+
multi_json (1.15.0)
|
|
55
61
|
multi_xml (0.6.0)
|
|
56
|
-
multipart-post (2.
|
|
57
|
-
nokogiri (1.
|
|
58
|
-
mini_portile2 (~> 2.
|
|
59
|
-
|
|
60
|
-
|
|
62
|
+
multipart-post (2.1.1)
|
|
63
|
+
nokogiri (1.13.1)
|
|
64
|
+
mini_portile2 (~> 2.7.0)
|
|
65
|
+
racc (~> 1.4)
|
|
66
|
+
oauth2 (1.4.7)
|
|
67
|
+
faraday (>= 0.8, < 2.0)
|
|
61
68
|
jwt (>= 1.0, < 3.0)
|
|
62
69
|
multi_json (~> 1.3)
|
|
63
70
|
multi_xml (~> 0.5)
|
|
64
71
|
rack (>= 1.2, < 3)
|
|
65
|
-
psych (3.1
|
|
66
|
-
public_suffix (
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
psych (3.3.1)
|
|
73
|
+
public_suffix (4.0.6)
|
|
74
|
+
racc (1.6.0)
|
|
75
|
+
rack (2.2.3)
|
|
76
|
+
rake (13.0.3)
|
|
77
|
+
rchardet (1.8.0)
|
|
69
78
|
rdoc (3.12.2)
|
|
70
79
|
json (~> 1.4)
|
|
80
|
+
ruby2_keywords (0.0.4)
|
|
71
81
|
semver2 (3.4.2)
|
|
72
|
-
shoulda (
|
|
73
|
-
shoulda-context (~>
|
|
74
|
-
shoulda-matchers (
|
|
75
|
-
shoulda-context (
|
|
76
|
-
shoulda-matchers (
|
|
77
|
-
activesupport (>=
|
|
78
|
-
simplecov (0.
|
|
82
|
+
shoulda (4.0.0)
|
|
83
|
+
shoulda-context (~> 2.0)
|
|
84
|
+
shoulda-matchers (~> 4.0)
|
|
85
|
+
shoulda-context (2.0.0)
|
|
86
|
+
shoulda-matchers (4.5.1)
|
|
87
|
+
activesupport (>= 4.2.0)
|
|
88
|
+
simplecov (0.18.5)
|
|
79
89
|
docile (~> 1.1)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
90
|
+
simplecov-html (~> 0.11)
|
|
91
|
+
simplecov-html (0.12.3)
|
|
92
|
+
thin (1.8.1)
|
|
93
|
+
daemons (~> 1.0, >= 1.0.9)
|
|
94
|
+
eventmachine (~> 1.0, >= 1.0.4)
|
|
95
|
+
rack (>= 1, < 3)
|
|
83
96
|
thread_safe (0.3.6)
|
|
84
|
-
tzinfo (1.2.
|
|
97
|
+
tzinfo (1.2.9)
|
|
85
98
|
thread_safe (~> 0.1)
|
|
86
99
|
|
|
87
100
|
PLATFORMS
|
|
88
101
|
ruby
|
|
89
102
|
|
|
90
103
|
DEPENDENCIES
|
|
91
|
-
bundler
|
|
104
|
+
bundler
|
|
92
105
|
concurrent-ruby (~> 1.0, >= 1.0.5)
|
|
93
106
|
faraday
|
|
94
|
-
|
|
95
|
-
|
|
107
|
+
google-protobuf
|
|
108
|
+
googleapis-common-protos-types
|
|
109
|
+
grpc
|
|
110
|
+
grpc-tools
|
|
96
111
|
juwelier (~> 2.4.9)
|
|
97
112
|
rdoc (~> 3.12)
|
|
98
113
|
shoulda
|
|
99
114
|
simplecov
|
|
115
|
+
thin
|
|
100
116
|
|
|
101
117
|
BUNDLED WITH
|
|
102
|
-
|
|
118
|
+
2.3.5
|
data/README.md
CHANGED
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.7.0
|
data/compile_protos.sh
CHANGED
|
@@ -1,8 +1,26 @@
|
|
|
1
1
|
module Prefab
|
|
2
2
|
class CancellableInterceptor < GRPC::ClientInterceptor
|
|
3
|
+
WAIT_SEC = 3
|
|
4
|
+
|
|
5
|
+
def initialize(base_client)
|
|
6
|
+
@base_client = base_client
|
|
7
|
+
end
|
|
3
8
|
|
|
4
9
|
def cancel
|
|
5
10
|
@call.instance_variable_get("@wrapped").instance_variable_get("@call").cancel
|
|
11
|
+
i = 0
|
|
12
|
+
while (i < WAIT_SEC) do
|
|
13
|
+
if @call.instance_variable_get("@wrapped").cancelled?
|
|
14
|
+
@base_client.log_internal Logger::DEBUG, "Cancelled streaming."
|
|
15
|
+
return
|
|
16
|
+
else
|
|
17
|
+
@base_client.log_internal Logger::DEBUG, "Unable to cancel streaming. Trying again"
|
|
18
|
+
@call.instance_variable_get("@wrapped").instance_variable_get("@call").cancel
|
|
19
|
+
i += 1
|
|
20
|
+
sleep(1)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
@base_client.log_internal Logger::INFO, "Unable to cancel streaming."
|
|
6
24
|
end
|
|
7
25
|
|
|
8
26
|
def request_response(request:, call:, method:, metadata:, &block)
|
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
|
|
12
12
|
|
|
13
13
|
def initialize(api_key: ENV['PREFAB_API_KEY'],
|
|
14
14
|
logdev: nil,
|
|
@@ -19,13 +19,15 @@ 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" unless api_key.count("-") == 2
|
|
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 = {}
|
|
@@ -37,7 +39,8 @@ module Prefab
|
|
|
37
39
|
|
|
38
40
|
def channel
|
|
39
41
|
credentials = ENV["PREFAB_CLOUD_HTTP"] == "true" ? :this_channel_is_insecure : creds
|
|
40
|
-
url = ENV["PREFAB_API_URL"] || '
|
|
42
|
+
url = ENV["PREFAB_API_URL"] || 'grpc.prefab.cloud:443'
|
|
43
|
+
log_internal Logger::DEBUG, "GRPC Channel #{url} #{credentials}"
|
|
41
44
|
@_channel ||= GRPC::Core::Channel.new(url, nil, credentials)
|
|
42
45
|
end
|
|
43
46
|
|
|
@@ -88,7 +91,7 @@ module Prefab
|
|
|
88
91
|
end
|
|
89
92
|
|
|
90
93
|
def cache_key(post_fix)
|
|
91
|
-
"prefab:#{
|
|
94
|
+
"prefab:#{project_id}:#{post_fix}"
|
|
92
95
|
end
|
|
93
96
|
|
|
94
97
|
def reset!
|
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,26 +15,26 @@ 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
|
-
@cancellable_interceptor = Prefab::CancellableInterceptor.new
|
|
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
|
-
|
|
28
|
-
@cancellable_interceptor.cancel
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
+
@streaming = true
|
|
31
32
|
start_api_connection_thread(@config_loader.highwater_mark)
|
|
32
33
|
end
|
|
33
34
|
|
|
34
|
-
def get(
|
|
35
|
+
def get(key)
|
|
35
36
|
@initialization_lock.with_read_lock do
|
|
36
|
-
@config_resolver.get(
|
|
37
|
+
@config_resolver.get(key)
|
|
37
38
|
end
|
|
38
39
|
end
|
|
39
40
|
|
|
@@ -89,9 +90,12 @@ module Prefab
|
|
|
89
90
|
end
|
|
90
91
|
|
|
91
92
|
def load_checkpoint_from_config
|
|
92
|
-
|
|
93
|
+
@base_client.log_internal Logger::DEBUG, "Load Checkpoint From Config"
|
|
94
|
+
|
|
95
|
+
config_req = Prefab::ConfigServicePointer.new(project_id: @base_client.project_id,
|
|
93
96
|
start_at_id: @config_loader.highwater_mark)
|
|
94
97
|
resp = stub.get_all_config(config_req)
|
|
98
|
+
@base_client.log_internal Logger::DEBUG, "Got Response #{resp}"
|
|
95
99
|
load_deltas(resp, :api)
|
|
96
100
|
resp.deltas.each do |delta|
|
|
97
101
|
@config_loader.set(delta)
|
|
@@ -115,7 +119,6 @@ module Prefab
|
|
|
115
119
|
end
|
|
116
120
|
end
|
|
117
121
|
|
|
118
|
-
|
|
119
122
|
def load_deltas(deltas, source)
|
|
120
123
|
deltas.deltas.each do |delta|
|
|
121
124
|
@config_loader.set(delta)
|
|
@@ -156,12 +159,18 @@ module Prefab
|
|
|
156
159
|
# Setup a streaming connection to the API
|
|
157
160
|
# Save new config values into the loader
|
|
158
161
|
def start_api_connection_thread(start_at_id)
|
|
159
|
-
config_req = Prefab::ConfigServicePointer.new(
|
|
162
|
+
config_req = Prefab::ConfigServicePointer.new(project_id: @base_client.project_id,
|
|
160
163
|
start_at_id: start_at_id)
|
|
161
164
|
@base_client.log_internal Logger::DEBUG, "start api connection thread #{start_at_id}"
|
|
162
165
|
@base_client.stats.increment("prefab.config.api.start")
|
|
166
|
+
|
|
163
167
|
@api_connection_thread = Thread.new do
|
|
164
|
-
|
|
168
|
+
at_exit do
|
|
169
|
+
@streaming = false
|
|
170
|
+
@cancellable_interceptor.cancel
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
while @streaming do
|
|
165
174
|
begin
|
|
166
175
|
resp = stub.get_config(config_req)
|
|
167
176
|
resp.each do |r|
|
|
@@ -172,13 +181,16 @@ module Prefab
|
|
|
172
181
|
finish_init!(:streaming)
|
|
173
182
|
end
|
|
174
183
|
rescue => e
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
184
|
+
if @streaming
|
|
185
|
+
level = e.code == 1 ? Logger::DEBUG : Logger::INFO
|
|
186
|
+
@base_client.log_internal level, ("config client encountered #{e.message} pausing #{RECONNECT_WAIT}")
|
|
187
|
+
reset
|
|
188
|
+
sleep(RECONNECT_WAIT)
|
|
189
|
+
end
|
|
179
190
|
end
|
|
180
191
|
end
|
|
181
192
|
end
|
|
193
|
+
|
|
182
194
|
end
|
|
183
195
|
end
|
|
184
196
|
end
|
|
@@ -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,7 +14,7 @@ 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
|
|
@@ -26,7 +26,7 @@ module Prefab
|
|
|
26
26
|
return
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
if delta.
|
|
29
|
+
if delta.default.nil?
|
|
30
30
|
@api_config.delete(delta.key)
|
|
31
31
|
else
|
|
32
32
|
@api_config[delta.key] = delta
|
|
@@ -63,7 +63,7 @@ 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::ConfigValue.new(value_from(v))
|
|
66
|
+
rtn[k] = Prefab::ConfigDelta.new(key: k, default: Prefab::ConfigValue.new(value_from(v)))
|
|
67
67
|
end
|
|
68
68
|
end
|
|
69
69
|
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,7 +17,7 @@ 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
|
|
@@ -39,21 +40,6 @@ module Prefab
|
|
|
39
40
|
|
|
40
41
|
private
|
|
41
42
|
|
|
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
43
|
# Should client a.b.c see key in namespace a.b? yes
|
|
58
44
|
# Should client a.b.c see key in namespace a.b.c? yes
|
|
59
45
|
# Should client a.b.c see key in namespace a.b.d? no
|
|
@@ -61,32 +47,50 @@ module Prefab
|
|
|
61
47
|
#
|
|
62
48
|
def starts_with_ns?(key_namespace, client_namespace)
|
|
63
49
|
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
|
|
50
|
+
mapped = zipped.map do |k, c|
|
|
51
|
+
(k.nil? || k.empty?) || k == c
|
|
52
|
+
end
|
|
53
|
+
[mapped.all?, mapped.size]
|
|
67
54
|
end
|
|
68
55
|
|
|
69
56
|
def make_local
|
|
70
57
|
store = {}
|
|
71
|
-
@config_loader.calc_config.each do |
|
|
72
|
-
|
|
73
|
-
|
|
58
|
+
@config_loader.calc_config.each do |key, delta|
|
|
59
|
+
# start with the top level default
|
|
60
|
+
to_store = { match: "default", value: delta.default }
|
|
61
|
+
if delta.envs.any?
|
|
62
|
+
env_values = delta.envs.select { |e| e.environment == @environment }
|
|
74
63
|
|
|
75
|
-
|
|
64
|
+
# do we have and env_values that match our env?
|
|
65
|
+
if env_values.any?
|
|
66
|
+
env_value = env_values.first
|
|
76
67
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
key_namespace = split[0]
|
|
80
|
-
end
|
|
68
|
+
# override the top level default with env default
|
|
69
|
+
to_store = { match: "env_default", env: env_value.environment, value: env_value.default }
|
|
81
70
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
71
|
+
if env_value.namespace_values.any?
|
|
72
|
+
# check all namespace_values for match
|
|
73
|
+
env_value.namespace_values.each do |namespace_value|
|
|
74
|
+
(starts_with, count) = starts_with_ns?(namespace_value.namespace, @namespace)
|
|
75
|
+
if starts_with
|
|
76
|
+
# is this match the best match?
|
|
77
|
+
if count > (to_store[:match_depth_count] || 0)
|
|
78
|
+
to_store = { match: namespace_value.namespace, count: count, value: namespace_value.config_value }
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
88
83
|
end
|
|
89
84
|
end
|
|
85
|
+
|
|
86
|
+
# feature flags are a funny case
|
|
87
|
+
# we only define the variants in the default in order to be DRY
|
|
88
|
+
# but we want to access them in environments, clone them over
|
|
89
|
+
if to_store[:value].type == :feature_flag
|
|
90
|
+
to_store[:value].feature_flag.variants = delta.default.feature_flag.variants
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
store[key] = to_store
|
|
90
94
|
end
|
|
91
95
|
@lock.with_write_lock do
|
|
92
96
|
@local_store = store
|