launchdarkly-server-sdk 8.14.0-java → 8.15.0-java
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 +24 -0
- data/lib/ldclient-rb/impl/context_filter.rb +7 -7
- data/lib/ldclient-rb/impl/data_source/polling.rb +14 -1
- data/lib/ldclient-rb/impl/data_source/requestor.rb +13 -3
- data/lib/ldclient-rb/impl/data_source/stream.rb +2 -0
- data/lib/ldclient-rb/impl/data_source.rb +37 -0
- data/lib/ldclient-rb/impl/data_system/fdv1.rb +5 -0
- data/lib/ldclient-rb/impl/data_system/fdv2.rb +25 -1
- data/lib/ldclient-rb/impl/data_system/polling.rb +1 -1
- data/lib/ldclient-rb/impl/data_system.rb +9 -0
- data/lib/ldclient-rb/integrations/file_data.rb +1 -1
- data/lib/ldclient-rb/interfaces/hooks.rb +11 -1
- data/lib/ldclient-rb/ldclient.rb +1 -1
- data/lib/ldclient-rb/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dc18b4c7af16130b9f7e9f7c8d0a4acd8e562d16ebb7714fc7846c9ff96d1d3f
|
|
4
|
+
data.tar.gz: 2c9bfdff939fb1f11a9bc83e707aa0e550136004c4a2870b7220fb293ad268f2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0b23cf9eda9b570844be07e71831f19734881b72d08c464b109929b17d225ac2083d78ead046418bdfec68e67db82f940c344efd3ce408ad1a1ea49e0d3dcaf2
|
|
7
|
+
data.tar.gz: ceb93287c34afbbb2301d3991267a10b1349aa126bea72c028552c431dc3493ec5f136d06b79f21641363dd9bfa87cce1a5ceb225534ff98ab8ad3088c2d7fc0
|
data/lib/ldclient-rb/config.rb
CHANGED
|
@@ -399,6 +399,30 @@ module LaunchDarkly
|
|
|
399
399
|
#
|
|
400
400
|
attr_reader :wrapper_version
|
|
401
401
|
|
|
402
|
+
#
|
|
403
|
+
# Returns a copy of this configuration with different wrapper information.
|
|
404
|
+
#
|
|
405
|
+
# This is intended for use by wrapper libraries, such as the LaunchDarkly OpenFeature provider, which need to
|
|
406
|
+
# identify themselves rather than the application which created the configuration.
|
|
407
|
+
#
|
|
408
|
+
# The copy is shallow: it shares the objects the original configuration references, such as the feature store,
|
|
409
|
+
# the logger, and the socket factory. Mutating one of those objects affects both configurations.
|
|
410
|
+
#
|
|
411
|
+
# @param wrapper_name [String, nil] see {#wrapper_name}
|
|
412
|
+
# @param wrapper_version [String, nil] see {#wrapper_version}
|
|
413
|
+
#
|
|
414
|
+
# @return [LaunchDarkly::Config]
|
|
415
|
+
#
|
|
416
|
+
def with_wrapper_information(wrapper_name, wrapper_version = nil)
|
|
417
|
+
updated = dup
|
|
418
|
+
updated.wrapper_name = wrapper_name
|
|
419
|
+
updated.wrapper_version = wrapper_version
|
|
420
|
+
|
|
421
|
+
updated
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
protected attr_writer :wrapper_name, :wrapper_version
|
|
425
|
+
|
|
402
426
|
#
|
|
403
427
|
# The factory used to construct sockets for HTTP operations. The factory must
|
|
404
428
|
# provide the method `open(uri, timeout)`. The `open` method must return a
|
|
@@ -70,15 +70,15 @@ module LaunchDarkly
|
|
|
70
70
|
filtered[:anonymous] = true if anonymous
|
|
71
71
|
|
|
72
72
|
redacted = []
|
|
73
|
-
private_attributes = @private_attributes
|
|
73
|
+
private_attributes = @private_attributes + context.private_attributes
|
|
74
74
|
|
|
75
75
|
name = context.get_value(:name)
|
|
76
|
-
if !name.nil? && !check_whole_attribute_private(:name, private_attributes, redacted, anonymous && redact_anonymous)
|
|
76
|
+
if !name.nil? && !check_whole_attribute_private(Reference.create_literal(:name), private_attributes, redacted, anonymous && redact_anonymous)
|
|
77
77
|
filtered[:name] = name
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
context.get_custom_attribute_names.each do |attribute|
|
|
81
|
-
unless check_whole_attribute_private(attribute, private_attributes, redacted, anonymous && redact_anonymous)
|
|
81
|
+
unless check_whole_attribute_private(Reference.create_literal(attribute), private_attributes, redacted, anonymous && redact_anonymous)
|
|
82
82
|
value = context.get_value(attribute)
|
|
83
83
|
filtered[attribute] = redact_json_value(nil, attribute, value, private_attributes, redacted)
|
|
84
84
|
end
|
|
@@ -92,7 +92,7 @@ module LaunchDarkly
|
|
|
92
92
|
#
|
|
93
93
|
# Check if an entire attribute should be redacted.
|
|
94
94
|
#
|
|
95
|
-
# @param attribute [
|
|
95
|
+
# @param attribute [Reference]
|
|
96
96
|
# @param private_attributes [Array<Reference>]
|
|
97
97
|
# @param redacted [Array<Symbol>]
|
|
98
98
|
# @param redact_all [Boolean]
|
|
@@ -100,13 +100,13 @@ module LaunchDarkly
|
|
|
100
100
|
#
|
|
101
101
|
private def check_whole_attribute_private(attribute, private_attributes, redacted, redact_all)
|
|
102
102
|
if @all_attributes_private || redact_all
|
|
103
|
-
redacted << attribute
|
|
103
|
+
redacted << attribute.raw_path.to_sym
|
|
104
104
|
return true
|
|
105
105
|
end
|
|
106
106
|
|
|
107
107
|
private_attributes.each do |private_attribute|
|
|
108
|
-
if private_attribute.component(0) == attribute && private_attribute.depth == 1
|
|
109
|
-
redacted << attribute
|
|
108
|
+
if private_attribute.component(0) == attribute.component(0) && private_attribute.depth == 1
|
|
109
|
+
redacted << attribute.raw_path.to_sym
|
|
110
110
|
return true
|
|
111
111
|
end
|
|
112
112
|
end
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require "ldclient-rb/impl/data_source"
|
|
1
2
|
require "ldclient-rb/impl/repeating_task"
|
|
2
3
|
require "ldclient-rb/impl/util"
|
|
3
4
|
|
|
@@ -35,7 +36,8 @@ module LaunchDarkly
|
|
|
35
36
|
|
|
36
37
|
def poll
|
|
37
38
|
begin
|
|
38
|
-
all_data =
|
|
39
|
+
all_data, headers = request_all_data
|
|
40
|
+
DataSource.record_environment_id(@config.data_source_update_sink, headers)
|
|
39
41
|
if all_data
|
|
40
42
|
update_sink_or_data_store.init(all_data)
|
|
41
43
|
if @initialized.make_true
|
|
@@ -92,6 +94,17 @@ module LaunchDarkly
|
|
|
92
94
|
@config.data_source_update_sink || @config.feature_store
|
|
93
95
|
end
|
|
94
96
|
|
|
97
|
+
#
|
|
98
|
+
# Requestors provided by application code may not be able to report the response headers.
|
|
99
|
+
#
|
|
100
|
+
# @return [Array(Hash, HTTP::Headers, nil)]
|
|
101
|
+
#
|
|
102
|
+
private def request_all_data
|
|
103
|
+
return @requestor.request_all_data_with_headers if @requestor.respond_to?(:request_all_data_with_headers)
|
|
104
|
+
|
|
105
|
+
[@requestor.request_all_data, nil]
|
|
106
|
+
end
|
|
107
|
+
|
|
95
108
|
#
|
|
96
109
|
# @param [LaunchDarkly::Interfaces::DataSource::ErrorInfo, nil] error_info
|
|
97
110
|
#
|
|
@@ -40,8 +40,18 @@ module LaunchDarkly
|
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
def request_all_data()
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
request_all_data_with_headers.first
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
#
|
|
47
|
+
# Requests the full data set, also returning the headers of the response it was retrieved from.
|
|
48
|
+
#
|
|
49
|
+
# @return [Array(Hash, HTTP::Headers)]
|
|
50
|
+
#
|
|
51
|
+
def request_all_data_with_headers
|
|
52
|
+
body, headers = make_request("/sdk/latest-all")
|
|
53
|
+
all_data = JSON.parse(body, symbolize_names: true)
|
|
54
|
+
[Impl::Model.make_all_store_data(all_data, @config.logger), headers]
|
|
45
55
|
end
|
|
46
56
|
|
|
47
57
|
def stop
|
|
@@ -80,7 +90,7 @@ module LaunchDarkly
|
|
|
80
90
|
etag = response.headers["etag"]
|
|
81
91
|
@cache.write(uri, CacheEntry.new(etag, body)) unless etag.nil?
|
|
82
92
|
end
|
|
83
|
-
body
|
|
93
|
+
[body, response.headers]
|
|
84
94
|
end
|
|
85
95
|
|
|
86
96
|
def fix_encoding(body, content_type)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require "ldclient-rb/impl/data_source"
|
|
1
2
|
require "ldclient-rb/impl/model/serialization"
|
|
2
3
|
require "ldclient-rb/impl/util"
|
|
3
4
|
require "ldclient-rb/in_memory_store"
|
|
@@ -54,6 +55,7 @@ module LaunchDarkly
|
|
|
54
55
|
|
|
55
56
|
uri = Impl::Util.add_payload_filter_key(@config.stream_uri + "/all", @config)
|
|
56
57
|
@es = SSE::Client.new(uri, **opts) do |conn|
|
|
58
|
+
conn.on_connect { |response_headers| DataSource.record_environment_id(@data_source_update_sink, response_headers) }
|
|
57
59
|
conn.on_event { |event| process_message(event) }
|
|
58
60
|
conn.on_error { |err|
|
|
59
61
|
log_connection_result(false)
|
|
@@ -7,6 +7,27 @@ require "set"
|
|
|
7
7
|
module LaunchDarkly
|
|
8
8
|
module Impl
|
|
9
9
|
module DataSource
|
|
10
|
+
LD_ENVID_HEADER = "X-LD-EnvID"
|
|
11
|
+
|
|
12
|
+
#
|
|
13
|
+
# Records the environment ID reported by LaunchDarkly, when the response headers provide one and the sink is
|
|
14
|
+
# able to hold it. Externally implemented sinks are not required to support this.
|
|
15
|
+
#
|
|
16
|
+
# @param sink [LaunchDarkly::Interfaces::DataSource::UpdateSink, nil]
|
|
17
|
+
# @param headers [#[], nil]
|
|
18
|
+
# @return [void]
|
|
19
|
+
#
|
|
20
|
+
def self.record_environment_id(sink, headers)
|
|
21
|
+
return if headers.nil? || !sink.respond_to?(:set_environment_id)
|
|
22
|
+
|
|
23
|
+
environment_id = headers[LD_ENVID_HEADER]
|
|
24
|
+
# The http gem returns arrays for repeated headers; normalize to a string.
|
|
25
|
+
environment_id = environment_id.first if environment_id.is_a?(Array)
|
|
26
|
+
return unless environment_id.is_a?(String) && !environment_id.empty?
|
|
27
|
+
|
|
28
|
+
sink.set_environment_id(environment_id)
|
|
29
|
+
end
|
|
30
|
+
|
|
10
31
|
class StatusProvider
|
|
11
32
|
include LaunchDarkly::Interfaces::DataSource::StatusProvider
|
|
12
33
|
|
|
@@ -41,12 +62,28 @@ module LaunchDarkly
|
|
|
41
62
|
@dependency_tracker = LaunchDarkly::Impl::DependencyTracker.new
|
|
42
63
|
|
|
43
64
|
@mutex = Mutex.new
|
|
65
|
+
@environment_id = nil
|
|
44
66
|
@current_status = LaunchDarkly::Interfaces::DataSource::Status.new(
|
|
45
67
|
LaunchDarkly::Interfaces::DataSource::Status::INITIALIZING,
|
|
46
68
|
Time.now,
|
|
47
69
|
nil)
|
|
48
70
|
end
|
|
49
71
|
|
|
72
|
+
#
|
|
73
|
+
# @return [String, nil] The environment ID reported by LaunchDarkly, if known
|
|
74
|
+
#
|
|
75
|
+
def environment_id
|
|
76
|
+
@mutex.synchronize { @environment_id }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
#
|
|
80
|
+
# @param environment_id [String]
|
|
81
|
+
# @return [void]
|
|
82
|
+
#
|
|
83
|
+
def set_environment_id(environment_id)
|
|
84
|
+
@mutex.synchronize { @environment_id = environment_id }
|
|
85
|
+
end
|
|
86
|
+
|
|
50
87
|
def init(all_data)
|
|
51
88
|
old_data = nil
|
|
52
89
|
monitor_store_update do
|
|
@@ -100,6 +100,9 @@ module LaunchDarkly
|
|
|
100
100
|
@store.with_persistence(wrapper, writable, @data_store_status_provider)
|
|
101
101
|
end
|
|
102
102
|
|
|
103
|
+
@environment_id = nil
|
|
104
|
+
@environment_id_lock = Mutex.new
|
|
105
|
+
|
|
103
106
|
# Threading
|
|
104
107
|
@stop_event = Concurrent::Event.new
|
|
105
108
|
@ready_event = Concurrent::Event.new
|
|
@@ -185,6 +188,11 @@ module LaunchDarkly
|
|
|
185
188
|
@flag_change_broadcaster
|
|
186
189
|
end
|
|
187
190
|
|
|
191
|
+
# (see DataSystem#environment_id)
|
|
192
|
+
def environment_id
|
|
193
|
+
@environment_id_lock.synchronize { @environment_id }
|
|
194
|
+
end
|
|
195
|
+
|
|
188
196
|
# (see DataSystem#data_availability)
|
|
189
197
|
def data_availability
|
|
190
198
|
return DataAvailability::REFRESHED if @store.selector.defined?
|
|
@@ -270,6 +278,7 @@ module LaunchDarkly
|
|
|
270
278
|
if basis_result.success?
|
|
271
279
|
basis = basis_result.value
|
|
272
280
|
@logger.info { "[LDClient] Initialized via #{initializer.name}" }
|
|
281
|
+
record_environment_id(basis.environment_id)
|
|
273
282
|
|
|
274
283
|
# Apply the basis to the store regardless of whether fallback was signalled.
|
|
275
284
|
# If the server returned a valid payload alongside the directive we still want
|
|
@@ -460,7 +469,10 @@ module LaunchDarkly
|
|
|
460
469
|
@store.apply(update.change_set, true) if update.change_set
|
|
461
470
|
|
|
462
471
|
# Set ready event on valid update
|
|
463
|
-
|
|
472
|
+
if update.state == LaunchDarkly::Interfaces::DataSource::Status::VALID
|
|
473
|
+
@ready_event.set
|
|
474
|
+
record_environment_id(update.environment_id)
|
|
475
|
+
end
|
|
464
476
|
|
|
465
477
|
# Update status
|
|
466
478
|
@data_source_status_provider.update_status(update.state, update.error)
|
|
@@ -481,6 +493,18 @@ module LaunchDarkly
|
|
|
481
493
|
SyncResult::REMOVE
|
|
482
494
|
end
|
|
483
495
|
|
|
496
|
+
#
|
|
497
|
+
# Retains the environment ID reported by a successful connection to LaunchDarkly.
|
|
498
|
+
#
|
|
499
|
+
# @param environment_id [String, nil]
|
|
500
|
+
# @return [void]
|
|
501
|
+
#
|
|
502
|
+
def record_environment_id(environment_id)
|
|
503
|
+
return unless environment_id.is_a?(String) && !environment_id.empty?
|
|
504
|
+
|
|
505
|
+
@environment_id_lock.synchronize { @environment_id = environment_id }
|
|
506
|
+
end
|
|
507
|
+
|
|
484
508
|
#
|
|
485
509
|
# Determine if we should fallback to the next synchronizer.
|
|
486
510
|
#
|
|
@@ -313,7 +313,7 @@ module LaunchDarkly
|
|
|
313
313
|
query_params << ["filter", @config.payload_filter_key] unless @config.payload_filter_key.nil?
|
|
314
314
|
|
|
315
315
|
if selector && selector.defined?
|
|
316
|
-
query_params << ["
|
|
316
|
+
query_params << ["basis", selector.state]
|
|
317
317
|
end
|
|
318
318
|
|
|
319
319
|
uri = @poll_uri
|
|
@@ -119,6 +119,15 @@ module LaunchDarkly
|
|
|
119
119
|
raise NotImplementedError, "#{self.class} must implement #set_diagnostic_accumulator"
|
|
120
120
|
end
|
|
121
121
|
|
|
122
|
+
#
|
|
123
|
+
# Returns the ID of the environment the SDK is connected to, if LaunchDarkly has reported one.
|
|
124
|
+
#
|
|
125
|
+
# @return [String, nil]
|
|
126
|
+
#
|
|
127
|
+
def environment_id
|
|
128
|
+
raise NotImplementedError, "#{self.class} must implement #environment_id"
|
|
129
|
+
end
|
|
130
|
+
|
|
122
131
|
#
|
|
123
132
|
# Represents the availability of data in the SDK.
|
|
124
133
|
#
|
|
@@ -70,17 +70,27 @@ module LaunchDarkly
|
|
|
70
70
|
attr_reader :default_value
|
|
71
71
|
attr_reader :method
|
|
72
72
|
|
|
73
|
+
#
|
|
74
|
+
# The ID of the LaunchDarkly environment the SDK is connected to, if known. This is only available once the
|
|
75
|
+
# SDK has received data from LaunchDarkly.
|
|
76
|
+
#
|
|
77
|
+
# @return [String, nil]
|
|
78
|
+
#
|
|
79
|
+
attr_reader :environment_id
|
|
80
|
+
|
|
73
81
|
#
|
|
74
82
|
# @param key [String]
|
|
75
83
|
# @param context [LaunchDarkly::LDContext]
|
|
76
84
|
# @param default_value [any]
|
|
77
85
|
# @param method [Symbol]
|
|
86
|
+
# @param environment_id [String, nil]
|
|
78
87
|
#
|
|
79
|
-
def initialize(key, context, default_value, method)
|
|
88
|
+
def initialize(key, context, default_value, method, environment_id = nil)
|
|
80
89
|
@key = key
|
|
81
90
|
@context = context
|
|
82
91
|
@default_value = default_value
|
|
83
92
|
@method = method
|
|
93
|
+
@environment_id = environment_id
|
|
84
94
|
end
|
|
85
95
|
end
|
|
86
96
|
end
|
data/lib/ldclient-rb/ldclient.rb
CHANGED
|
@@ -421,7 +421,7 @@ module LaunchDarkly
|
|
|
421
421
|
# Hooks can be added and we want to ensure all correct stages for a given hook execute. For example, we do not
|
|
422
422
|
# want to trigger the after_evaluation method without also triggering the before_evaluation method.
|
|
423
423
|
hooks = @hooks.dup
|
|
424
|
-
evaluation_series_context = Interfaces::Hooks::EvaluationSeriesContext.new(key, context, default, method)
|
|
424
|
+
evaluation_series_context = Interfaces::Hooks::EvaluationSeriesContext.new(key, context, default, method, @data_system.environment_id)
|
|
425
425
|
|
|
426
426
|
[hooks, evaluation_series_context]
|
|
427
427
|
end
|
data/lib/ldclient-rb/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
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.15.0
|
|
5
5
|
platform: java
|
|
6
6
|
authors:
|
|
7
7
|
- LaunchDarkly
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-08-20 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: aws-sdk-dynamodb
|