Skip to content

Commit

Permalink
Merge dd8adaa into 6446a64
Browse files Browse the repository at this point in the history
  • Loading branch information
sighphyre committed Dec 13, 2021
2 parents 6446a64 + dd8adaa commit b1fa033
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 20 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pull_request.yml
Expand Up @@ -31,7 +31,7 @@ jobs:
- name: Install dependencies
run: bundle install
- name: Download test cases
run: git clone --depth 5 --branch v3.3.0 https://github.com/Unleash/client-specification.git client-specification
run: git clone --depth 5 --branch v4.0.0 https://github.com/Unleash/client-specification.git client-specification
- name: rubocop
uses: reviewdog/action-rubocop@v2
with:
Expand Down
15 changes: 10 additions & 5 deletions lib/unleash/feature_toggle.rb
Expand Up @@ -40,8 +40,7 @@ def get_variant(context, fallback_variant = Unleash::FeatureToggle.disabled_vari
return Unleash::FeatureToggle.disabled_variant unless self.enabled && am_enabled?(context, true)
return Unleash::FeatureToggle.disabled_variant if sum_variant_defs_weights <= 0

variant = variant_from_override_match(context)
variant = variant_from_weights(context) if variant.nil?
variant = variant_from_override_match(context) || variant_from_weights(context, resolve_stickiness)

Unleash.toggle_metrics.increment_variant(self.name, variant.name) unless Unleash.configuration.disable_metrics
variant
Expand All @@ -53,6 +52,10 @@ def self.disabled_variant

private

def resolve_stickiness
self.variant_definitions&.map(&:stickiness)&.compact&.first || "default"
end

# only check if it is enabled, do not do metrics
def am_enabled?(context, default_result)
result =
Expand Down Expand Up @@ -85,7 +88,8 @@ def sum_variant_defs_weights
self.variant_definitions.map(&:weight).reduce(0, :+)
end

def variant_salt(context)
def variant_salt(context, stickiness = "default")
return context.get_by_name(stickiness) unless stickiness == "default"
return context.user_id unless context.user_id.to_s.empty?
return context.session_id unless context.session_id.to_s.empty?
return context.remote_address unless context.remote_address.to_s.empty?
Expand All @@ -100,8 +104,8 @@ def variant_from_override_match(context)
Unleash::Variant.new(name: variant.name, enabled: true, payload: variant.payload)
end

def variant_from_weights(context)
variant_weight = Unleash::Strategy::Util.get_normalized_number(variant_salt(context), self.name, sum_variant_defs_weights)
def variant_from_weights(context, stickiness)
variant_weight = Unleash::Strategy::Util.get_normalized_number(variant_salt(context, stickiness), self.name, sum_variant_defs_weights)
prev_weights = 0

variant_definition = self.variant_definitions
Expand Down Expand Up @@ -150,6 +154,7 @@ def initialize_variant_definitions(params)
v.fetch('name', ''),
v.fetch('weight', 0),
v.fetch('payload', nil),
v.fetch('stickiness', nil),
v.fetch('overrides', [])
)
end || []
Expand Down
10 changes: 5 additions & 5 deletions lib/unleash/strategy/flexible_rollout.rb
Expand Up @@ -38,16 +38,16 @@ def random

def resolve_stickiness(stickiness, context)
case stickiness
when 'userId'
context.user_id
when 'sessionId'
context.session_id
when 'random'
random
when 'default'
context.user_id || context.session_id || random
else
nil
begin
context.get_by_name(stickiness)
rescue KeyError
nil
end
end
end
end
Expand Down
9 changes: 5 additions & 4 deletions lib/unleash/variant_definition.rb
Expand Up @@ -2,13 +2,13 @@

module Unleash
class VariantDefinition
attr_accessor :name, :weight, :payload, :overrides
attr_accessor :name, :weight, :payload, :overrides, :stickiness

def initialize(name, weight = 0, payload = nil, overrides = [])
def initialize(name, weight = 0, payload = nil, stickiness = nil, overrides = [])
self.name = name
self.weight = weight
self.payload = payload
# self.overrides = overrides
self.stickiness = stickiness
self.overrides = (overrides || [])
.select{ |v| v.is_a?(Hash) && v.has_key?('contextName') }
.map{ |v| VariantOverride.new(v.fetch('contextName', ''), v.fetch('values', [])) } || []
Expand All @@ -19,7 +19,8 @@ def override_matches_context?(context)
end

