launchdarkly-server-sdk 8.15.0 → 8.15.1
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 +4 -0
- data/lib/ldclient-rb/context.rb +29 -0
- data/lib/ldclient-rb/events.rb +1 -1
- data/lib/ldclient-rb/impl/context_filter.rb +54 -4
- data/lib/ldclient-rb/impl/data_source/polling.rb +4 -1
- data/lib/ldclient-rb/reference.rb +4 -0
- 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: 06b22ffc1ba131d82969abf4c5e6b04ab80d18a5112393a41f59a3b7217e9633
|
|
4
|
+
data.tar.gz: 52e8d3a1615a43d15d9fa548db9568456409c3cbfd716f558635c7377b35987f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b632a4958bab5ad77398d87cf3b018e0fe35c37e53b5590dc330b6c395a6fe22f0fc50ef820182b197179ac9c933356bf1a718b8f5bdcd4d8b40d3daae8367b5
|
|
7
|
+
data.tar.gz: 22284f8464984ed1de431109260354d8285ae2079a47429a62e2e8fb49abb954e4d75c220cf264a9409b20bcd0ddc2cb6ca167c8ddd481bf8d769e08b5642362
|
data/lib/ldclient-rb/config.rb
CHANGED
|
@@ -262,6 +262,10 @@ module LaunchDarkly
|
|
|
262
262
|
# You can also specify the same behavior for an individual flag evaluation
|
|
263
263
|
# by providing the context object with a list of private attributes.
|
|
264
264
|
#
|
|
265
|
+
# Each entry is an attribute reference. A reference addresses attributes by
|
|
266
|
+
# symbol name, so it cannot make an attribute private if that attribute was
|
|
267
|
+
# given a string name. Refer to {LDContext} for the symbol requirement.
|
|
268
|
+
#
|
|
265
269
|
# @see https://docs.launchdarkly.com/sdk/features/user-context-config#using-private-attributes
|
|
266
270
|
#
|
|
267
271
|
# @return [Array<String>]
|
data/lib/ldclient-rb/context.rb
CHANGED
|
@@ -6,6 +6,17 @@ module LaunchDarkly
|
|
|
6
6
|
# LDContext is a collection of attributes that can be referenced in flag
|
|
7
7
|
# evaluations and analytics events.
|
|
8
8
|
#
|
|
9
|
+
# Every attribute name must be a symbol. This applies to the built-in
|
|
10
|
+
# properties, such as :key and :kind, and to custom attributes at every level
|
|
11
|
+
# of nesting. The SDK addresses attributes by symbol, so it cannot find an
|
|
12
|
+
# attribute that has a string name. Such an attribute is invisible to flag
|
|
13
|
+
# evaluation and to private attribute redaction.
|
|
14
|
+
#
|
|
15
|
+
# Take care with data that arrives as JSON. `JSON.parse` returns string keys
|
|
16
|
+
# by default, so pass `symbolize_names: true` before you build a context from
|
|
17
|
+
# it. Note that the `"name": value` form in a hash literal already produces a
|
|
18
|
+
# symbol, so a hand-written literal is safe.
|
|
19
|
+
#
|
|
9
20
|
# To create an LDContext of a single kind, such as a user, you may use
|
|
10
21
|
# {LDContext#create} or {LDContext#with_key}.
|
|
11
22
|
#
|
|
@@ -148,6 +159,9 @@ module LaunchDarkly
|
|
|
148
159
|
#
|
|
149
160
|
# Return an array of top level attribute keys (excluding built-in attributes)
|
|
150
161
|
#
|
|
162
|
+
# The keys come back in the form the caller supplied. A string key is
|
|
163
|
+
# returned as a string, even though the SDK cannot address it.
|
|
164
|
+
#
|
|
151
165
|
# @return [Array<Symbol>]
|
|
152
166
|
#
|
|
153
167
|
def get_custom_attribute_names
|
|
@@ -172,6 +186,9 @@ module LaunchDarkly
|
|
|
172
186
|
# values out of JSON objects or arrays, such as "/address/street". Use
|
|
173
187
|
# {#get_value_for_reference} for that purpose.
|
|
174
188
|
#
|
|
189
|
+
# The lookup treats the name as a symbol. An attribute that was stored under
|
|
190
|
+
# a string name is therefore not found, and the result is nil.
|
|
191
|
+
#
|
|
175
192
|
# If the value is found, the return value is the attribute value;
|
|
176
193
|
# otherwise, it is nil.
|
|
177
194
|
#
|
|
@@ -200,6 +217,10 @@ module LaunchDarkly
|
|
|
200
217
|
# Use {#individual_context} to inspect a Context for a particular kind and
|
|
201
218
|
# then get its attributes.
|
|
202
219
|
#
|
|
220
|
+
# A Reference holds its path components as symbols, so each step of the
|
|
221
|
+
# lookup matches a symbol key. A path that crosses an attribute with a
|
|
222
|
+
# string name resolves to nil.
|
|
223
|
+
#
|
|
203
224
|
# If the value is found, the return value is the attribute value;
|
|
204
225
|
# otherwise, it is nil.
|
|
205
226
|
#
|
|
@@ -440,6 +461,14 @@ module LaunchDarkly
|
|
|
440
461
|
# {https://docs.launchdarkly.com/sdk/features/user-config SDK
|
|
441
462
|
# documentation}.
|
|
442
463
|
#
|
|
464
|
+
# Every key in the hash must be a symbol, at the top level and inside any
|
|
465
|
+
# nested attribute value. Refer to the {LDContext} class documentation for
|
|
466
|
+
# why, and for the JSON.parse caveat.
|
|
467
|
+
#
|
|
468
|
+
# A string key does not make the context invalid, with two exceptions. The
|
|
469
|
+
# context requires a symbol :kind and a symbol :key, so a hash that supplies
|
|
470
|
+
# those as strings is invalid.
|
|
471
|
+
#
|
|
443
472
|
# @param data [Hash]
|
|
444
473
|
# @return [LDContext]
|
|
445
474
|
#
|
data/lib/ldclient-rb/events.rb
CHANGED
|
@@ -487,7 +487,7 @@ module LaunchDarkly
|
|
|
487
487
|
SUMMARY_KIND = 'summary'
|
|
488
488
|
|
|
489
489
|
def initialize(config)
|
|
490
|
-
@context_filter = LaunchDarkly::Impl::ContextFilter.new(config.all_attributes_private, config.private_attributes)
|
|
490
|
+
@context_filter = LaunchDarkly::Impl::ContextFilter.new(config.all_attributes_private, config.private_attributes, config.logger)
|
|
491
491
|
end
|
|
492
492
|
|
|
493
493
|
# Transforms events into the format used for event sending.
|
|
@@ -1,12 +1,17 @@
|
|
|
1
|
+
require "concurrent/atomics"
|
|
2
|
+
|
|
1
3
|
module LaunchDarkly
|
|
2
4
|
module Impl
|
|
3
5
|
class ContextFilter
|
|
4
6
|
#
|
|
5
7
|
# @param all_attributes_private [Boolean]
|
|
6
8
|
# @param private_attributes [Array<String>]
|
|
9
|
+
# @param logger [Logger, nil]
|
|
7
10
|
#
|
|
8
|
-
def initialize(all_attributes_private, private_attributes)
|
|
11
|
+
def initialize(all_attributes_private, private_attributes, logger = nil)
|
|
9
12
|
@all_attributes_private = all_attributes_private
|
|
13
|
+
@logger = logger
|
|
14
|
+
@non_symbol_name_logged = Concurrent::AtomicBoolean.new(false)
|
|
10
15
|
|
|
11
16
|
@private_attributes = []
|
|
12
17
|
private_attributes.each do |attribute|
|
|
@@ -78,9 +83,14 @@ module LaunchDarkly
|
|
|
78
83
|
end
|
|
79
84
|
|
|
80
85
|
context.get_custom_attribute_names.each do |attribute|
|
|
86
|
+
unless attribute.is_a?(Symbol)
|
|
87
|
+
log_non_symbol_name(attribute)
|
|
88
|
+
next
|
|
89
|
+
end
|
|
90
|
+
|
|
81
91
|
unless check_whole_attribute_private(Reference.create_literal(attribute), private_attributes, redacted, anonymous && redact_anonymous)
|
|
82
92
|
value = context.get_value(attribute)
|
|
83
|
-
filtered[attribute] = redact_json_value(nil, attribute, value, private_attributes, redacted)
|
|
93
|
+
filtered[attribute] = redact_json_value(nil, attribute, value, private_attributes, redacted, [])
|
|
84
94
|
end
|
|
85
95
|
end
|
|
86
96
|
|
|
@@ -122,16 +132,34 @@ module LaunchDarkly
|
|
|
122
132
|
# @param value [any]
|
|
123
133
|
# @param private_attributes [Array<Reference>]
|
|
124
134
|
# @param redacted [Array<Symbol>]
|
|
135
|
+
# @param visited [Array<Hash>]
|
|
125
136
|
# @return [any]
|
|
126
137
|
#
|
|
127
|
-
private def redact_json_value(parent_path, name, value, private_attributes, redacted)
|
|
138
|
+
private def redact_json_value(parent_path, name, value, private_attributes, redacted, visited)
|
|
128
139
|
return value unless value.is_a?(Hash)
|
|
129
140
|
|
|
130
141
|
ret = {}
|
|
131
142
|
current_path = parent_path.clone || []
|
|
132
143
|
current_path << name
|
|
133
144
|
|
|
145
|
+
# The chain of hashes from the attribute root down to this value. A
|
|
146
|
+
# nested value that points back into this chain is a cycle and is
|
|
147
|
+
# omitted.
|
|
148
|
+
#
|
|
149
|
+
# The comparison is by object identity, which keeps the check cheap.
|
|
150
|
+
# Context creation copies each top-level attribute value, so a cycle
|
|
151
|
+
# through that value appears once in the output before it is cut.
|
|
152
|
+
# Private attribute references match the paths that appear in the
|
|
153
|
+
# output. Cyclic values are not valid context data; this check only
|
|
154
|
+
# prevents unbounded recursion.
|
|
155
|
+
branch = visited + [value]
|
|
156
|
+
|
|
134
157
|
value.each do |k, v|
|
|
158
|
+
unless k.is_a?(Symbol)
|
|
159
|
+
log_non_symbol_name(k)
|
|
160
|
+
next
|
|
161
|
+
end
|
|
162
|
+
|
|
135
163
|
was_redacted = false
|
|
136
164
|
private_attributes.each do |private_attribute|
|
|
137
165
|
next unless private_attribute.depth == (current_path.count + 1)
|
|
@@ -155,12 +183,34 @@ module LaunchDarkly
|
|
|
155
183
|
end
|
|
156
184
|
|
|
157
185
|
unless was_redacted
|
|
158
|
-
|
|
186
|
+
next if v.is_a?(Hash) && branch.any? { |seen| seen.equal?(v) }
|
|
187
|
+
|
|
188
|
+
ret[k] = redact_json_value(current_path, k, v, private_attributes, redacted, branch)
|
|
159
189
|
end
|
|
160
190
|
end
|
|
161
191
|
|
|
162
192
|
ret
|
|
163
193
|
end
|
|
194
|
+
|
|
195
|
+
#
|
|
196
|
+
# Log the first attribute found with a non-symbol name. Later
|
|
197
|
+
# occurrences are not logged to prevent log spam.
|
|
198
|
+
#
|
|
199
|
+
# Attribute references cannot address non-symbol names, so these
|
|
200
|
+
# attributes cannot be evaluated or redacted. They are omitted from
|
|
201
|
+
# analytics events.
|
|
202
|
+
#
|
|
203
|
+
# @param name [any]
|
|
204
|
+
#
|
|
205
|
+
private def log_non_symbol_name(name)
|
|
206
|
+
return if @logger.nil?
|
|
207
|
+
return unless @non_symbol_name_logged.make_true
|
|
208
|
+
|
|
209
|
+
@logger.error do
|
|
210
|
+
"[LDClient] Context attributes with non-symbol names cannot be evaluated or redacted, " \
|
|
211
|
+
"so they are omitted from analytics events (first occurrence: #{name.inspect}). This message is logged once."
|
|
212
|
+
end
|
|
213
|
+
end
|
|
164
214
|
end
|
|
165
215
|
end
|
|
166
216
|
end
|
|
@@ -67,8 +67,11 @@ module LaunchDarkly
|
|
|
67
67
|
error_info
|
|
68
68
|
)
|
|
69
69
|
else
|
|
70
|
-
|
|
70
|
+
# Publish the OFF status before releasing anyone waiting on the
|
|
71
|
+
# ready event, so a client that returns from start can rely on the
|
|
72
|
+
# data source status already reflecting the failure.
|
|
71
73
|
stop_with_error_info error_info
|
|
74
|
+
@ready.set # if client was waiting on us, make it stop waiting - has no effect if already set
|
|
72
75
|
end
|
|
73
76
|
rescue StandardError => e
|
|
74
77
|
Impl::Util.log_exception(@config.logger, "Exception while polling", e)
|
|
@@ -12,6 +12,10 @@ module LaunchDarkly
|
|
|
12
12
|
# or to identify an attribute or nested value that should be considered
|
|
13
13
|
# private.
|
|
14
14
|
#
|
|
15
|
+
# A Reference holds its path components as symbols, whichever form the input
|
|
16
|
+
# string used. It therefore addresses only context attributes that have symbol
|
|
17
|
+
# names, which is the form a context requires.
|
|
18
|
+
#
|
|
15
19
|
# Parsing and validation are done at the time that the Reference is
|
|
16
20
|
# constructed. If a Reference instance was created from an invalid string, it
|
|
17
21
|
# is considered invalid and its {Reference#error} attribute will return a
|
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.15.
|
|
4
|
+
version: 8.15.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- LaunchDarkly
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-08-
|
|
11
|
+
date: 2026-08-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: aws-sdk-dynamodb
|