Skip to content

Commit 5f71781

Browse files
authored
prepare 5.5.5 release (#132)
1 parent 29a5b7f commit 5f71781

File tree

7 files changed

+122
-46
lines changed

7 files changed

+122
-46
lines changed

lib/ldclient-rb/evaluation.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,10 @@ def self.comparator(converter)
189189
# Used internally to hold an evaluation result and the events that were generated from prerequisites.
190190
EvalResult = Struct.new(:detail, :events)
191191

192+
USER_ATTRS_TO_STRINGIFY_FOR_EVALUATION = [ :key, :secondary ]
193+
# Currently we are not stringifying the rest of the built-in attributes prior to evaluation, only for events.
194+
# This is because it could affect evaluation results for existing users (ch35206).
195+
192196
def error_result(errorKind, value = nil)
193197
EvaluationDetail.new(value, nil, { kind: 'ERROR', errorKind: errorKind })
194198
end
@@ -200,8 +204,10 @@ def evaluate(flag, user, store, logger)
200204
return EvalResult.new(error_result('USER_NOT_SPECIFIED'), [])
201205
end
202206

207+
sanitized_user = Util.stringify_attrs(user, USER_ATTRS_TO_STRINGIFY_FOR_EVALUATION)
208+
203209
events = []
204-
detail = eval_internal(flag, user, store, events, logger)
210+
detail = eval_internal(flag, sanitized_user, store, events, logger)
205211
return EvalResult.new(detail, events)
206212
end
207213

lib/ldclient-rb/events.rb

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@
77
module LaunchDarkly
88
MAX_FLUSH_WORKERS = 5
99
CURRENT_SCHEMA_VERSION = 3
10+
USER_ATTRS_TO_STRINGIFY_FOR_EVENTS = [ :key, :secondary, :ip, :country, :email, :firstName, :lastName,
11+
:avatar, :name ]
1012

1113
private_constant :MAX_FLUSH_WORKERS
1214
private_constant :CURRENT_SCHEMA_VERSION
15+
private_constant :USER_ATTRS_TO_STRINGIFY_FOR_EVENTS
1316

1417
# @private
1518
class NullEventProcessor
@@ -219,7 +222,7 @@ def notice_user(user)
219222
if user.nil? || !user.has_key?(:key)
220223
true
221224
else
222-
@user_keys.add(user[:key])
225+
@user_keys.add(user[:key].to_s)
223226
end
224227
end
225228

@@ -371,6 +374,11 @@ def make_output_events(events, summary)
371374

372375
private
373376

377+
def process_user(event)
378+
filtered = @user_filter.transform_user_props(event[:user])
379+
Util.stringify_attrs(filtered, USER_ATTRS_TO_STRINGIFY_FOR_EVENTS)
380+
end
381+
374382
def make_output_event(event)
375383
case event[:kind]
376384
when "feature"
@@ -386,7 +394,7 @@ def make_output_event(event)
386394
out[:version] = event[:version] if event.has_key?(:version)
387395
out[:prereqOf] = event[:prereqOf] if event.has_key?(:prereqOf)
388396
if @inline_users || is_debug
389-
out[:user] = @user_filter.transform_user_props(event[:user])
397+
out[:user] = process_user(event)
390398
else
391399
out[:userKey] = event[:user].nil? ? nil : event[:user][:key]
392400
end
@@ -396,8 +404,8 @@ def make_output_event(event)
396404
{
397405
kind: "identify",
398406
creationDate: event[:creationDate],
399-
key: event[:user].nil? ? nil : event[:user][:key],
400-
user: @user_filter.transform_user_props(event[:user])
407+
key: event[:user].nil? ? nil : event[:user][:key].to_s,
408+
user: process_user(event)
401409
}
402410
when "custom"
403411
out = {
@@ -407,7 +415,7 @@ def make_output_event(event)
407415
}
408416
out[:data] = event[:data] if event.has_key?(:data)
409417
if @inline_users
410-
out[:user] = @user_filter.transform_user_props(event[:user])
418+
out[:user] = process_user(event)
411419
else
412420
out[:userKey] = event[:user].nil? ? nil : event[:user][:key]
413421
end
@@ -416,7 +424,7 @@ def make_output_event(event)
416424
{
417425
kind: "index",
418426
creationDate: event[:creationDate],
419-
user: @user_filter.transform_user_props(event[:user])
427+
user: process_user(event)
420428
}
421429
else
422430
event

lib/ldclient-rb/ldclient.rb

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,6 @@ def identify(user)
215215
@config.logger.warn("Identify called with nil user or nil user key!")
216216
return
217217
end
218-
sanitize_user(user)
219218
@event_processor.add_event(kind: "identify", key: user[:key], user: user)
220219
end
221220

@@ -237,7 +236,6 @@ def track(event_name, user, data)
237236
@config.logger.warn("Track called with nil user or nil user key!")
238237
return
239238
end
240-
sanitize_user(user)
241239
@event_processor.add_event(kind: "custom", key: event_name, user: user, data: data)
242240
end
243241

@@ -280,8 +278,6 @@ def all_flags_state(user, options={})
280278
return FeatureFlagsState.new(false)
281279
end
282280

283-
sanitize_user(user)
284-
285281
begin
286282
features = @store.all(FEATURES)
287283
rescue => exn
@@ -353,7 +349,6 @@ def evaluate_internal(key, user, default, include_reasons_in_events)
353349
end
354350
end
355351

356-
sanitize_user(user) if !user.nil?
357352
feature = @store.get(FEATURES, key)
358353

359354
if feature.nil?
@@ -367,12 +362,12 @@ def evaluate_internal(key, user, default, include_reasons_in_events)
367362
unless user
368363
@config.logger.error { "[LDClient] Must specify user" }
369364
detail = error_result('USER_NOT_SPECIFIED', default)
370-
@event_processor.add_event(make_feature_event(feature, user, detail, default, include_reasons_in_events))
365+
@event_processor.add_event(make_feature_event(feature, nil, detail, default, include_reasons_in_events))
371366
return detail
372367
end
373368

374369
begin
375-
res = evaluate(feature, user, @store, @config.logger)
370+
res = evaluate(feature, user, @store, @config.logger) # note, evaluate will do its own sanitization
376371
if !res.events.nil?
377372
res.events.each do |event|
378373
@event_processor.add_event(event)
@@ -392,12 +387,6 @@ def evaluate_internal(key, user, default, include_reasons_in_events)
392387
end
393388
end
394389

395-
def sanitize_user(user)
396-
if user[:key]
397-
user[:key] = user[:key].to_s
398-
end
399-
end
400-
401390
def make_feature_event(flag, user, detail, default, with_reasons)
402391
{
403392
kind: "feature",

lib/ldclient-rb/util.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,21 @@
44
module LaunchDarkly
55
# @private
66
module Util
7+
def self.stringify_attrs(hash, attrs)
8+
return hash if hash.nil?
9+
ret = hash
10+
changed = false
11+
attrs.each do |attr|
12+
value = hash[attr]
13+
if !value.nil? && !value.is_a?(String)
14+
ret = hash.clone if !changed
15+
ret[attr] = value.to_s
16+
changed = true
17+
end
18+
end
19+
ret
20+
end
21+
722
def self.new_http_client(uri_s, config)
823
uri = URI(uri_s)
924
client = Net::HTTP.new(uri.hostname, uri.port)

spec/evaluation_spec.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,25 @@ def boolean_flag_with_clauses(clauses)
359359
expect(result.detail).to eq(detail)
360360
expect(result.events).to eq([])
361361
end
362+
363+
it "coerces user key to a string for evaluation" do
364+
clause = { attribute: 'key', op: 'in', values: ['999'] }
365+
flag = boolean_flag_with_clauses([clause])
366+
user = { key: 999 }
367+
result = evaluate(flag, user, features, logger)
368+
expect(result.detail.value).to eq(true)
369+
end
370+
371+
it "coerces secondary key to a string for evaluation" do
372+
# We can't really verify that the rollout calculation works correctly, but we can at least
373+
# make sure it doesn't error out if there's a non-string secondary value (ch35189)
374+
rule = { id: 'ruleid', clauses: [{ attribute: 'key', op: 'in', values: ['userkey'] }],
375+
rollout: { salt: '', variations: [ { weight: 100000, variation: 1 } ] } }
376+
flag = boolean_flag_with_rules([rule])
377+
user = { key: "userkey", secondary: 999 }
378+
result = evaluate(flag, user, features, logger)
379+
expect(result.detail.reason).to eq({ kind: 'RULE_MATCH', ruleIndex: 0, ruleId: 'ruleid'})
380+
end
362381
end
363382

364383
describe "clause" do

spec/events_spec.rb

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
let(:hc) { FakeHttpClient.new }
1010
let(:user) { { key: "userkey", name: "Red" } }
1111
let(:filtered_user) { { key: "userkey", privateAttrs: [ "name" ] } }
12+
let(:numeric_user) { { key: 1, secondary: 2, ip: 3, country: 4, email: 5, firstName: 6, lastName: 7,
13+
avatar: 8, name: 9, anonymous: false, custom: { age: 99 } } }
14+
let(:stringified_numeric_user) { { key: '1', secondary: '2', ip: '3', country: '4', email: '5', firstName: '6',
15+
lastName: '7', avatar: '8', name: '9', anonymous: false, custom: { age: 99 } } }
1216

1317
after(:each) do
1418
if !@ep.nil?
@@ -40,6 +44,21 @@
4044
})
4145
end
4246

