launchdarkly-server-sdk 8.11.1 → 8.11.3
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 +4 -4
- data/lib/ldclient-rb/config.rb +66 -3
- data/lib/ldclient-rb/context.rb +1 -1
- data/lib/ldclient-rb/data_system.rb +243 -0
- data/lib/ldclient-rb/events.rb +35 -20
- data/lib/ldclient-rb/flags_state.rb +1 -1
- data/lib/ldclient-rb/impl/big_segments.rb +4 -4
- data/lib/ldclient-rb/impl/cache_store.rb +44 -0
- data/lib/ldclient-rb/impl/data_source/null_processor.rb +52 -0
- data/lib/ldclient-rb/impl/data_source/polling.rb +108 -0
- data/lib/ldclient-rb/impl/data_source/requestor.rb +106 -0
- data/lib/ldclient-rb/impl/data_source/status_provider.rb +78 -0
- data/lib/ldclient-rb/impl/data_source/stream.rb +198 -0
- data/lib/ldclient-rb/impl/data_source.rb +3 -3
- data/lib/ldclient-rb/impl/data_store/data_kind.rb +108 -0
- data/lib/ldclient-rb/impl/data_store/feature_store_client_wrapper.rb +187 -0
- data/lib/ldclient-rb/impl/data_store/in_memory_feature_store.rb +130 -0
- data/lib/ldclient-rb/impl/data_store/status_provider.rb +82 -0
- data/lib/ldclient-rb/impl/data_store/store.rb +371 -0
- data/lib/ldclient-rb/impl/data_store.rb +11 -97
- data/lib/ldclient-rb/impl/data_system/fdv1.rb +178 -0
- data/lib/ldclient-rb/impl/data_system/fdv2.rb +471 -0
- data/lib/ldclient-rb/impl/data_system/polling.rb +601 -0
- data/lib/ldclient-rb/impl/data_system/protocolv2.rb +264 -0
- data/lib/ldclient-rb/impl/data_system.rb +298 -0
- data/lib/ldclient-rb/impl/dependency_tracker.rb +21 -9
- data/lib/ldclient-rb/impl/evaluator.rb +3 -2
- data/lib/ldclient-rb/impl/event_sender.rb +4 -3
- data/lib/ldclient-rb/impl/expiring_cache.rb +79 -0
- data/lib/ldclient-rb/impl/integrations/file_data_source.rb +9 -9
- data/lib/ldclient-rb/impl/integrations/test_data/test_data_source.rb +0 -1
- data/lib/ldclient-rb/impl/integrations/test_data/test_data_source_v2.rb +288 -0
- data/lib/ldclient-rb/impl/memoized_value.rb +34 -0
- data/lib/ldclient-rb/impl/migrations/migrator.rb +2 -1
- data/lib/ldclient-rb/impl/migrations/tracker.rb +2 -1
- data/lib/ldclient-rb/impl/model/serialization.rb +6 -6
- data/lib/ldclient-rb/impl/non_blocking_thread_pool.rb +48 -0
- data/lib/ldclient-rb/impl/repeating_task.rb +2 -2
- data/lib/ldclient-rb/impl/simple_lru_cache.rb +27 -0
- data/lib/ldclient-rb/impl/util.rb +65 -0
- data/lib/ldclient-rb/impl.rb +1 -2
- data/lib/ldclient-rb/in_memory_store.rb +1 -18
- data/lib/ldclient-rb/integrations/test_data/flag_builder.rb +9 -9
- data/lib/ldclient-rb/integrations/test_data.rb +11 -11
- data/lib/ldclient-rb/integrations/test_data_v2/flag_builder_v2.rb +582 -0
- data/lib/ldclient-rb/integrations/test_data_v2.rb +248 -0
- data/lib/ldclient-rb/integrations/util/store_wrapper.rb +3 -2
- data/lib/ldclient-rb/interfaces/data_system.rb +755 -0
- data/lib/ldclient-rb/interfaces/feature_store.rb +3 -0
- data/lib/ldclient-rb/ldclient.rb +55 -149
- data/lib/ldclient-rb/util.rb +11 -70
- data/lib/ldclient-rb/version.rb +1 -1
- data/lib/ldclient-rb.rb +8 -17
- metadata +52 -17
- data/lib/ldclient-rb/cache_store.rb +0 -45
- data/lib/ldclient-rb/expiring_cache.rb +0 -77
- data/lib/ldclient-rb/memoized_value.rb +0 -32
- data/lib/ldclient-rb/non_blocking_thread_pool.rb +0 -46
- data/lib/ldclient-rb/polling.rb +0 -102
- data/lib/ldclient-rb/requestor.rb +0 -102
- data/lib/ldclient-rb/simple_lru_cache.rb +0 -25
- data/lib/ldclient-rb/stream.rb +0 -196
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
require "uri"
|
|
2
|
+
require "http"
|
|
3
|
+
|
|
1
4
|
module LaunchDarkly
|
|
2
5
|
module Impl
|
|
3
6
|
module Util
|
|
@@ -93,6 +96,68 @@ module LaunchDarkly
|
|
|
93
96
|
}
|
|
94
97
|
nil
|
|
95
98
|
end
|
|
99
|
+
|
|
100
|
+
#
|
|
101
|
+
# Append the payload filter key query parameter to the provided URI.
|
|
102
|
+
#
|
|
103
|
+
# @param uri [String]
|
|
104
|
+
# @param config [Config]
|
|
105
|
+
# @return [String]
|
|
106
|
+
#
|
|
107
|
+
def self.add_payload_filter_key(uri, config)
|
|
108
|
+
return uri if config.payload_filter_key.nil?
|
|
109
|
+
|
|
110
|
+
begin
|
|
111
|
+
parsed = URI.parse(uri)
|
|
112
|
+
new_query_params = URI.decode_www_form(String(parsed.query)) << ["filter", config.payload_filter_key]
|
|
113
|
+
parsed.query = URI.encode_www_form(new_query_params)
|
|
114
|
+
parsed.to_s
|
|
115
|
+
rescue URI::InvalidURIError
|
|
116
|
+
config.logger.warn { "[LDClient] URI could not be parsed. No filtering will be applied." }
|
|
117
|
+
uri
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def self.new_http_client(uri_s, config)
|
|
122
|
+
http_client_options = {}
|
|
123
|
+
if config.socket_factory
|
|
124
|
+
http_client_options["socket_class"] = config.socket_factory
|
|
125
|
+
end
|
|
126
|
+
proxy = URI.parse(uri_s).find_proxy
|
|
127
|
+
unless proxy.nil?
|
|
128
|
+
http_client_options["proxy"] = {
|
|
129
|
+
proxy_address: proxy.host,
|
|
130
|
+
proxy_port: proxy.port,
|
|
131
|
+
proxy_username: proxy.user,
|
|
132
|
+
proxy_password: proxy.password,
|
|
133
|
+
}
|
|
134
|
+
end
|
|
135
|
+
HTTP::Client.new(http_client_options)
|
|
136
|
+
.timeout({
|
|
137
|
+
read: config.read_timeout,
|
|
138
|
+
connect: config.connect_timeout,
|
|
139
|
+
})
|
|
140
|
+
.persistent(uri_s)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def self.log_exception(logger, message, exc)
|
|
144
|
+
logger.error { "[LDClient] #{message}: #{exc.inspect}" }
|
|
145
|
+
logger.debug { "[LDClient] Exception trace: #{exc.backtrace}" }
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def self.http_error_recoverable?(status)
|
|
149
|
+
if status >= 400 && status < 500
|
|
150
|
+
status == 400 || status == 408 || status == 429
|
|
151
|
+
else
|
|
152
|
+
true
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def self.http_error_message(status, context, recoverable_message)
|
|
157
|
+
desc = (status == 401 || status == 403) ? " (invalid SDK key)" : ""
|
|
158
|
+
message = http_error_recoverable?(status) ? recoverable_message : "giving up permanently"
|
|
159
|
+
"HTTP error #{status}#{desc} for #{context} - #{message}"
|
|
160
|
+
end
|
|
96
161
|
end
|
|
97
162
|
end
|
|
98
163
|
end
|
data/lib/ldclient-rb/impl.rb
CHANGED
|
@@ -1,24 +1,7 @@
|
|
|
1
1
|
require "concurrent/atomics"
|
|
2
|
+
require "ldclient-rb/impl/data_store"
|
|
2
3
|
|
|
3
4
|
module LaunchDarkly
|
|
4
|
-
|
|
5
|
-
# These constants denote the types of data that can be stored in the feature store. If
|
|
6
|
-
# we add another storable data type in the future, as long as it follows the same pattern
|
|
7
|
-
# (having "key", "version", and "deleted" properties), we only need to add a corresponding
|
|
8
|
-
# constant here and the existing store should be able to handle it.
|
|
9
|
-
#
|
|
10
|
-
# The :priority and :get_dependency_keys properties are used by FeatureStoreDataSetSorter
|
|
11
|
-
# to ensure data consistency during non-atomic updates.
|
|
12
|
-
|
|
13
|
-
# @private
|
|
14
|
-
FEATURES = Impl::DataStore::DataKind.new(namespace: "features", priority: 1).freeze
|
|
15
|
-
|
|
16
|
-
# @private
|
|
17
|
-
SEGMENTS = Impl::DataStore::DataKind.new(namespace: "segments", priority: 0).freeze
|
|
18
|
-
|
|
19
|
-
# @private
|
|
20
|
-
ALL_KINDS = [FEATURES, SEGMENTS].freeze
|
|
21
|
-
|
|
22
5
|
#
|
|
23
6
|
# Default implementation of the LaunchDarkly client's feature store, using an in-memory
|
|
24
7
|
# cache. This object holds feature flags and related data received from LaunchDarkly.
|
|
@@ -12,14 +12,14 @@ module LaunchDarkly
|
|
|
12
12
|
class FlagBuilder
|
|
13
13
|
attr_reader :key
|
|
14
14
|
|
|
15
|
-
# @private
|
|
15
|
+
# @api private
|
|
16
16
|
def initialize(key)
|
|
17
17
|
@key = key
|
|
18
18
|
@on = true
|
|
19
19
|
@variations = []
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
# @private
|
|
22
|
+
# @api private
|
|
23
23
|
def initialize_copy(other)
|
|
24
24
|
super(other)
|
|
25
25
|
@variations = @variations.clone
|
|
@@ -357,7 +357,7 @@ module LaunchDarkly
|
|
|
357
357
|
self
|
|
358
358
|
end
|
|
359
359
|
|
|
360
|
-
# @private
|
|
360
|
+
# @api private
|
|
361
361
|
def add_rule(rule)
|
|
362
362
|
if @rules.nil?
|
|
363
363
|
@rules = Array.new
|
|
@@ -386,7 +386,7 @@ module LaunchDarkly
|
|
|
386
386
|
end
|
|
387
387
|
end
|
|
388
388
|
|
|
389
|
-
# @private
|
|
389
|
+
# @api private
|
|
390
390
|
def build(version)
|
|
391
391
|
res = { key: @key,
|
|
392
392
|
version: version,
|
|
@@ -486,16 +486,16 @@ module LaunchDarkly
|
|
|
486
486
|
# Finally, call {#then_return} to finish defining the rule.
|
|
487
487
|
#
|
|
488
488
|
class FlagRuleBuilder
|
|
489
|
-
# @private
|
|
489
|
+
# @api private
|
|
490
490
|
FlagRuleClause = Struct.new(:contextKind, :attribute, :op, :values, :negate, keyword_init: true) # rubocop:disable Naming/MethodName:
|
|
491
491
|
|
|
492
|
-
# @private
|
|
492
|
+
# @api private
|
|
493
493
|
def initialize(flag_builder)
|
|
494
494
|
@flag_builder = flag_builder
|
|
495
495
|
@clauses = Array.new
|
|
496
496
|
end
|
|
497
497
|
|
|
498
|
-
# @private
|
|
498
|
+
# @api private
|
|
499
499
|
def intialize_copy(other)
|
|
500
500
|
super(other)
|
|
501
501
|
@clauses = @clauses.clone
|
|
@@ -612,7 +612,7 @@ module LaunchDarkly
|
|
|
612
612
|
end
|
|
613
613
|
end
|
|
614
614
|
|
|
615
|
-
# @private
|
|
615
|
+
# @api private
|
|
616
616
|
def build(ri)
|
|
617
617
|
{
|
|
618
618
|
id: 'rule' + ri.to_s,
|
|
@@ -622,7 +622,7 @@ module LaunchDarkly
|
|
|
622
622
|
end
|
|
623
623
|
end
|
|
624
624
|
|
|
625
|
-
# @private
|
|
625
|
+
# @api private
|
|
626
626
|
def variation_for_boolean(variation)
|
|
627
627
|
variation ? TRUE_VARIATION_INDEX : FALSE_VARIATION_INDEX
|
|
628
628
|
end
|
|
@@ -42,7 +42,7 @@ module LaunchDarkly
|
|
|
42
42
|
self.new
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
# @private
|
|
45
|
+
# @api private
|
|
46
46
|
def initialize
|
|
47
47
|
@flag_builders = Hash.new
|
|
48
48
|
@current_flags = Hash.new
|
|
@@ -56,7 +56,7 @@ module LaunchDarkly
|
|
|
56
56
|
# Called internally by the SDK to determine what arguments to pass to call
|
|
57
57
|
# You do not need to call this method.
|
|
58
58
|
#
|
|
59
|
-
# @private
|
|
59
|
+
# @api private
|
|
60
60
|
def arity
|
|
61
61
|
2
|
|
62
62
|
end
|
|
@@ -65,7 +65,7 @@ module LaunchDarkly
|
|
|
65
65
|
# Called internally by the SDK to associate this test data source with an {@code LDClient} instance.
|
|
66
66
|
# You do not need to call this method.
|
|
67
67
|
#
|
|
68
|
-
# @private
|
|
68
|
+
# @api private
|
|
69
69
|
def call(_, config)
|
|
70
70
|
impl = LaunchDarkly::Impl::Integrations::TestData::TestDataSource.new(config.feature_store, self)
|
|
71
71
|
@instances_lock.with_write_lock { @instances.push(impl) }
|
|
@@ -121,10 +121,10 @@ module LaunchDarkly
|
|
|
121
121
|
if @current_flags[flag_key]
|
|
122
122
|
version = @current_flags[flag_key][:version]
|
|
123
123
|
end
|
|
124
|
-
new_flag = Impl::Model.deserialize(FEATURES, flag_builder.build(version+1))
|
|
124
|
+
new_flag = LaunchDarkly::Impl::Model.deserialize(LaunchDarkly::Impl::DataStore::FEATURES, flag_builder.build(version+1))
|
|
125
125
|
@current_flags[flag_key] = new_flag
|
|
126
126
|
end
|
|
127
|
-
update_item(FEATURES, new_flag)
|
|
127
|
+
update_item(LaunchDarkly::Impl::DataStore::FEATURES, new_flag)
|
|
128
128
|
self
|
|
129
129
|
end
|
|
130
130
|
|
|
@@ -147,7 +147,7 @@ module LaunchDarkly
|
|
|
147
147
|
# @return [TestData] the TestData instance
|
|
148
148
|
#
|
|
149
149
|
def use_preconfigured_flag(flag)
|
|
150
|
-
use_preconfigured_item(FEATURES, flag, @current_flags)
|
|
150
|
+
use_preconfigured_item(LaunchDarkly::Impl::DataStore::FEATURES, flag, @current_flags)
|
|
151
151
|
end
|
|
152
152
|
|
|
153
153
|
#
|
|
@@ -167,7 +167,7 @@ module LaunchDarkly
|
|
|
167
167
|
# @return [TestData] the TestData instance
|
|
168
168
|
#
|
|
169
169
|
def use_preconfigured_segment(segment)
|
|
170
|
-
use_preconfigured_item(SEGMENTS, segment, @current_segments)
|
|
170
|
+
use_preconfigured_item(LaunchDarkly::Impl::DataStore::SEGMENTS, segment, @current_segments)
|
|
171
171
|
end
|
|
172
172
|
|
|
173
173
|
private def use_preconfigured_item(kind, item, current)
|
|
@@ -194,17 +194,17 @@ module LaunchDarkly
|
|
|
194
194
|
end
|
|
195
195
|
end
|
|
196
196
|
|
|
197
|
-
# @private
|
|
197
|
+
# @api private
|
|
198
198
|
def make_init_data
|
|
199
199
|
@lock.with_read_lock do
|
|
200
200
|
{
|
|
201
|
-
FEATURES => @current_flags.clone,
|
|
202
|
-
SEGMENTS => @current_segments.clone,
|
|
201
|
+
LaunchDarkly::Impl::DataStore::FEATURES => @current_flags.clone,
|
|
202
|
+
LaunchDarkly::Impl::DataStore::SEGMENTS => @current_segments.clone,
|
|
203
203
|
}
|
|
204
204
|
end
|
|
205
205
|
end
|
|
206
206
|
|
|
207
|
-
# @private
|
|
207
|
+
# @api private
|
|
208
208
|
def closed_instance(instance)
|
|
209
209
|
@instances_lock.with_write_lock { @instances.delete(instance) }
|
|
210
210
|
end
|