def to_s
"<VariantDefinition: name=#{self.name},weight=#{self.weight},payload=#{self.payload},overrides=#{self.overrides}>"
"<VariantDefinition: name=#{self.name},weight=#{self.weight},payload=#{self.payload},stickiness=#{self.stickiness}" \
",overrides=#{self.overrides}>"
end
end
end
1 change: 1 addition & 0 deletions spec/unleash/client_spec.rb
Expand Up @@ -376,6 +376,7 @@
{
name: "a",
weight: 50,
stickiness: "default",
payload: {
type: "string",
value: ""
Expand Down
103 changes: 98 additions & 5 deletions spec/unleash/feature_toggle_spec.rb
Expand Up @@ -156,11 +156,13 @@
"variants" => [
{
"name" => "variant1",
"weight" => 50
"weight" => 50,
"stickiness" => "default"
},
{
"name" => "variant2",
"weight" => 50
"weight" => 50,
"stickiness" => "default"
}
],
"createdAt" => "2019-01-24T10:41:45.236Z"
Expand Down Expand Up @@ -207,15 +209,18 @@
"variants" => [
{
"name" => "variantA",
"weight" => 0
"weight" => 0,
"stickiness" => "default"
},
{
"name" => "variantB",
"weight" => 10
"weight" => 10,
"stickiness" => "default"
},
{
"name" => "variantC",
"weight" => 20
"weight" => 20,
"stickiness" => "default"
}
],
"createdAt" => "2019-01-24T10:41:45.236Z"
Expand Down Expand Up @@ -303,6 +308,7 @@
{
"name" => "variant1",
"weight" => 50,
"stickiness" => "default",
"payload" => {
"type" => "string",
"value" => "val1"
Expand All @@ -315,6 +321,7 @@
{
"name" => "variant2",
"weight" => 50,
"stickiness" => "default",
"payload" => {
"type" => "string",
"value" => "val2"
Expand Down Expand Up @@ -528,4 +535,90 @@
expect(ret.name).to eq 'disabled'
end
end

describe 'FeatureToggle with custom stickiness' do
let(:feature_toggle) do
Unleash::FeatureToggle.new(
"name" => "toggleName",
"description" => nil,
"enabled" => true,
"variants" => [
{
"name" => "variant1",
"weight" => 25,
"stickiness" => "organization"
},
{
"name" => "variant2",
"weight" => 25,
"stickiness" => "organization"
},
{
"name" => "variant3",
"weight" => 25,
"stickiness" => "organization"
},
{
"name" => "variant4",
"weight" => 25,
"stickiness" => "organization"
}

],
"createdAt" => "2019-01-24T10:41:45.236Z"
)
end

it 'should return variant1 organization 726' do
context = Unleash::Context.new(
properties: {
organization: '726'
}
)

expect(feature_toggle.get_variant(context)).to have_attributes(
name: "variant1",
enabled: true
)
end

it 'should return variant2 organization 48' do
context = Unleash::Context.new(
properties: {
organization: '48'
}
)

expect(feature_toggle.get_variant(context)).to have_attributes(
name: "variant2",
enabled: true
)
end

it 'should return variant3 organization 381' do
context = Unleash::Context.new(
properties: {
organization: '381'
}
)

expect(feature_toggle.get_variant(context)).to have_attributes(
name: "variant3",
enabled: true
)
end

it 'should return variant4 organization 222' do
context = Unleash::Context.new(
properties: {
organization: '222'
}
)

expect(feature_toggle.get_variant(context)).to have_attributes(
name: "variant4",
enabled: true
)
end
end
end
32 changes: 32 additions & 0 deletions spec/unleash/strategy/flexible_rollout_spec.rb
Expand Up @@ -29,5 +29,37 @@
expect(strategy.is_enabled?(params.merge({ 'rollout' => 15 }), unleash_context)).to be_truthy
expect(strategy.is_enabled?(params.merge({ 'rollout' => 16 }), unleash_context)).to be_truthy
end

it 'should be enabled when stickiness=customerId and customerId=61 and rollout=10' do
params = {
'groupId' => 'Demo',
'rollout' => 10,
'stickiness' => 'customerId'
}

custom_context = Unleash::Context.new(
properties: {
customer_id: '61'
}
)

expect(strategy.is_enabled?(params, custom_context)).to be_truthy
end

it 'should be disabled when stickiness=customerId and customerId=63 and rollout=10' do
params = {
'groupId' => 'Demo',
'rollout' => 10,
'stickiness' => 'customerId'
}

custom_context = Unleash::Context.new(
properties: {
customer_id: '63'
}
)

expect(strategy.is_enabled?(params, custom_context)).to be_falsey
end
end
end

0 comments on commit b1fa033

Please sign in to comment.