launchdarkly-server-sdk 8.6.0 → 8.8.2
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 +21 -1
- data/lib/ldclient-rb/events.rb +1 -1
- data/lib/ldclient-rb/flags_state.rb +1 -0
- data/lib/ldclient-rb/impl/data_store.rb +50 -0
- data/lib/ldclient-rb/impl/evaluator.rb +16 -4
- data/lib/ldclient-rb/impl/event_sender.rb +13 -1
- data/lib/ldclient-rb/impl/util.rb +15 -0
- data/lib/ldclient-rb/in_memory_store.rb +2 -9
- data/lib/ldclient-rb/ldclient.rb +4 -2
- data/lib/ldclient-rb/util.rb +0 -5
- data/lib/ldclient-rb/version.rb +1 -1
- metadata +37 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d13d9414f9f1c60e2af1e2505cc40005da11facb98f362f6ce7c5575f92a9596
|
4
|
+
data.tar.gz: 94b1b3fd7e9e6f3c685f0b42c2fd798c72445df71d6e436426a89341b864bbeb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eafd096ae3a4257855b762e2a267560fcec2b0e96c976fff4a6aa69e113dfe178888637ea27deb963d70df2dd9933503304c010f9e96e17c2ff68b6697d976f1
|
7
|
+
data.tar.gz: 1ec423bf28d9bf8b6f15f0a0deb9e6b24b175fdb0f9ebc1b25987d4335ceaedfd91532539aaaf1cea9f85c954739707b971642619b40fa9cbce66f3168ed527d
|
data/lib/ldclient-rb/config.rb
CHANGED
@@ -65,6 +65,7 @@ module LaunchDarkly
|
|
65
65
|
@all_attributes_private = opts[:all_attributes_private] || false
|
66
66
|
@private_attributes = opts[:private_attributes] || []
|
67
67
|
@send_events = opts.has_key?(:send_events) ? opts[:send_events] : Config.default_send_events
|
68
|
+
@compress_events = opts.has_key?(:compress_events) ? opts[:compress_events] : Config.default_compress_events
|
68
69
|
@context_keys_capacity = opts[:context_keys_capacity] || Config.default_context_keys_capacity
|
69
70
|
@context_keys_flush_interval = opts[:context_keys_flush_interval] || Config.default_context_keys_flush_interval
|
70
71
|
@data_source = opts[:data_source]
|
@@ -76,7 +77,7 @@ module LaunchDarkly
|
|
76
77
|
@socket_factory = opts[:socket_factory]
|
77
78
|
@big_segments = opts[:big_segments] || BigSegmentsConfig.new(store: nil)
|
78
79
|
@application = LaunchDarkly::Impl::Util.validate_application_info(opts[:application] || {}, @logger)
|
79
|
-
@payload_filter_key = opts[:payload_filter_key]
|
80
|
+
@payload_filter_key = LaunchDarkly::Impl::Util.validate_payload_filter_key(opts[:payload_filter_key] , @logger)
|
80
81
|
@hooks = (opts[:hooks] || []).keep_if { |hook| hook.is_a? Interfaces::Hooks::Hook }
|
81
82
|
@omit_anonymous_contexts = opts.has_key?(:omit_anonymous_contexts) && opts[:omit_anonymous_contexts]
|
82
83
|
@data_source_update_sink = nil
|
@@ -254,6 +255,17 @@ module LaunchDarkly
|
|
254
255
|
#
|
255
256
|
attr_reader :send_events
|
256
257
|
|
258
|
+
#
|
259
|
+
# Should the event payload sent to LaunchDarkly use gzip compression. By default this is false to prevent backward
|
260
|
+
# breaking compatibility issues with older versions of the relay proxy.
|
261
|
+
#
|
262
|
+
# Customers not using the relay proxy are strongly encouraged to enable this feature to reduce egress bandwidth
|
263
|
+
# cost.
|
264
|
+
#
|
265
|
+
# @return [Boolean]
|
266
|
+
#
|
267
|
+
attr_reader :compress_events
|
268
|
+
|
257
269
|
#
|
258
270
|
# The number of context keys that the event processor can remember at any one time. This reduces the
|
259
271
|
# amount of duplicate context details sent in analytics events.
|
@@ -539,6 +551,14 @@ module LaunchDarkly
|
|
539
551
|
true
|
540
552
|
end
|
541
553
|
|
554
|
+
#
|
555
|
+
# The default value for {#compress_events}.
|
556
|
+
# @return [Boolean] false
|
557
|
+
#
|
558
|
+
def self.default_compress_events
|
559
|
+
false
|
560
|
+
end
|
561
|
+
|
542
562
|
#
|
543
563
|
# The default value for {#context_keys_capacity}.
|
544
564
|
# @return [Integer] 1000
|
data/lib/ldclient-rb/events.rb
CHANGED
@@ -513,13 +513,13 @@ module LaunchDarkly
|
|
513
513
|
evaluation: {
|
514
514
|
key: event.key,
|
515
515
|
value: event.evaluation.value,
|
516
|
+
reason: event.evaluation.reason,
|
516
517
|
},
|
517
518
|
}
|
518
519
|
|
519
520
|
out[:evaluation][:version] = event.version unless event.version.nil?
|
520
521
|
out[:evaluation][:default] = event.default unless event.default.nil?
|
521
522
|
out[:evaluation][:variation] = event.evaluation.variation_index unless event.evaluation.variation_index.nil?
|
522
|
-
out[:evaluation][:reason] = event.evaluation.reason unless event.evaluation.reason.nil?
|
523
523
|
out[:samplingRatio] = event.sampling_ratio unless event.sampling_ratio.nil? || event.sampling_ratio == 1
|
524
524
|
|
525
525
|
measurements = []
|
@@ -38,6 +38,7 @@ module LaunchDarkly
|
|
38
38
|
meta[:version] = flag_state[:version]
|
39
39
|
end
|
40
40
|
|
41
|
+
meta[:prerequisites] = flag_state[:prerequisites] unless flag_state[:prerequisites].nil? || flag_state[:prerequisites].empty?
|
41
42
|
meta[:variation] = flag_state[:variation] unless flag_state[:variation].nil?
|
42
43
|
meta[:trackEvents] = true if flag_state[:trackEvents]
|
43
44
|
meta[:trackReason] = true if flag_state[:trackReason]
|
@@ -4,6 +4,56 @@ require "ldclient-rb/interfaces"
|
|
4
4
|
module LaunchDarkly
|
5
5
|
module Impl
|
6
6
|
module DataStore
|
7
|
+
|
8
|
+
class DataKind
|
9
|
+
FEATURES = "features".freeze
|
10
|
+
SEGMENTS = "segments".freeze
|
11
|
+
|
12
|
+
FEATURE_PREREQ_FN = lambda { |flag| (flag[:prerequisites] || []).map { |p| p[:key] } }.freeze
|
13
|
+
|
14
|
+
attr_reader :namespace
|
15
|
+
attr_reader :priority
|
16
|
+
|
17
|
+
#
|
18
|
+
# @param namespace [String]
|
19
|
+
# @param priority [Integer]
|
20
|
+
#
|
21
|
+
def initialize(namespace:, priority:)
|
22
|
+
@namespace = namespace
|
23
|
+
@priority = priority
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Maintain the same behavior when these data kinds were standard ruby hashes.
|
28
|
+
#
|
29
|
+
# @param key [Symbol]
|
30
|
+
# @return [Object]
|
31
|
+
#
|
32
|
+
def [](key)
|
33
|
+
return priority if key == :priority
|
34
|
+
return namespace if key == :namespace
|
35
|
+
return get_dependency_keys_fn() if key == :get_dependency_keys
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
# Retrieve the dependency keys for a particular data kind. Right now, this is only defined for flags.
|
41
|
+
#
|
42
|
+
def get_dependency_keys_fn()
|
43
|
+
return nil unless @namespace == FEATURES
|
44
|
+
|
45
|
+
FEATURE_PREREQ_FN
|
46
|
+
end
|
47
|
+
|
48
|
+
def eql?(other)
|
49
|
+
other.is_a?(DataKind) && namespace == other.namespace && priority == other.priority
|
50
|
+
end
|
51
|
+
|
52
|
+
def hash
|
53
|
+
[namespace, priority].hash
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
7
57
|
class StatusProvider
|
8
58
|
include LaunchDarkly::Interfaces::DataStore::StatusProvider
|
9
59
|
|
@@ -32,8 +32,16 @@ module LaunchDarkly
|
|
32
32
|
def initialize(original_flag)
|
33
33
|
@prereq_stack = EvaluatorStack.new(original_flag.key)
|
34
34
|
@segment_stack = EvaluatorStack.new(nil)
|
35
|
+
@prerequisites = []
|
36
|
+
@depth = 0
|
35
37
|
end
|
36
38
|
|
39
|
+
def record_evaluated_prereq_key(key)
|
40
|
+
@prerequisites.push(key) if @depth.zero?
|
41
|
+
end
|
42
|
+
|
43
|
+
attr_accessor :depth
|
44
|
+
attr_reader :prerequisites
|
37
45
|
attr_reader :prereq_stack
|
38
46
|
attr_reader :segment_stack
|
39
47
|
end
|
@@ -135,7 +143,8 @@ module LaunchDarkly
|
|
135
143
|
#
|
136
144
|
# @param flag [LaunchDarkly::Impl::Model::FeatureFlag] the flag
|
137
145
|
# @param context [LaunchDarkly::LDContext] the evaluation context
|
138
|
-
# @return [EvalResult] the evaluation result
|
146
|
+
# @return [Array<EvalResult, EvaluatorState>] the evaluation result and a state object that may be used for
|
147
|
+
# inspecting the evaluation process
|
139
148
|
def evaluate(flag, context)
|
140
149
|
state = EvaluatorState.new(flag)
|
141
150
|
|
@@ -145,11 +154,11 @@ module LaunchDarkly
|
|
145
154
|
rescue EvaluationException => exn
|
146
155
|
LaunchDarkly::Util.log_exception(@logger, "Unexpected error when evaluating flag #{flag.key}", exn)
|
147
156
|
result.detail = EvaluationDetail.new(nil, nil, EvaluationReason::error(exn.error_kind))
|
148
|
-
return result
|
157
|
+
return result, state
|
149
158
|
rescue => exn
|
150
159
|
LaunchDarkly::Util.log_exception(@logger, "Unexpected error when evaluating flag #{flag.key}", exn)
|
151
160
|
result.detail = EvaluationDetail.new(nil, nil, EvaluationReason::error(EvaluationReason::ERROR_EXCEPTION))
|
152
|
-
return result
|
161
|
+
return result, state
|
153
162
|
end
|
154
163
|
|
155
164
|
unless result.big_segments_status.nil?
|
@@ -159,7 +168,7 @@ module LaunchDarkly
|
|
159
168
|
detail.reason.with_big_segments_status(result.big_segments_status))
|
160
169
|
end
|
161
170
|
result.detail = detail
|
162
|
-
result
|
171
|
+
[result, state]
|
163
172
|
end
|
164
173
|
|
165
174
|
# @param segment [LaunchDarkly::Impl::Model::Segment]
|
@@ -223,13 +232,16 @@ module LaunchDarkly
|
|
223
232
|
)
|
224
233
|
end
|
225
234
|
|
235
|
+
state.record_evaluated_prereq_key(prereq_key)
|
226
236
|
prereq_flag = @get_flag.call(prereq_key)
|
227
237
|
|
228
238
|
if prereq_flag.nil?
|
229
239
|
@logger.error { "[LDClient] Could not retrieve prerequisite flag \"#{prereq_key}\" when evaluating \"#{flag.key}\"" }
|
230
240
|
prereq_ok = false
|
231
241
|
else
|
242
|
+
state.depth += 1
|
232
243
|
prereq_res = eval_internal(prereq_flag, context, eval_result, state)
|
244
|
+
state.depth -= 1
|
233
245
|
# Note that if the prerequisite flag is off, we don't consider it a match no matter what its
|
234
246
|
# off variation was. But we still need to evaluate it in order to generate an event.
|
235
247
|
if !prereq_flag.on || prereq_res.variation_index != prerequisite.variation
|
@@ -2,6 +2,8 @@ require "ldclient-rb/impl/unbounded_pool"
|
|
2
2
|
|
3
3
|
require "securerandom"
|
4
4
|
require "http"
|
5
|
+
require "stringio"
|
6
|
+
require "zlib"
|
5
7
|
|
6
8
|
module LaunchDarkly
|
7
9
|
module Impl
|
@@ -42,14 +44,24 @@ module LaunchDarkly
|
|
42
44
|
@logger.debug { "[LDClient] sending #{description}: #{event_data}" }
|
43
45
|
headers = {}
|
44
46
|
headers["content-type"] = "application/json"
|
47
|
+
headers["content-encoding"] = "gzip" if @config.compress_events
|
45
48
|
Impl::Util.default_http_headers(@sdk_key, @config).each { |k, v| headers[k] = v }
|
46
49
|
unless is_diagnostic
|
47
50
|
headers["X-LaunchDarkly-Event-Schema"] = CURRENT_SCHEMA_VERSION.to_s
|
48
51
|
headers["X-LaunchDarkly-Payload-ID"] = payload_id
|
49
52
|
end
|
53
|
+
|
54
|
+
body = event_data
|
55
|
+
if @config.compress_events
|
56
|
+
gzip = Zlib::GzipWriter.new(StringIO.new)
|
57
|
+
gzip << event_data
|
58
|
+
|
59
|
+
body = gzip.close.string
|
60
|
+
end
|
61
|
+
|
50
62
|
response = http_client.request("POST", uri, {
|
51
63
|
headers: headers,
|
52
|
-
body:
|
64
|
+
body: body,
|
53
65
|
})
|
54
66
|
rescue StandardError => exn
|
55
67
|
@logger.warn { "[LDClient] Error sending events: #{exn.inspect}." }
|
@@ -75,6 +75,21 @@ module LaunchDarkly
|
|
75
75
|
version: validate_application_value(app[:version], :version, logger),
|
76
76
|
}
|
77
77
|
end
|
78
|
+
|
79
|
+
#
|
80
|
+
# @param value [String, nil]
|
81
|
+
# @param logger [Logger]
|
82
|
+
# @return [String, nil]
|
83
|
+
#
|
84
|
+
def self.validate_payload_filter_key(value, logger)
|
85
|
+
return nil if value.nil?
|
86
|
+
return value if value.is_a?(String) && /^[a-zA-Z0-9][._\-a-zA-Z0-9]*$/.match?(value)
|
87
|
+
|
88
|
+
logger.warn {
|
89
|
+
"Invalid payload filter configured, full environment will be fetched. Ensure the filter key is not empty and was copied correctly from LaunchDarkly settings."
|
90
|
+
}
|
91
|
+
nil
|
92
|
+
end
|
78
93
|
end
|
79
94
|
end
|
80
95
|
end
|
@@ -11,17 +11,10 @@ module LaunchDarkly
|
|
11
11
|
# to ensure data consistency during non-atomic updates.
|
12
12
|
|
13
13
|
# @private
|
14
|
-
FEATURES =
|
15
|
-
namespace: "features",
|
16
|
-
priority: 1, # that is, features should be stored after segments
|
17
|
-
get_dependency_keys: lambda { |flag| (flag[:prerequisites] || []).map { |p| p[:key] } },
|
18
|
-
}.freeze
|
14
|
+
FEATURES = Impl::DataStore::DataKind.new(namespace: "features", priority: 1).freeze
|
19
15
|
|
20
16
|
# @private
|
21
|
-
SEGMENTS =
|
22
|
-
namespace: "segments",
|
23
|
-
priority: 0,
|
24
|
-
}.freeze
|
17
|
+
SEGMENTS = Impl::DataStore::DataKind.new(namespace: "segments", priority: 0).freeze
|
25
18
|
|
26
19
|
# @private
|
27
20
|
ALL_KINDS = [FEATURES, SEGMENTS].freeze
|
data/lib/ldclient-rb/ldclient.rb
CHANGED
@@ -546,7 +546,8 @@ module LaunchDarkly
|
|
546
546
|
next
|
547
547
|
end
|
548
548
|
begin
|
549
|
-
|
549
|
+
(eval_result, eval_state) = @evaluator.evaluate(f, context)
|
550
|
+
detail = eval_result.detail
|
550
551
|
rescue => exn
|
551
552
|
detail = EvaluationDetail.new(nil, nil, EvaluationReason::error(EvaluationReason::ERROR_EXCEPTION))
|
552
553
|
Util.log_exception(@config.logger, "Error evaluating flag \"#{k}\" in all_flags_state", exn)
|
@@ -558,6 +559,7 @@ module LaunchDarkly
|
|
558
559
|
value: detail.value,
|
559
560
|
variation: detail.variation_index,
|
560
561
|
reason: detail.reason,
|
562
|
+
prerequisites: eval_state.prerequisites,
|
561
563
|
version: f[:version],
|
562
564
|
trackEvents: f[:trackEvents] || requires_experiment_data,
|
563
565
|
trackReason: requires_experiment_data,
|
@@ -705,7 +707,7 @@ module LaunchDarkly
|
|
705
707
|
end
|
706
708
|
|
707
709
|
begin
|
708
|
-
res = @evaluator.evaluate(feature, context)
|
710
|
+
(res, _) = @evaluator.evaluate(feature, context)
|
709
711
|
unless res.prereq_evals.nil?
|
710
712
|
res.prereq_evals.each do |prereq_eval|
|
711
713
|
record_prereq_flag_eval(prereq_eval.prereq_flag, prereq_eval.prereq_of_flag, context, prereq_eval.detail, with_reasons)
|
data/lib/ldclient-rb/util.rb
CHANGED
@@ -77,11 +77,6 @@ module LaunchDarkly
|
|
77
77
|
def self.add_payload_filter_key(uri, config)
|
78
78
|
return uri if config.payload_filter_key.nil?
|
79
79
|
|
80
|
-
unless config.payload_filter_key.is_a?(String) && !config.payload_filter_key.empty?
|
81
|
-
config.logger.warn { "[LDClient] Filter key must be a non-empty string. No filtering will be applied." }
|
82
|
-
return uri
|
83
|
-
end
|
84
|
-
|
85
80
|
begin
|
86
81
|
parsed = URI.parse(uri)
|
87
82
|
new_query_params = URI.decode_www_form(String(parsed.query)) << ["filter", config.payload_filter_key]
|
data/lib/ldclient-rb/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: launchdarkly-server-sdk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 8.
|
4
|
+
version: 8.8.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- LaunchDarkly
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-06
|
11
|
+
date: 2024-11-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk-dynamodb
|
@@ -24,6 +24,26 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.57'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rexml
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.3'
|
34
|
+
- - ">="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: 3.3.7
|
37
|
+
type: :development
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - "~>"
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '3.3'
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 3.3.7
|
27
47
|
- !ruby/object:Gem::Dependency
|
28
48
|
name: bundler
|
29
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -248,6 +268,20 @@ dependencies:
|
|
248
268
|
- - "~>"
|
249
269
|
- !ruby/object:Gem::Version
|
250
270
|
version: 0.1.2
|
271
|
+
- !ruby/object:Gem::Dependency
|
272
|
+
name: zlib
|
273
|
+
requirement: !ruby/object:Gem::Requirement
|
274
|
+
requirements:
|
275
|
+
- - "~>"
|
276
|
+
- !ruby/object:Gem::Version
|
277
|
+
version: '3.1'
|
278
|
+
type: :runtime
|
279
|
+
prerelease: false
|
280
|
+
version_requirements: !ruby/object:Gem::Requirement
|
281
|
+
requirements:
|
282
|
+
- - "~>"
|
283
|
+
- !ruby/object:Gem::Version
|
284
|
+
version: '3.1'
|
251
285
|
- !ruby/object:Gem::Dependency
|
252
286
|
name: json
|
253
287
|
requirement: !ruby/object:Gem::Requirement
|
@@ -376,7 +410,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
376
410
|
- !ruby/object:Gem::Version
|
377
411
|
version: '0'
|
378
412
|
requirements: []
|
379
|
-
rubygems_version: 3.5.
|
413
|
+
rubygems_version: 3.5.22
|
380
414
|
signing_key:
|
381
415
|
specification_version: 4
|
382
416
|
summary: LaunchDarkly SDK for Ruby
|