47+
it "stringifies built-in user attributes in identify event" do
48+
@ep = subject.new("sdk_key", default_config, hc)
49+
flag = { key: "flagkey", version: 11 }
50+
e = { kind: "identify", key: numeric_user[:key], user: numeric_user }
51+
@ep.add_event(e)
52+
53+
output = flush_and_get_events
54+
expect(output).to contain_exactly(
55+
kind: "identify",
56+
key: numeric_user[:key].to_s,
57+
creationDate: e[:creationDate],
58+
user: stringified_numeric_user
59+
)
60+
end
61+
4362
it "queues individual feature event with index event" do
4463
@ep = subject.new("sdk_key", default_config, hc)
4564
flag = { key: "flagkey", version: 11 }
@@ -75,6 +94,23 @@
7594
)
7695
end
7796

97+
it "stringifies built-in user attributes in index event" do
98+
@ep = subject.new("sdk_key", default_config, hc)
99+
flag = { key: "flagkey", version: 11 }
100+
fe = {
101+
kind: "feature", key: "flagkey", version: 11, user: numeric_user,
102+
variation: 1, value: "value", trackEvents: true
103+
}
104+
@ep.add_event(fe)
105+
106+
output = flush_and_get_events
107+
expect(output).to contain_exactly(
108+
eq(index_event(fe, stringified_numeric_user)),
109+
eq(feature_event(fe, flag, false, nil)),
110+
include(:kind => "summary")
111+
)
112+
end
113+
78114
it "can include inline user in feature event" do
79115
config = LaunchDarkly::Config.new(inline_users_in_events: true)
80116
@ep = subject.new("sdk_key", config, hc)
@@ -92,6 +128,23 @@
92128
)
93129
end
94130

