prefab-cloud-ruby 0.23.3 → 0.23.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +0 -2
- data/Gemfile.lock +0 -6
- data/README.md +1 -3
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/lib/prefab/client.rb +5 -79
- data/lib/prefab/config_client.rb +7 -40
- data/lib/prefab/criteria_evaluator.rb +3 -1
- data/lib/prefab/http_connection.rb +37 -0
- data/lib/prefab/log_path_collector.rb +1 -1
- data/lib/prefab/options.rb +0 -3
- data/lib/prefab-cloud-ruby.rb +1 -4
- data/prefab-cloud-ruby.gemspec +5 -11
- data/test/integration_test.rb +1 -2
- data/test/test_client.rb +0 -7
- data/test/test_criteria_evaluator.rb +37 -0
- data/test/test_log_path_collector.rb +21 -15
- metadata +4 -34
- data/lib/prefab/auth_interceptor.rb +0 -34
- data/lib/prefab/cancellable_interceptor.rb +0 -49
- data/lib/prefab/ratelimit_client.rb +0 -78
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 24a51f70838a78fdb76b79565fa4c8a28983d95cba2faa486e6bfdd71e095a98
|
4
|
+
data.tar.gz: b559a16d49be776460fe65df55791ff1aa95a2d2a7b655698f9cc434306514ba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a9365864e53a40214bce51cd9ce0ddc887def06a02a689263d62328964b8344911021363468ec0b5e1bb02568fc07f66ba8f9247cadf8b8215ad1fc628323db9
|
7
|
+
data.tar.gz: 68e93df7c02776894411605d3fbc48d16828bd94d0ae6a7034b713d601916a496a0218b5f76f29f9110661cd2d98a7dc55a3e0118890456e75d80004178b0323
|
data/Gemfile
CHANGED
@@ -4,14 +4,12 @@ gem 'concurrent-ruby', '~> 1.0', '>= 1.0.5'
|
|
4
4
|
gem 'faraday'
|
5
5
|
gem 'googleapis-common-protos-types', platforms: :ruby
|
6
6
|
gem 'google-protobuf', platforms: :ruby
|
7
|
-
gem 'grpc', platforms: :ruby
|
8
7
|
gem 'ld-eventsource'
|
9
8
|
gem 'uuid'
|
10
9
|
|
11
10
|
group :development do
|
12
11
|
gem 'benchmark-ips'
|
13
12
|
gem 'bundler'
|
14
|
-
gem 'grpc-tools', platforms: :ruby
|
15
13
|
gem 'juwelier', '~> 2.4.9'
|
16
14
|
gem 'rdoc'
|
17
15
|
gem 'simplecov', '>= 0'
|
data/Gemfile.lock
CHANGED
@@ -33,10 +33,6 @@ GEM
|
|
33
33
|
google-protobuf (3.22.2)
|
34
34
|
googleapis-common-protos-types (1.5.0)
|
35
35
|
google-protobuf (~> 3.14)
|
36
|
-
grpc (1.53.0)
|
37
|
-
google-protobuf (~> 3.21)
|
38
|
-
googleapis-common-protos-types (~> 1.0)
|
39
|
-
grpc-tools (1.43.1)
|
40
36
|
hashie (3.6.0)
|
41
37
|
highline (2.0.3)
|
42
38
|
http (5.0.1)
|
@@ -124,8 +120,6 @@ DEPENDENCIES
|
|
124
120
|
faraday
|
125
121
|
google-protobuf
|
126
122
|
googleapis-common-protos-types
|
127
|
-
grpc
|
128
|
-
grpc-tools
|
129
123
|
juwelier (~> 2.4.9)
|
130
124
|
ld-eventsource
|
131
125
|
minitest
|
data/README.md
CHANGED
@@ -28,9 +28,7 @@ See full documentation https://docs.prefab.cloud/docs/ruby-sdk/ruby
|
|
28
28
|
|
29
29
|
## Important note about Forking and realtime updates
|
30
30
|
|
31
|
-
Many ruby web servers fork.
|
32
|
-
|
33
|
-
If you're using Puma or Unicorn, you can do the following.
|
31
|
+
Many ruby web servers fork. When the process is forked, the current realtime update stream is disconnected. If you're using Puma or Unicorn, do the following.
|
34
32
|
|
35
33
|
```ruby
|
36
34
|
#config/initializers/prefab.rb
|
data/Rakefile
CHANGED
@@ -17,7 +17,7 @@ Juwelier::Tasks.new do |gem|
|
|
17
17
|
gem.homepage = 'http://github.com/prefab-cloud/prefab-cloud-ruby'
|
18
18
|
gem.license = 'MIT'
|
19
19
|
gem.summary = %(Prefab Ruby Infrastructure)
|
20
|
-
gem.description = %(
|
20
|
+
gem.description = %(Feature Flags, Live Config, and Dynamic Log Levels as a service)
|
21
21
|
gem.email = 'jdwyer@prefab.cloud'
|
22
22
|
gem.authors = ['Jeff Dwyer']
|
23
23
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.23.
|
1
|
+
0.23.5
|
data/lib/prefab/client.rb
CHANGED
@@ -31,14 +31,8 @@ module Prefab
|
|
31
31
|
@api_key = @options.api_key
|
32
32
|
raise Prefab::Errors::InvalidApiKeyError, @api_key if @api_key.nil? || @api_key.empty? || api_key.count('-') < 1
|
33
33
|
|
34
|
-
@interceptor = Prefab::AuthInterceptor.new(@api_key)
|
35
34
|
@prefab_api_url = @options.prefab_api_url
|
36
|
-
|
37
|
-
log_internal ::Logger::INFO,
|
38
|
-
"Prefab Connecting to: #{@prefab_api_url} and #{@prefab_grpc_url} Secure: #{http_secure?}"
|
39
|
-
at_exit do
|
40
|
-
channel.destroy
|
41
|
-
end
|
35
|
+
log_internal ::Logger::INFO, "Prefab Connecting to: #{@prefab_api_url}"
|
42
36
|
end
|
43
37
|
# start config client
|
44
38
|
config_client
|
@@ -54,19 +48,10 @@ module Prefab
|
|
54
48
|
Thread.current[:prefab_log_properties] = {}
|
55
49
|
end
|
56
50
|
|
57
|
-
def channel
|
58
|
-
credentials = http_secure? ? creds : :this_channel_is_insecure
|
59
|
-
@_channel ||= GRPC::Core::Channel.new(@prefab_grpc_url, nil, credentials)
|
60
|
-
end
|
61
|
-
|
62
51
|
def config_client(timeout: 5.0)
|
63
52
|
@config_client ||= Prefab::ConfigClient.new(self, timeout)
|
64
53
|
end
|
65
54
|
|
66
|
-
def ratelimit_client(timeout: 5.0)
|
67
|
-
@ratelimit_client ||= Prefab::RateLimitClient.new(self, timeout)
|
68
|
-
end
|
69
|
-
|
70
55
|
def feature_flag_client
|
71
56
|
@feature_flag_client ||= Prefab::FeatureFlagClient.new(self)
|
72
57
|
end
|
@@ -97,39 +82,6 @@ module Prefab
|
|
97
82
|
log.log_internal msg, path, nil, level
|
98
83
|
end
|
99
84
|
|
100
|
-
def request(service, method, req_options: {}, params: {})
|
101
|
-
# Future-proofing since we previously bumped into a conflict with a service with a `send` method
|
102
|
-
raise ArgumentError, 'Cannot call public_send on an grpc service in Ruby' if method.to_s == 'public_send'
|
103
|
-
|
104
|
-
opts = { timeout: 10 }.merge(req_options)
|
105
|
-
|
106
|
-
attempts = 0
|
107
|
-
start_time = Time.now
|
108
|
-
|
109
|
-
begin
|
110
|
-
attempts += 1
|
111
|
-
|
112
|
-
stub_for(service, opts[:timeout]).public_send(method, *params)
|
113
|
-
rescue StandardError => e
|
114
|
-
log_internal ::Logger::WARN, e
|
115
|
-
|
116
|
-
raise e if Time.now - start_time > opts[:timeout]
|
117
|
-
|
118
|
-
sleep_seconds = [BASE_SLEEP_SEC * (2**(attempts - 1)), MAX_SLEEP_SEC].min
|
119
|
-
sleep_seconds *= (0.5 * (1 + rand))
|
120
|
-
sleep_seconds = [BASE_SLEEP_SEC, sleep_seconds].max
|
121
|
-
log_internal ::Logger::INFO, "Sleep #{sleep_seconds} and Reset #{service} #{method}"
|
122
|
-
sleep sleep_seconds
|
123
|
-
reset!
|
124
|
-
retry
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
def reset!
|
129
|
-
@stubs.clear
|
130
|
-
@_channel = nil
|
131
|
-
end
|
132
|
-
|
133
85
|
def enabled?(feature_name, lookup_key = nil, attributes = {})
|
134
86
|
feature_flag_client.feature_is_on_for?(feature_name, lookup_key, attributes: attributes)
|
135
87
|
end
|
@@ -142,6 +94,10 @@ module Prefab
|
|
142
94
|
end
|
143
95
|
end
|
144
96
|
|
97
|
+
def post(path, body)
|
98
|
+
Prefab::HttpConnection.new(@options.prefab_api_url, @api_key).post(path, body)
|
99
|
+
end
|
100
|
+
|
145
101
|
private
|
146
102
|
|
147
103
|
def is_ff?(key)
|
@@ -149,35 +105,5 @@ module Prefab
|
|
149
105
|
|
150
106
|
raw && raw.allowable_values.any?
|
151
107
|
end
|
152
|
-
|
153
|
-
def http_secure?
|
154
|
-
ENV['PREFAB_CLOUD_HTTP'] != 'true'
|
155
|
-
end
|
156
|
-
|
157
|
-
def stub_for(service, timeout)
|
158
|
-
@stubs["#{service}_#{timeout}"] ||= service::Stub.new(nil,
|
159
|
-
nil,
|
160
|
-
timeout: timeout,
|
161
|
-
channel_override: channel,
|
162
|
-
interceptors: [@interceptor])
|
163
|
-
end
|
164
|
-
|
165
|
-
def creds
|
166
|
-
GRPC::Core::ChannelCredentials.new(ssl_certs)
|
167
|
-
end
|
168
|
-
|
169
|
-
def ssl_certs
|
170
|
-
ssl_certs = ''
|
171
|
-
Dir["#{OpenSSL::X509::DEFAULT_CERT_DIR}/*.pem"].each do |cert|
|
172
|
-
ssl_certs += File.open(cert).read
|
173
|
-
end
|
174
|
-
if OpenSSL::X509::DEFAULT_CERT_FILE && File.exist?(OpenSSL::X509::DEFAULT_CERT_FILE)
|
175
|
-
ssl_certs += File.open(OpenSSL::X509::DEFAULT_CERT_FILE).read
|
176
|
-
end
|
177
|
-
ssl_certs
|
178
|
-
rescue StandardError => e
|
179
|
-
log.warn("Issue loading SSL certs #{e.message}")
|
180
|
-
ssl_certs
|
181
|
-
end
|
182
108
|
end
|
183
109
|
end
|
data/lib/prefab/config_client.rb
CHANGED
@@ -26,8 +26,6 @@ module Prefab
|
|
26
26
|
@base_client.log_internal ::Logger::DEBUG, 'Initialize ConfigClient: AcquiredWriteLock'
|
27
27
|
@initialized_future = Concurrent::Future.execute { @initialization_lock.acquire_read_lock }
|
28
28
|
|
29
|
-
@cancellable_interceptor = Prefab::CancellableInterceptor.new(@base_client)
|
30
|
-
|
31
29
|
if @options.local_only?
|
32
30
|
finish_init!(:local_only)
|
33
31
|
else
|
@@ -58,11 +56,6 @@ module Prefab
|
|
58
56
|
@config_resolver.update
|
59
57
|
end
|
60
58
|
|
61
|
-
def reset
|
62
|
-
@base_client.reset!
|
63
|
-
@_stub = nil
|
64
|
-
end
|
65
|
-
|
66
59
|
def to_s
|
67
60
|
@config_resolver.to_s
|
68
61
|
end
|
@@ -108,54 +101,28 @@ module Prefab
|
|
108
101
|
@config_resolver.get(key, lookup_key, properties)
|
109
102
|
end
|
110
103
|
|
111
|
-
def stub
|
112
|
-
@_stub = Prefab::ConfigService::Stub.new(nil,
|
113
|
-
nil,
|
114
|
-
channel_override: @base_client.channel,
|
115
|
-
interceptors: [@base_client.interceptor, @cancellable_interceptor])
|
116
|
-
end
|
117
|
-
|
118
104
|
def load_checkpoint
|
119
105
|
success = load_checkpoint_api_cdn
|
120
106
|
|
121
107
|
return if success
|
122
108
|
|
123
|
-
|
124
|
-
|
125
|
-
success = load_checkpoint_from_grpc_api
|
109
|
+
success = load_checkpoint_api
|
126
110
|
|
127
111
|
return if success
|
128
112
|
|
129
113
|
@base_client.log_internal ::Logger::WARN, 'No success loading checkpoints'
|
130
114
|
end
|
131
115
|
|
132
|
-
def load_checkpoint_from_grpc_api
|
133
|
-
config_req = Prefab::ConfigServicePointer.new(start_at_id: @config_loader.highwater_mark)
|
134
|
-
|
135
|
-
resp = stub.get_all_config(config_req)
|
136
|
-
load_configs(resp, :remote_api_grpc)
|
137
|
-
true
|
138
|
-
rescue GRPC::Unauthenticated
|
139
|
-
@base_client.log_internal ::Logger::WARN, 'Unauthenticated'
|
140
|
-
rescue StandardError => e
|
141
|
-
@base_client.log_internal ::Logger::WARN, "Unexpected grpc_api problem loading checkpoint #{e}"
|
142
|
-
false
|
143
|
-
end
|
144
|
-
|
145
116
|
def load_checkpoint_api_cdn
|
146
|
-
|
147
|
-
conn = if Faraday::VERSION[0].to_i >= 2
|
148
|
-
Faraday.new(url) do |conn|
|
149
|
-
conn.request :authorization, :basic, AUTH_USER, @base_client.api_key
|
150
|
-
end
|
151
|
-
else
|
152
|
-
Faraday.new(url) do |conn|
|
153
|
-
conn.request :basic_auth, AUTH_USER, @base_client.api_key
|
154
|
-
end
|
155
|
-
end
|
117
|
+
conn = Prefab::HttpConnection.new("#{@options.url_for_api_cdn}/api/v1/configs/0", @base_client.api_key)
|
156
118
|
load_url(conn, :remote_cdn_api)
|
157
119
|
end
|
158
120
|
|
121
|
+
def load_checkpoint_api
|
122
|
+
conn = Prefab::HttpConnection.new("#{@options.prefab_api_url}/api/v1/configs/0", @base_client.api_key)
|
123
|
+
load_url(conn, :remote_api)
|
124
|
+
end
|
125
|
+
|
159
126
|
def load_url(conn, source)
|
160
127
|
resp = conn.get('')
|
161
128
|
if resp.status == 200
|
@@ -87,7 +87,9 @@ module Prefab
|
|
87
87
|
|
88
88
|
case criterion_value_or_values
|
89
89
|
when Google::Protobuf::RepeatedField
|
90
|
-
|
90
|
+
# we to_s the value from properties for comparison because the
|
91
|
+
# criterion_value_or_values is a list of strings
|
92
|
+
criterion_value_or_values.include?(value_from_properties.to_s)
|
91
93
|
else
|
92
94
|
criterion_value_or_values == value_from_properties
|
93
95
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Prefab
|
4
|
+
class HttpConnection
|
5
|
+
AUTH_USER = 'authuser'
|
6
|
+
PROTO_HEADERS = { 'Content-Type' => 'application/x-protobuf', 'Accept' => 'application/x-protobuf' }.freeze
|
7
|
+
|
8
|
+
def initialize(api_root, api_key)
|
9
|
+
@api_root = api_root
|
10
|
+
@api_key = api_key
|
11
|
+
end
|
12
|
+
|
13
|
+
def get(path)
|
14
|
+
connection.get(path)
|
15
|
+
end
|
16
|
+
|
17
|
+
def post(path, body)
|
18
|
+
connection(PROTO_HEADERS).post(path, body.to_proto)
|
19
|
+
end
|
20
|
+
|
21
|
+
def connection(headers = {})
|
22
|
+
if Faraday::VERSION[0].to_i >= 2
|
23
|
+
Faraday.new(@api_root) do |conn|
|
24
|
+
conn.request :authorization, :basic, AUTH_USER, @api_key
|
25
|
+
|
26
|
+
conn.headers.merge!(headers)
|
27
|
+
end
|
28
|
+
else
|
29
|
+
Faraday.new(@api_root) do |conn|
|
30
|
+
conn.request :basic_auth, AUTH_USER, @api_key
|
31
|
+
|
32
|
+
conn.headers.merge!(headers)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/prefab/options.rb
CHANGED
@@ -10,7 +10,6 @@ module Prefab
|
|
10
10
|
attr_reader :shared_cache
|
11
11
|
attr_reader :namespace
|
12
12
|
attr_reader :prefab_api_url
|
13
|
-
attr_reader :prefab_grpc_url
|
14
13
|
attr_reader :on_no_default
|
15
14
|
attr_reader :initialization_timeout_sec
|
16
15
|
attr_reader :on_init_failure
|
@@ -49,7 +48,6 @@ module Prefab
|
|
49
48
|
log_formatter: DEFAULT_LOG_FORMATTER,
|
50
49
|
log_prefix: nil,
|
51
50
|
prefab_api_url: ENV['PREFAB_API_URL'] || 'https://api.prefab.cloud',
|
52
|
-
prefab_grpc_url: ENV['PREFAB_GRPC_URL'] || 'grpc.prefab.cloud:443',
|
53
51
|
on_no_default: ON_NO_DEFAULT::RAISE, # options :raise, :warn_and_return_nil,
|
54
52
|
initialization_timeout_sec: 10, # how long to wait before on_init_failure
|
55
53
|
on_init_failure: ON_INITIALIZATION_FAILURE::RAISE, # options :unlock_and_continue, :lock_and_keep_trying, :raise
|
@@ -71,7 +69,6 @@ module Prefab
|
|
71
69
|
@log_formatter = log_formatter
|
72
70
|
@log_prefix = log_prefix
|
73
71
|
@prefab_api_url = remove_trailing_slash(prefab_api_url)
|
74
|
-
@prefab_grpc_url = prefab_grpc_url
|
75
72
|
@on_no_default = on_no_default
|
76
73
|
@initialization_timeout_sec = initialization_timeout_sec
|
77
74
|
@on_init_failure = on_init_failure
|
data/lib/prefab-cloud-ruby.rb
CHANGED
@@ -12,7 +12,6 @@ require 'prefab/exponential_backoff'
|
|
12
12
|
require 'prefab/errors/initialization_timeout_error'
|
13
13
|
require 'prefab/errors/invalid_api_key_error'
|
14
14
|
require 'prefab/errors/missing_default_error'
|
15
|
-
require 'prefab_services_pb'
|
16
15
|
require 'prefab/options'
|
17
16
|
require 'prefab/internal_logger'
|
18
17
|
require 'prefab/sse_logger'
|
@@ -23,13 +22,11 @@ require 'prefab/config_loader'
|
|
23
22
|
require 'prefab/local_config_parser'
|
24
23
|
require 'prefab/yaml_config_parser'
|
25
24
|
require 'prefab/config_resolver'
|
25
|
+
require 'prefab/http_connection'
|
26
26
|
require 'prefab/client'
|
27
|
-
require 'prefab/ratelimit_client'
|
28
27
|
require 'prefab/config_client'
|
29
28
|
require 'prefab/feature_flag_client'
|
30
29
|
require 'prefab/logger_client'
|
31
|
-
require 'prefab/auth_interceptor'
|
32
|
-
require 'prefab/cancellable_interceptor'
|
33
30
|
require 'prefab/noop_cache'
|
34
31
|
require 'prefab/noop_stats'
|
35
32
|
require 'prefab/murmer3'
|
data/prefab-cloud-ruby.gemspec
CHANGED
@@ -2,17 +2,17 @@
|
|
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 0.23.
|
5
|
+
# stub: prefab-cloud-ruby 0.23.5 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "prefab-cloud-ruby".freeze
|
9
|
-
s.version = "0.23.
|
9
|
+
s.version = "0.23.5"
|
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-04-
|
15
|
-
s.description = "
|
14
|
+
s.date = "2023-04-13"
|
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.extra_rdoc_files = [
|
18
18
|
"LICENSE.txt",
|
@@ -33,8 +33,6 @@ Gem::Specification.new do |s|
|
|
33
33
|
"VERSION",
|
34
34
|
"compile_protos.sh",
|
35
35
|
"lib/prefab-cloud-ruby.rb",
|
36
|
-
"lib/prefab/auth_interceptor.rb",
|
37
|
-
"lib/prefab/cancellable_interceptor.rb",
|
38
36
|
"lib/prefab/client.rb",
|
39
37
|
"lib/prefab/config_client.rb",
|
40
38
|
"lib/prefab/config_loader.rb",
|
@@ -47,6 +45,7 @@ Gem::Specification.new do |s|
|
|
47
45
|
"lib/prefab/errors/missing_default_error.rb",
|
48
46
|
"lib/prefab/exponential_backoff.rb",
|
49
47
|
"lib/prefab/feature_flag_client.rb",
|
48
|
+
"lib/prefab/http_connection.rb",
|
50
49
|
"lib/prefab/internal_logger.rb",
|
51
50
|
"lib/prefab/local_config_parser.rb",
|
52
51
|
"lib/prefab/log_path_collector.rb",
|
@@ -55,7 +54,6 @@ Gem::Specification.new do |s|
|
|
55
54
|
"lib/prefab/noop_cache.rb",
|
56
55
|
"lib/prefab/noop_stats.rb",
|
57
56
|
"lib/prefab/options.rb",
|
58
|
-
"lib/prefab/ratelimit_client.rb",
|
59
57
|
"lib/prefab/sse_logger.rb",
|
60
58
|
"lib/prefab/weighted_value_resolver.rb",
|
61
59
|
"lib/prefab/yaml_config_parser.rb",
|
@@ -96,12 +94,10 @@ Gem::Specification.new do |s|
|
|
96
94
|
s.add_runtime_dependency(%q<faraday>.freeze, [">= 0"])
|
97
95
|
s.add_runtime_dependency(%q<googleapis-common-protos-types>.freeze, [">= 0"])
|
98
96
|
s.add_runtime_dependency(%q<google-protobuf>.freeze, [">= 0"])
|
99
|
-
s.add_runtime_dependency(%q<grpc>.freeze, [">= 0"])
|
100
97
|
s.add_runtime_dependency(%q<ld-eventsource>.freeze, [">= 0"])
|
101
98
|
s.add_runtime_dependency(%q<uuid>.freeze, [">= 0"])
|
102
99
|
s.add_development_dependency(%q<benchmark-ips>.freeze, [">= 0"])
|
103
100
|
s.add_development_dependency(%q<bundler>.freeze, [">= 0"])
|
104
|
-
s.add_development_dependency(%q<grpc-tools>.freeze, [">= 0"])
|
105
101
|
s.add_development_dependency(%q<juwelier>.freeze, ["~> 2.4.9"])
|
106
102
|
s.add_development_dependency(%q<rdoc>.freeze, [">= 0"])
|
107
103
|
s.add_development_dependency(%q<simplecov>.freeze, [">= 0"])
|
@@ -110,12 +106,10 @@ Gem::Specification.new do |s|
|
|
110
106
|
s.add_dependency(%q<faraday>.freeze, [">= 0"])
|
111
107
|
s.add_dependency(%q<googleapis-common-protos-types>.freeze, [">= 0"])
|
112
108
|
s.add_dependency(%q<google-protobuf>.freeze, [">= 0"])
|
113
|
-
s.add_dependency(%q<grpc>.freeze, [">= 0"])
|
114
109
|
s.add_dependency(%q<ld-eventsource>.freeze, [">= 0"])
|
115
110
|
s.add_dependency(%q<uuid>.freeze, [">= 0"])
|
116
111
|
s.add_dependency(%q<benchmark-ips>.freeze, [">= 0"])
|
117
112
|
s.add_dependency(%q<bundler>.freeze, [">= 0"])
|
118
|
-
s.add_dependency(%q<grpc-tools>.freeze, [">= 0"])
|
119
113
|
s.add_dependency(%q<juwelier>.freeze, ["~> 2.4.9"])
|
120
114
|
s.add_dependency(%q<rdoc>.freeze, [">= 0"])
|
121
115
|
s.add_dependency(%q<simplecov>.freeze, [">= 0"])
|
data/test/integration_test.rb
CHANGED
@@ -91,8 +91,7 @@ class IntegrationTest
|
|
91
91
|
prefab_envs: ['unit_tests'],
|
92
92
|
prefab_datasources: Prefab::Options::DATASOURCES::ALL,
|
93
93
|
api_key: ENV['PREFAB_INTEGRATION_TEST_API_KEY'],
|
94
|
-
prefab_api_url: 'https://api.staging-prefab.cloud'
|
95
|
-
prefab_grpc_url: 'grpc.staging-prefab.cloud:443'
|
94
|
+
prefab_api_url: 'https://api.staging-prefab.cloud'
|
96
95
|
}.merge(@client_overrides))
|
97
96
|
end
|
98
97
|
end
|
data/test/test_client.rb
CHANGED
@@ -77,13 +77,6 @@ class TestClient < Minitest::Test
|
|
77
77
|
assert_equal 'all-features', @client.get('flag_with_a_value')
|
78
78
|
end
|
79
79
|
|
80
|
-
def test_ssl_certs
|
81
|
-
certs = @client.send(:ssl_certs).split('-----BEGIN CERTIFICATE-----')
|
82
|
-
|
83
|
-
# This is a smoke test to make sure multiple certs are loaded
|
84
|
-
assert certs.length > 1
|
85
|
-
end
|
86
|
-
|
87
80
|
def test_initialization_with_an_options_object
|
88
81
|
options_hash = {
|
89
82
|
namespace: 'test-namespace',
|
@@ -505,6 +505,43 @@ class TestCriteriaEvaluator < Minitest::Test
|
|
505
505
|
assert_equal DEFAULT_VALUE, evaluator.evaluate({ email: 'example@gmail.com', admin: true, deleted: true }).string
|
506
506
|
end
|
507
507
|
|
508
|
+
def test_stringifying_property_values_and_names
|
509
|
+
config = Prefab::Config.new(
|
510
|
+
key: KEY,
|
511
|
+
rows: [
|
512
|
+
DEFAULT_ROW,
|
513
|
+
Prefab::ConfigRow.new(
|
514
|
+
project_env_id: PROJECT_ENV_ID,
|
515
|
+
values: [
|
516
|
+
Prefab::ConditionalValue.new(
|
517
|
+
criteria: [
|
518
|
+
Prefab::Criterion.new(
|
519
|
+
operator: Prefab::Criterion::CriterionOperator::PROP_IS_ONE_OF,
|
520
|
+
value_to_match: string_list(%w[1 true hello]),
|
521
|
+
property_name: 'team_name'
|
522
|
+
)
|
523
|
+
],
|
524
|
+
value: Prefab::ConfigValue.new(string: DESIRED_VALUE)
|
525
|
+
)
|
526
|
+
]
|
527
|
+
)
|
528
|
+
]
|
529
|
+
)
|
530
|
+
|
531
|
+
evaluator = Prefab::CriteriaEvaluator.new(config, project_env_id: PROJECT_ENV_ID, resolver: nil,
|
532
|
+
base_client: nil)
|
533
|
+
|
534
|
+
assert_equal DEFAULT_VALUE, evaluator.evaluate({}).string
|
535
|
+
assert_equal DEFAULT_VALUE, evaluator.evaluate({ team_name: 'prefab.cloud' }).string
|
536
|
+
|
537
|
+
[1, true, :hello].each do |value|
|
538
|
+
[:team_name, 'team_name'].each do |property_name|
|
539
|
+
assert_equal DESIRED_VALUE, evaluator.evaluate({ property_name => value }).string
|
540
|
+
assert_equal DESIRED_VALUE, evaluator.evaluate({ property_name => value.to_s }).string
|
541
|
+
end
|
542
|
+
end
|
543
|
+
end
|
544
|
+
|
508
545
|
private
|
509
546
|
|
510
547
|
def string_list(values)
|
@@ -4,6 +4,9 @@ require 'test_helper'
|
|
4
4
|
require 'timecop'
|
5
5
|
|
6
6
|
class TestLogPathCollector < Minitest::Test
|
7
|
+
MAX_WAIT = 2
|
8
|
+
SLEEP_TIME = 0.01
|
9
|
+
|
7
10
|
def test_sync
|
8
11
|
Timecop.freeze do
|
9
12
|
client = new_client(namespace: 'this.is.a.namespace')
|
@@ -13,29 +16,32 @@ class TestLogPathCollector < Minitest::Test
|
|
13
16
|
|
14
17
|
requests = []
|
15
18
|
|
16
|
-
client.define_singleton_method(:
|
19
|
+
client.define_singleton_method(:post) do |*params|
|
17
20
|
requests.push(params)
|
18
21
|
end
|
19
22
|
|
20
23
|
client.log_path_collector.send(:sync)
|
21
24
|
|
22
25
|
# let the flush thread run
|
23
|
-
|
26
|
+
|
27
|
+
wait_time = 0
|
28
|
+
while requests.length == 0
|
29
|
+
wait_time += SLEEP_TIME
|
30
|
+
sleep SLEEP_TIME
|
31
|
+
|
32
|
+
raise "Waited #{MAX_WAIT} seconds for the flush thread to run, but it never did" if wait_time > MAX_WAIT
|
33
|
+
end
|
24
34
|
|
25
35
|
assert_equal requests, [[
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
instance_hash: client.instance_hash,
|
36
|
-
namespace: 'this.is.a.namespace'
|
37
|
-
)
|
38
|
-
}
|
36
|
+
'/api/v1/known-loggers',
|
37
|
+
Prefab::Loggers.new(
|
38
|
+
loggers: [Prefab::Logger.new(logger_name: 'test.test_log_path_collector.test_sync',
|
39
|
+
infos: 2, errors: 3)],
|
40
|
+
start_at: (Time.now.utc.to_f * 1000).to_i,
|
41
|
+
end_at: (Time.now.utc.to_f * 1000).to_i,
|
42
|
+
instance_hash: client.instance_hash,
|
43
|
+
namespace: 'this.is.a.namespace'
|
44
|
+
)
|
39
45
|
]]
|
40
46
|
end
|
41
47
|
end
|
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: 0.23.
|
4
|
+
version: 0.23.5
|
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-04-
|
11
|
+
date: 2023-04-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -72,20 +72,6 @@ dependencies:
|
|
72
72
|
- - ">="
|
73
73
|
- !ruby/object:Gem::Version
|
74
74
|
version: '0'
|
75
|
-
- !ruby/object:Gem::Dependency
|
76
|
-
name: grpc
|
77
|
-
requirement: !ruby/object:Gem::Requirement
|
78
|
-
requirements:
|
79
|
-
- - ">="
|
80
|
-
- !ruby/object:Gem::Version
|
81
|
-
version: '0'
|
82
|
-
type: :runtime
|
83
|
-
prerelease: false
|
84
|
-
version_requirements: !ruby/object:Gem::Requirement
|
85
|
-
requirements:
|
86
|
-
- - ">="
|
87
|
-
- !ruby/object:Gem::Version
|
88
|
-
version: '0'
|
89
75
|
- !ruby/object:Gem::Dependency
|
90
76
|
name: ld-eventsource
|
91
77
|
requirement: !ruby/object:Gem::Requirement
|
@@ -142,20 +128,6 @@ dependencies:
|
|
142
128
|
- - ">="
|
143
129
|
- !ruby/object:Gem::Version
|
144
130
|
version: '0'
|
145
|
-
- !ruby/object:Gem::Dependency
|
146
|
-
name: grpc-tools
|
147
|
-
requirement: !ruby/object:Gem::Requirement
|
148
|
-
requirements:
|
149
|
-
- - ">="
|
150
|
-
- !ruby/object:Gem::Version
|
151
|
-
version: '0'
|
152
|
-
type: :development
|
153
|
-
prerelease: false
|
154
|
-
version_requirements: !ruby/object:Gem::Requirement
|
155
|
-
requirements:
|
156
|
-
- - ">="
|
157
|
-
- !ruby/object:Gem::Version
|
158
|
-
version: '0'
|
159
131
|
- !ruby/object:Gem::Dependency
|
160
132
|
name: juwelier
|
161
133
|
requirement: !ruby/object:Gem::Requirement
|
@@ -198,7 +170,7 @@ dependencies:
|
|
198
170
|
- - ">="
|
199
171
|
- !ruby/object:Gem::Version
|
200
172
|
version: '0'
|
201
|
-
description:
|
173
|
+
description: Feature Flags, Live Config, and Dynamic Log Levels as a service
|
202
174
|
email: jdwyer@prefab.cloud
|
203
175
|
executables: []
|
204
176
|
extensions: []
|
@@ -220,8 +192,6 @@ files:
|
|
220
192
|
- VERSION
|
221
193
|
- compile_protos.sh
|
222
194
|
- lib/prefab-cloud-ruby.rb
|
223
|
-
- lib/prefab/auth_interceptor.rb
|
224
|
-
- lib/prefab/cancellable_interceptor.rb
|
225
195
|
- lib/prefab/client.rb
|
226
196
|
- lib/prefab/config_client.rb
|
227
197
|
- lib/prefab/config_loader.rb
|
@@ -234,6 +204,7 @@ files:
|
|
234
204
|
- lib/prefab/errors/missing_default_error.rb
|
235
205
|
- lib/prefab/exponential_backoff.rb
|
236
206
|
- lib/prefab/feature_flag_client.rb
|
207
|
+
- lib/prefab/http_connection.rb
|
237
208
|
- lib/prefab/internal_logger.rb
|
238
209
|
- lib/prefab/local_config_parser.rb
|
239
210
|
- lib/prefab/log_path_collector.rb
|
@@ -242,7 +213,6 @@ files:
|
|
242
213
|
- lib/prefab/noop_cache.rb
|
243
214
|
- lib/prefab/noop_stats.rb
|
244
215
|
- lib/prefab/options.rb
|
245
|
-
- lib/prefab/ratelimit_client.rb
|
246
216
|
- lib/prefab/sse_logger.rb
|
247
217
|
- lib/prefab/weighted_value_resolver.rb
|
248
218
|
- lib/prefab/yaml_config_parser.rb
|
@@ -1,34 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Prefab
|
4
|
-
class AuthInterceptor < GRPC::ClientInterceptor
|
5
|
-
VERSION = File.exist?('VERSION') ? File.read('VERSION').chomp : ''
|
6
|
-
CLIENT = "prefab-cloud-ruby.#{VERSION}".freeze
|
7
|
-
|
8
|
-
def initialize(api_key)
|
9
|
-
@api_key = api_key
|
10
|
-
end
|
11
|
-
|
12
|
-
def request_response(request:, call:, method:, metadata:, &block)
|
13
|
-
shared(metadata, &block)
|
14
|
-
end
|
15
|
-
|
16
|
-
def client_streamer(requests:, call:, method:, metadata:, &block)
|
17
|
-
shared(metadata, &block)
|
18
|
-
end
|
19
|
-
|
20
|
-
def server_streamer(request:, call:, method:, metadata:, &block)
|
21
|
-
shared(metadata, &block)
|
22
|
-
end
|
23
|
-
|
24
|
-
def bidi_streamer(requests:, call:, method:, metadata:, &block)
|
25
|
-
shared(metadata, &block)
|
26
|
-
end
|
27
|
-
|
28
|
-
def shared(metadata)
|
29
|
-
metadata['auth'] = @api_key
|
30
|
-
metadata['client'] = CLIENT
|
31
|
-
yield
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
@@ -1,49 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Prefab
|
4
|
-
class CancellableInterceptor < GRPC::ClientInterceptor
|
5
|
-
WAIT_SEC = 3
|
6
|
-
|
7
|
-
def initialize(base_client)
|
8
|
-
@base_client = base_client
|
9
|
-
end
|
10
|
-
|
11
|
-
def cancel
|
12
|
-
@call.instance_variable_get('@wrapped').instance_variable_get('@call').cancel
|
13
|
-
i = 0
|
14
|
-
while i < WAIT_SEC
|
15
|
-
if @call.instance_variable_get('@wrapped').cancelled?
|
16
|
-
@base_client.log_internal ::Logger::DEBUG, 'Cancelled streaming.'
|
17
|
-
return
|
18
|
-
else
|
19
|
-
@base_client.log_internal ::Logger::DEBUG, 'Unable to cancel streaming. Trying again'
|
20
|
-
@call.instance_variable_get('@wrapped').instance_variable_get('@call').cancel
|
21
|
-
i += 1
|
22
|
-
sleep(1)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
@base_client.log_internal ::Logger::INFO, 'Unable to cancel streaming.'
|
26
|
-
end
|
27
|
-
|
28
|
-
def request_response(request:, call:, method:, metadata:, &block)
|
29
|
-
shared(call, &block)
|
30
|
-
end
|
31
|
-
|
32
|
-
def client_streamer(requests:, call:, method:, metadata:, &block)
|
33
|
-
shared(call, &block)
|
34
|
-
end
|
35
|
-
|
36
|
-
def server_streamer(request:, call:, method:, metadata:, &block)
|
37
|
-
shared(call, &block)
|
38
|
-
end
|
39
|
-
|
40
|
-
def bidi_streamer(requests:, call:, method:, metadata:, &block)
|
41
|
-
shared(call, &block)
|
42
|
-
end
|
43
|
-
|
44
|
-
def shared(call)
|
45
|
-
@call = call
|
46
|
-
yield
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
@@ -1,78 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Prefab
|
4
|
-
class RateLimitClient
|
5
|
-
def initialize(base_client, timeout)
|
6
|
-
@timeout = timeout
|
7
|
-
@base_client = base_client
|
8
|
-
end
|
9
|
-
|
10
|
-
def pass?(group)
|
11
|
-
result = acquire([group], 1)
|
12
|
-
result.passed
|
13
|
-
end
|
14
|
-
|
15
|
-
def acquire(groups, acquire_amount, allow_partial_response: false, on_error: :log_and_pass)
|
16
|
-
expiry_cache_key = "prefab.ratelimit.expiry:#{groups.join('.')}"
|
17
|
-
expiry = @base_client.shared_cache.read(expiry_cache_key)
|
18
|
-
if !expiry.nil? && Integer(expiry) > Time.now.utc.to_f * 1000
|
19
|
-
@base_client.stats.increment('prefab.ratelimit.limitcheck.expirycache.hit', tags: [])
|
20
|
-
return Prefab::LimitResponse.new(passed: false, amount: 0)
|
21
|
-
end
|
22
|
-
|
23
|
-
req = Prefab::LimitRequest.new(
|
24
|
-
account_id: @base_client.account_id,
|
25
|
-
acquire_amount: acquire_amount,
|
26
|
-
groups: groups,
|
27
|
-
allow_partial_response: allow_partial_response
|
28
|
-
)
|
29
|
-
|
30
|
-
result = @base_client.request Prefab::RateLimitService, :limit_check, req_options: { timeout: @timeout },
|
31
|
-
params: req
|
32
|
-
|
33
|
-
reset = result.limit_reset_at
|
34
|
-
@base_client.shared_cache.write(expiry_cache_key, reset) unless reset < 1 # protobuf default int to 0
|
35
|
-
|
36
|
-
@base_client.stats.increment('prefab.ratelimit.limitcheck',
|
37
|
-
tags: ["policy_group:#{result.policy_group}", "pass:#{result.passed}"])
|
38
|
-
|
39
|
-
result
|
40
|
-
rescue StandardError => e
|
41
|
-
handle_error(e, on_error, groups)
|
42
|
-
end
|
43
|
-
|
44
|
-
def upsert(key, policy_name, limit, burst: nil, safety_level: nil)
|
45
|
-
burst = limit if burst.nil?
|
46
|
-
limit_definition = Prefab::LimitDefinition.new(
|
47
|
-
account_id: @base_client.account_id,
|
48
|
-
policy_name: Object.const_get("Prefab::LimitResponse::LimitPolicyNames::#{policy_name}"),
|
49
|
-
limit: limit,
|
50
|
-
burst: burst
|
51
|
-
)
|
52
|
-
limit_definition.safety_level = safety_level unless safety_level.nil?
|
53
|
-
config_value = Prefab::ConfigValue.new(limit_definition: limit_definition)
|
54
|
-
config_delta = Prefab::ConfigClient.value_to_delta(key, config_value)
|
55
|
-
upsert_req = Prefab::UpsertRequest.new(config_delta: config_delta)
|
56
|
-
|
57
|
-
@base_client.request Prefab::ConfigService, :upsert, req_options: { timeout: @timeout }, params: upsert_req
|
58
|
-
end
|
59
|
-
|
60
|
-
private
|
61
|
-
|
62
|
-
def handle_error(e, on_error, groups)
|
63
|
-
@base_client.stats.increment('prefab.ratelimit.error', tags: ['type:limit'])
|
64
|
-
|
65
|
-
message = "ratelimit for #{groups} error: #{e.message}"
|
66
|
-
case on_error
|
67
|
-
when :log_and_pass
|
68
|
-
@base_client.log.warn(message)
|
69
|
-
Prefab::LimitResponse.new(passed: true, amount: 0)
|
70
|
-
when :log_and_hit
|
71
|
-
@base_client.log.warn(message)
|
72
|
-
Prefab::LimitResponse.new(passed: false, amount: 0)
|
73
|
-
when :throw
|
74
|
-
raise e
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|