ldclient-rb 5.5.4 → 5.5.5
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/CHANGELOG.md +4 -0
- data/lib/ldclient-rb/evaluation.rb +7 -1
- data/lib/ldclient-rb/events.rb +14 -6
- data/lib/ldclient-rb/ldclient.rb +2 -13
- data/lib/ldclient-rb/util.rb +15 -0
- data/lib/ldclient-rb/version.rb +1 -1
- data/spec/evaluation_spec.rb +19 -0
- data/spec/events_spec.rb +65 -0
- data/spec/ldclient_spec.rb +0 -26
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '07628a76356f0beddcd2faffb3a50af652d51fcb'
|
4
|
+
data.tar.gz: e0f6cedf0d5c6b6c3496e4a234307fa9bd904b4e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 978e957445f2669f9c59f3e4a1af9bc7b603d272e6defea501735be9a8db2024148a2570d7c5e81dfc70d51115cf08f76447fa855c3d6be1912c94a6b5c58264
|
7
|
+
data.tar.gz: 768fc857cefdb5764bbecd35378320c99770058fc176facc5cbc47e8921ccdc80b5a64f699a83974cefde3c47e4a3225824f4149ebc08c900696635433f94775
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,10 @@
|
|
2
2
|
|
3
3
|
All notable changes to the LaunchDarkly Ruby SDK will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org).
|
4
4
|
|
5
|
+
## [5.5.5] - 2019-03-28
|
6
|
+
### Fixed:
|
7
|
+
- Setting user attributes to non-string values when a string was expected would cause analytics events not to be processed. Also, in the case of the `secondary` attribute, this could cause evaluations to fail for a flag with a percentage rollout. The SDK will now convert attribute values to strings as needed. ([#131](https://github.com/launchdarkly/ruby-client/issues/131))
|
8
|
+
|
5
9
|
## [5.5.4] - 2019-03-29
|
6
10
|
### Fixed:
|
7
11
|
- Fixed a missing `require` that could sometimes cause a `NameError` to be thrown when starting the client, depending on what other gems were installed. This bug was introduced in version 5.5.3. ([#129](https://github.com/launchdarkly/ruby-client/issues/129))
|
@@ -189,6 +189,10 @@ module LaunchDarkly
|
|
189
189
|
# Used internally to hold an evaluation result and the events that were generated from prerequisites.
|
190
190
|
EvalResult = Struct.new(:detail, :events)
|
191
191
|
|
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
|
+
|
192
196
|
def error_result(errorKind, value = nil)
|
193
197
|
EvaluationDetail.new(value, nil, { kind: 'ERROR', errorKind: errorKind })
|
194
198
|
end
|
@@ -200,8 +204,10 @@ module LaunchDarkly
|
|
200
204
|
return EvalResult.new(error_result('USER_NOT_SPECIFIED'), [])
|
201
205
|
end
|
202
206
|
|
207
|
+
sanitized_user = Util.stringify_attrs(user, USER_ATTRS_TO_STRINGIFY_FOR_EVALUATION)
|
208
|
+
|
203
209
|
events = []
|
204
|
-
detail = eval_internal(flag,
|
210
|
+
detail = eval_internal(flag, sanitized_user, store, events, logger)
|
205
211
|
return EvalResult.new(detail, events)
|
206
212
|
end
|
207
213
|
|
data/lib/ldclient-rb/events.rb
CHANGED
@@ -7,9 +7,12 @@ require "time"
|
|
7
7
|
module LaunchDarkly
|
8
8
|
MAX_FLUSH_WORKERS = 5
|
9
9
|
CURRENT_SCHEMA_VERSION = 3
|
10
|
+
USER_ATTRS_TO_STRINGIFY_FOR_EVENTS = [ :key, :secondary, :ip, :country, :email, :firstName, :lastName,
|
11
|
+
:avatar, :name ]
|
10
12
|
|
11
13
|
private_constant :MAX_FLUSH_WORKERS
|
12
14
|
private_constant :CURRENT_SCHEMA_VERSION
|
15
|
+
private_constant :USER_ATTRS_TO_STRINGIFY_FOR_EVENTS
|
13
16
|
|
14
17
|
# @private
|
15
18
|
class NullEventProcessor
|
@@ -219,7 +222,7 @@ module LaunchDarkly
|
|
219
222
|
if user.nil? || !user.has_key?(:key)
|
220
223
|
true
|
221
224
|
else
|
222
|
-
@user_keys.add(user[:key])
|
225
|
+
@user_keys.add(user[:key].to_s)
|
223
226
|
end
|
224
227
|
end
|
225
228
|
|
@@ -371,6 +374,11 @@ module LaunchDarkly
|
|
371
374
|
|
372
375
|
private
|
373
376
|
|
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
|
+
|
374
382
|
def make_output_event(event)
|
375
383
|
case event[:kind]
|
376
384
|
when "feature"
|
@@ -386,7 +394,7 @@ module LaunchDarkly
|
|
386
394
|
out[:version] = event[:version] if event.has_key?(:version)
|
387
395
|
out[:prereqOf] = event[:prereqOf] if event.has_key?(:prereqOf)
|
388
396
|
if @inline_users || is_debug
|
389
|
-
out[:user] =
|
397
|
+
out[:user] = process_user(event)
|
390
398
|
else
|
391
399
|
out[:userKey] = event[:user].nil? ? nil : event[:user][:key]
|
392
400
|
end
|
@@ -396,8 +404,8 @@ module LaunchDarkly
|
|
396
404
|
{
|
397
405
|
kind: "identify",
|
398
406
|
creationDate: event[:creationDate],
|
399
|
-
key: event[:user].nil? ? nil : event[:user][:key],
|
400
|
-
user:
|
407
|
+
key: event[:user].nil? ? nil : event[:user][:key].to_s,
|
408
|
+
user: process_user(event)
|
401
409
|
}
|
402
410
|
when "custom"
|
403
411
|
out = {
|
@@ -407,7 +415,7 @@ module LaunchDarkly
|
|
407
415
|
}
|
408
416
|
out[:data] = event[:data] if event.has_key?(:data)
|
409
417
|
if @inline_users
|
410
|
-
out[:user] =
|
418
|
+
out[:user] = process_user(event)
|
411
419
|
else
|
412
420
|
out[:userKey] = event[:user].nil? ? nil : event[:user][:key]
|
413
421
|
end
|
@@ -416,7 +424,7 @@ module LaunchDarkly
|
|
416
424
|
{
|
417
425
|
kind: "index",
|
418
426
|
creationDate: event[:creationDate],
|
419
|
-
user:
|
427
|
+
user: process_user(event)
|
420
428
|
}
|
421
429
|
else
|
422
430
|
event
|
data/lib/ldclient-rb/ldclient.rb
CHANGED
@@ -215,7 +215,6 @@ module LaunchDarkly
|
|
215
215
|
@config.logger.warn("Identify called with nil user or nil user key!")
|
216
216
|
return
|
217
217
|
end
|
218
|
-
sanitize_user(user)
|
219
218
|
@event_processor.add_event(kind: "identify", key: user[:key], user: user)
|
220
219
|
end
|
221
220
|
|
@@ -237,7 +236,6 @@ module LaunchDarkly
|
|
237
236
|
@config.logger.warn("Track called with nil user or nil user key!")
|
238
237
|
return
|
239
238
|
end
|
240
|
-
sanitize_user(user)
|
241
239
|
@event_processor.add_event(kind: "custom", key: event_name, user: user, data: data)
|
242
240
|
end
|
243
241
|
|
@@ -280,8 +278,6 @@ module LaunchDarkly
|
|
280
278
|
return FeatureFlagsState.new(false)
|
281
279
|
end
|
282
280
|
|
283
|
-
sanitize_user(user)
|
284
|
-
|
285
281
|
begin
|
286
282
|
features = @store.all(FEATURES)
|
287
283
|
rescue => exn
|
@@ -353,7 +349,6 @@ module LaunchDarkly
|
|
353
349
|
end
|
354
350
|
end
|
355
351
|
|
356
|
-
sanitize_user(user) if !user.nil?
|
357
352
|
feature = @store.get(FEATURES, key)
|
358
353
|
|
359
354
|
if feature.nil?
|
@@ -367,12 +362,12 @@ module LaunchDarkly
|
|
367
362
|
unless user
|
368
363
|
@config.logger.error { "[LDClient] Must specify user" }
|
369
364
|
detail = error_result('USER_NOT_SPECIFIED', default)
|
370
|
-
@event_processor.add_event(make_feature_event(feature,
|
365
|
+
@event_processor.add_event(make_feature_event(feature, nil, detail, default, include_reasons_in_events))
|
371
366
|
return detail
|
372
367
|
end
|
373
368
|
|
374
369
|
begin
|
375
|
-
res = evaluate(feature, user, @store, @config.logger)
|
370
|
+
res = evaluate(feature, user, @store, @config.logger) # note, evaluate will do its own sanitization
|
376
371
|
if !res.events.nil?
|
377
372
|
res.events.each do |event|
|
378
373
|
@event_processor.add_event(event)
|
@@ -392,12 +387,6 @@ module LaunchDarkly
|
|
392
387
|
end
|
393
388
|
end
|
394
389
|
|
395
|
-
def sanitize_user(user)
|
396
|
-
if user[:key]
|
397
|
-
user[:key] = user[:key].to_s
|
398
|
-
end
|
399
|
-
end
|
400
|
-
|
401
390
|
def make_feature_event(flag, user, detail, default, with_reasons)
|
402
391
|
{
|
403
392
|
kind: "feature",
|
data/lib/ldclient-rb/util.rb
CHANGED
@@ -4,6 +4,21 @@ require "uri"
|
|
4
4
|
module LaunchDarkly
|
5
5
|
# @private
|
6
6
|
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
|
+
|
7
22
|
def self.new_http_client(uri_s, config)
|
8
23
|
uri = URI(uri_s)
|
9
24
|
client = Net::HTTP.new(uri.hostname, uri.port)
|
data/lib/ldclient-rb/version.rb
CHANGED
data/spec/evaluation_spec.rb
CHANGED
@@ -359,6 +359,25 @@ describe LaunchDarkly::Evaluation do
|
|
359
359
|
expect(result.detail).to eq(detail)
|
360
360
|
expect(result.events).to eq([])
|
361
361
|
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
|
362
381
|
end
|
363
382
|
|
364
383
|
describe "clause" do
|
data/spec/events_spec.rb
CHANGED
@@ -9,6 +9,10 @@ describe LaunchDarkly::EventProcessor do
|
|
9
9
|
let(:hc) { FakeHttpClient.new }
|
10
10
|
let(:user) { { key: "userkey", name: "Red" } }
|
11
11
|
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 } } }
|
12
16
|
|
13
17
|
after(:each) do
|
14
18
|
if !@ep.nil?
|
@@ -40,6 +44,21 @@ describe LaunchDarkly::EventProcessor do
|
|
40
44
|
})
|
41
45
|
end
|
42
46
|
|
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
|
+
|
43
62
|
it "queues individual feature event with index event" do
|
44
63
|
@ep = subject.new("sdk_key", default_config, hc)
|
45
64
|
flag = { key: "flagkey", version: 11 }
|
@@ -75,6 +94,23 @@ describe LaunchDarkly::EventProcessor do
|
|
75
94
|
)
|
76
95
|
end
|
77
96
|
|
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
|
+
|
78
114
|
it "can include inline user in feature event" do
|
79
115
|
config = LaunchDarkly::Config.new(inline_users_in_events: true)
|
80
116
|
@ep = subject.new("sdk_key", config, hc)
|
@@ -92,6 +128,23 @@ describe LaunchDarkly::EventProcessor do
|
|
92
128
|
)
|
93
129
|
end
|
94
130
|
|
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
|
+
|
95
148
|
it "filters user in feature event" do
|
96
149
|
config = LaunchDarkly::Config.new(all_attributes_private: true, inline_users_in_events: true)
|
97
150
|
@ep = subject.new("sdk_key", config, hc)
|
@@ -323,6 +376,18 @@ describe LaunchDarkly::EventProcessor do
|
|
323
376
|
)
|
324
377
|
end
|
325
378
|
|
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
|
+
|
326
391
|
it "does a final flush when shutting down" do
|
327
392
|
@ep = subject.new("sdk_key", default_config, hc)
|
328
393
|
e = { kind: "identify", key: user[:key], user: user }
|
data/spec/ldclient_spec.rb
CHANGED
@@ -25,22 +25,6 @@ describe LaunchDarkly::LDClient do
|
|
25
25
|
}
|
26
26
|
}
|
27
27
|
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
|
44
28
|
let(:user_without_key) do
|
45
29
|
{ name: "Keyless Joe" }
|
46
30
|
end
|
@@ -354,11 +338,6 @@ describe LaunchDarkly::LDClient do
|
|
354
338
|
client.track("custom_event_name", user, 42)
|
355
339
|
end
|
356
340
|
|
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
|
-
|
362
341
|
it "does not send an event, and logs a warning, if user is nil" do
|
363
342
|
expect(event_processor).not_to receive(:add_event)
|
364
343
|
expect(logger).to receive(:warn)
|
@@ -378,11 +357,6 @@ describe LaunchDarkly::LDClient do
|
|
378
357
|
client.identify(user)
|
379
358
|
end
|
380
359
|
|
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
|
-
|
386
360
|
it "does not send an event, and logs a warning, if user is nil" do
|
387
361
|
expect(event_processor).not_to receive(:add_event)
|
388
362
|
expect(logger).to receive(:warn)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ldclient-rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.5.
|
4
|
+
version: 5.5.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- LaunchDarkly
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-03-
|
11
|
+
date: 2019-03-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk-dynamodb
|