Skip to content

Commit

Permalink
Account for traffic allocation on all flags (#185)
Browse files Browse the repository at this point in the history
  • Loading branch information
keelerm84 authored Feb 3, 2022
1 parent 275b005 commit 32e74ed
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 51 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
- when:
condition: <<parameters.jruby>>
steps:
- run: gem install jruby-openssl # required by bundler, no effect on Ruby MRI
- run: gem install jruby-openssl -v 0.11.0 # required by bundler, no effect on Ruby MRI
- run: apt-get update -y && apt-get install -y build-essential
- when:
condition:
Expand Down
35 changes: 23 additions & 12 deletions lib/ldclient-rb/flags_state.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,32 @@ def initialize(valid)

# Used internally to build the state map.
# @private
def add_flag(flag, value, variation, reason = nil, details_only_if_tracked = false)
key = flag[:key]
@flag_values[key] = value
def add_flag(flag_state, with_reasons, details_only_if_tracked)
key = flag_state[:key]
@flag_values[key] = flag_state[:value]
meta = {}
with_details = !details_only_if_tracked || flag[:trackEvents]
if !with_details && flag[:debugEventsUntilDate]
with_details = flag[:debugEventsUntilDate] > Impl::Util::current_time_millis

omit_details = false
if details_only_if_tracked
if !flag_state[:trackEvents] && !flag_state[:trackReason] && !(flag_state[:debugEventsUntilDate] && flag_state[:debugEventsUntilDate] > Impl::Util::current_time_millis)
omit_details = true
end
end

reason = (!with_reasons and !flag_state[:trackReason]) ? nil : flag_state[:reason]

if !reason.nil? && !omit_details
meta[:reason] = reason
end
if with_details
meta[:version] = flag[:version]
meta[:reason] = reason if !reason.nil?

if !omit_details
meta[:version] = flag_state[:version]
end
meta[:variation] = variation if !variation.nil?
meta[:trackEvents] = true if flag[:trackEvents]
meta[:debugEventsUntilDate] = flag[:debugEventsUntilDate] if flag[:debugEventsUntilDate]

meta[:variation] = flag_state[:variation] if !flag_state[:variation].nil?
meta[:trackEvents] = true if flag_state[:trackEvents]
meta[:trackReason] = true if flag_state[:trackReason]
meta[:debugEventsUntilDate] = flag_state[:debugEventsUntilDate] if flag_state[:debugEventsUntilDate]
@flag_metadata[key] = meta
end

Expand Down
21 changes: 9 additions & 12 deletions lib/ldclient-rb/impl/event_factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def initialize(with_reasons)
end

def new_eval_event(flag, user, detail, default_value, prereq_of_flag = nil)
add_experiment_data = is_experiment(flag, detail.reason)
add_experiment_data = self.class.is_experiment(flag, detail.reason)
e = {
kind: 'feature',
key: flag[:key],
Expand Down Expand Up @@ -91,17 +91,7 @@ def new_custom_event(event_name, user, data, metric_value)
e
end

private

def context_to_context_kind(user)
if !user.nil? && user[:anonymous]
return "anonymousUser"
else
return "user"
end
end

def is_experiment(flag, reason)
def self.is_experiment(flag, reason)
return false if !reason

if reason.in_experiment
Expand All @@ -121,6 +111,13 @@ def is_experiment(flag, reason)
false
end

private def context_to_context_kind(user)
if !user.nil? && user[:anonymous]
return "anonymousUser"
else
return "user"
end
end
end
end
end
21 changes: 16 additions & 5 deletions lib/ldclient-rb/ldclient.rb
Original file line number Diff line number Diff line change
Expand Up @@ -368,14 +368,25 @@ def all_flags_state(user, options={})
next
end
begin
result = @evaluator.evaluate(f, user, @event_factory_default)
state.add_flag(f, result.detail.value, result.detail.variation_index, with_reasons ? result.detail.reason : nil,
details_only_if_tracked)
detail = @evaluator.evaluate(f, user, @event_factory_default).detail
rescue => exn
detail = EvaluationDetail.new(nil, nil, EvaluationReason::error(EvaluationReason::ERROR_EXCEPTION))
Util.log_exception(@config.logger, "Error evaluating flag \"#{k}\" in all_flags_state", exn)
state.add_flag(f, nil, nil, with_reasons ? EvaluationReason::error(EvaluationReason::ERROR_EXCEPTION) : nil,
details_only_if_tracked)
end

requires_experiment_data = EventFactory.is_experiment(f, detail.reason)
flag_state = {
key: f[:key],
value: detail.value,
variation: detail.variation_index,
reason: detail.reason,
version: f[:version],
trackEvents: f[:trackEvents] || requires_experiment_data,
trackReason: requires_experiment_data,
debugEventsUntilDate: f[:debugEventsUntilDate],
}

state.add_flag(flag_state, with_reasons, details_only_if_tracked)
end

state
Expand Down
42 changes: 21 additions & 21 deletions spec/flags_state_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

it "can get flag value" do
state = subject.new(true)
flag = { key: 'key' }
state.add_flag(flag, 'value', 1)
flag_state = { key: 'key', value: 'value', variation: 1, reason: LaunchDarkly::EvaluationReason.fallthrough(false) }
state.add_flag(flag_state, false, false)

expect(state.flag_value('key')).to eq 'value'
end
Expand All @@ -20,21 +20,21 @@

it "can be converted to values map" do
state = subject.new(true)
flag1 = { key: 'key1' }
flag2 = { key: 'key2' }
state.add_flag(flag1, 'value1', 0)
state.add_flag(flag2, 'value2', 1)
flag_state1 = { key: 'key1', value: 'value1', variation: 0, reason: LaunchDarkly::EvaluationReason.fallthrough(false) }
flag_state2 = { key: 'key2', value: 'value2', variation: 1, reason: LaunchDarkly::EvaluationReason.fallthrough(false) }
state.add_flag(flag_state1, false, false)
state.add_flag(flag_state2, false, false)

expect(state.values_map).to eq({ 'key1' => 'value1', 'key2' => 'value2' })
end

it "can be converted to JSON structure" do
state = subject.new(true)
flag1 = { key: "key1", version: 100, offVariation: 0, variations: [ 'value1' ], trackEvents: false }
flag2 = { key: "key2", version: 200, offVariation: 1, variations: [ 'x', 'value2' ], trackEvents: true, debugEventsUntilDate: 1000 }
state.add_flag(flag1, 'value1', 0)
state.add_flag(flag2, 'value2', 1)
flag_state1 = { key: "key1", version: 100, trackEvents: false, value: 'value1', variation: 0, reason: LaunchDarkly::EvaluationReason.fallthrough(false) }
flag_state2 = { key: "key2", version: 200, trackEvents: true, debugEventsUntilDate: 1000, value: 'value2', variation: 1, reason: LaunchDarkly::EvaluationReason.fallthrough(false) }
state.add_flag(flag_state1, false, false)
state.add_flag(flag_state2, false, false)

result = state.as_json
expect(result).to eq({
'key1' => 'value1',
Expand All @@ -57,23 +57,23 @@

it "can be converted to JSON string" do
state = subject.new(true)
flag1 = { key: "key1", version: 100, offVariation: 0, variations: [ 'value1' ], trackEvents: false }
flag2 = { key: "key2", version: 200, offVariation: 1, variations: [ 'x', 'value2' ], trackEvents: true, debugEventsUntilDate: 1000 }
state.add_flag(flag1, 'value1', 0)
state.add_flag(flag2, 'value2', 1)
flag_state1 = { key: "key1", version: 100, trackEvents: false, value: 'value1', variation: 0, reason: LaunchDarkly::EvaluationReason.fallthrough(false) }
flag_state2 = { key: "key2", version: 200, trackEvents: true, debugEventsUntilDate: 1000, value: 'value2', variation: 1, reason: LaunchDarkly::EvaluationReason.fallthrough(false) }
state.add_flag(flag_state1, false, false)
state.add_flag(flag_state2, false, false)

object = state.as_json
str = state.to_json
expect(object.to_json).to eq(str)
end

it "uses our custom serializer with JSON.generate" do
state = subject.new(true)
flag1 = { key: "key1", version: 100, offVariation: 0, variations: [ 'value1' ], trackEvents: false }
flag2 = { key: "key2", version: 200, offVariation: 1, variations: [ 'x', 'value2' ], trackEvents: true, debugEventsUntilDate: 1000 }
state.add_flag(flag1, 'value1', 0)
state.add_flag(flag2, 'value2', 1)
flag_state1 = { key: "key1", version: 100, trackEvents: false, value: 'value1', variation: 0, reason: LaunchDarkly::EvaluationReason.fallthrough(false) }
flag_state2 = { key: "key2", version: 200, trackEvents: true, debugEventsUntilDate: 1000, value: 'value2', variation: 1, reason: LaunchDarkly::EvaluationReason.fallthrough(false) }
state.add_flag(flag_state1, false, false)
state.add_flag(flag_state2, false, false)

stringFromToJson = state.to_json
stringFromGenerate = JSON.generate(state)
expect(stringFromGenerate).to eq(stringFromToJson)
Expand Down

0 comments on commit 32e74ed

Please sign in to comment.