131+
it "stringifies built-in user attributes in feature event" do
132+
config = LaunchDarkly::Config.new(inline_users_in_events: true)
133+
@ep = subject.new("sdk_key", config, hc)
134+
flag = { key: "flagkey", version: 11 }
135+
fe = {
136+
kind: "feature", key: "flagkey", version: 11, user: numeric_user,
137+
variation: 1, value: "value", trackEvents: true
138+
}
139+
@ep.add_event(fe)
140+
141+
output = flush_and_get_events
142+
expect(output).to contain_exactly(
143+
eq(feature_event(fe, flag, false, stringified_numeric_user)),
144+
include(:kind => "summary")
145+
)
146+
end
147+
95148
it "filters user in feature event" do
96149
config = LaunchDarkly::Config.new(all_attributes_private: true, inline_users_in_events: true)
97150
@ep = subject.new("sdk_key", config, hc)
@@ -323,6 +376,18 @@
323376
)
324377
end
325378

379+
it "stringifies built-in user attributes in custom event" do
380+
config = LaunchDarkly::Config.new(inline_users_in_events: true)
381+
@ep = subject.new("sdk_key", config, hc)
382+
e = { kind: "custom", key: "eventkey", user: numeric_user }
383+
@ep.add_event(e)
384+
385+
output = flush_and_get_events
386+
expect(output).to contain_exactly(
387+
eq(custom_event(e, stringified_numeric_user))
388+
)
389+
end
390+
326391
it "does a final flush when shutting down" do
327392
@ep = subject.new("sdk_key", default_config, hc)
328393
e = { kind: "identify", key: user[:key], user: user }

spec/ldclient_spec.rb

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,6 @@
2525
}
2626
}
2727
end
28-
let(:numeric_key_user) do
29-
{
30-
key: 33,
31-
custom: {
32-
groups: [ "microsoft", "google" ]
33-
}
34-
}
35-
end
36-
let(:sanitized_numeric_key_user) do
37-
{
38-
key: "33",
39-
custom: {
40-
groups: [ "microsoft", "google" ]
41-
}
42-
}
43-
end
4428
let(:user_without_key) do
4529
{ name: "Keyless Joe" }
4630
end
@@ -354,11 +338,6 @@ def event_processor
354338
client.track("custom_event_name", user, 42)
355339
end
356340

357-
it "sanitizes the user in the event" do
358-
expect(event_processor).to receive(:add_event).with(hash_including(user: sanitized_numeric_key_user))
359-
client.track("custom_event_name", numeric_key_user, nil)
360-
end
361-
362341
it "does not send an event, and logs a warning, if user is nil" do
363342
expect(event_processor).not_to receive(:add_event)
364343
expect(logger).to receive(:warn)
@@ -378,11 +357,6 @@ def event_processor
378357
client.identify(user)
379358
end
380359

381-
it "sanitizes the user in the event" do
382-
expect(event_processor).to receive(:add_event).with(hash_including(user: sanitized_numeric_key_user))
383-
client.identify(numeric_key_user)
384-
end
385-
386360
it "does not send an event, and logs a warning, if user is nil" do
387361
expect(event_processor).not_to receive(:add_event)
388362
expect(logger).to receive(:warn)

0 commit comments

Comments
 (0)