-
Notifications
You must be signed in to change notification settings - Fork 55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add variant strategy support #135
Changes from 7 commits
7fcf526
c6db0eb
590fc1c
90787c9
caea56f
37e6297
c914ced
03914f5
7c65044
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,8 +19,8 @@ type FeatureResponse struct { | |
} | ||
|
||
type Segment struct { | ||
Id int `json:"id"` | ||
Constraints []Constraint `json:"constraints"` | ||
Id int `json:"id"` | ||
Constraints []Constraint `json:"constraints"` | ||
} | ||
|
||
type Feature struct { | ||
|
@@ -63,12 +63,12 @@ func (fr FeatureResponse) SegmentsMap() map[int][]Constraint { | |
segments[segment.Id] = segment.Constraints | ||
} | ||
|
||
return segments; | ||
return segments | ||
} | ||
|
||
// Get variant for a given feature which is considered as enabled | ||
func (f Feature) GetVariant(ctx *context.Context) *Variant { | ||
if f.Enabled && len(f.Variants) > 0 { | ||
func (f VariantCollection) GetVariant(ctx *context.Context) *Variant { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of feature, now variant can be resolved from Strategy. |
||
if len(f.Variants) > 0 { | ||
v := f.getOverrideVariant(ctx) | ||
var variant *Variant | ||
if v == nil { | ||
|
@@ -82,7 +82,7 @@ func (f Feature) GetVariant(ctx *context.Context) *Variant { | |
return DISABLED_VARIANT | ||
} | ||
|
||
func (f Feature) getVariantFromWeights(ctx *context.Context) *Variant { | ||
func (f VariantCollection) getVariantFromWeights(ctx *context.Context) *Variant { | ||
totalWeight := 0 | ||
for _, variant := range f.Variants { | ||
totalWeight += variant.Weight | ||
|
@@ -92,7 +92,7 @@ func (f Feature) getVariantFromWeights(ctx *context.Context) *Variant { | |
} | ||
stickiness := f.Variants[0].Stickiness | ||
|
||
target := getNormalizedNumber(getSeed(ctx, stickiness), f.Name, totalWeight) | ||
target := getNormalizedNumber(getSeed(ctx, stickiness), f.GroupId, totalWeight) | ||
counter := uint32(0) | ||
for _, variant := range f.Variants { | ||
counter += uint32(variant.Weight) | ||
|
@@ -104,7 +104,7 @@ func (f Feature) getVariantFromWeights(ctx *context.Context) *Variant { | |
return DISABLED_VARIANT | ||
} | ||
|
||
func (f Feature) getOverrideVariant(ctx *context.Context) *VariantInternal { | ||
func (f VariantCollection) getOverrideVariant(ctx *context.Context) *VariantInternal { | ||
for _, variant := range f.Variants { | ||
for _, override := range variant.Overrides { | ||
if override.matchValue(ctx) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -243,12 +243,12 @@ func (uc *Client) IsEnabled(feature string, options ...FeatureOption) (enabled b | |
uc.metrics.count(feature, enabled) | ||
}() | ||
|
||
return uc.isEnabled(feature, options...) | ||
return uc.isEnabled(feature, options...).Enabled | ||
} | ||
|
||
// isEnabled abstracts away the details of checking if a toggle is turned on or off | ||
// without metrics | ||
func (uc *Client) isEnabled(feature string, options ...FeatureOption) (enabled bool) { | ||
func (uc *Client) isEnabled(feature string, options ...FeatureOption) api.StrategyResult { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of simple boolean, we are now returning complex object, that includes variant. |
||
var opts featureOption | ||
for _, o := range options { | ||
o(&opts) | ||
|
@@ -268,19 +268,29 @@ func (uc *Client) isEnabled(feature string, options ...FeatureOption) (enabled b | |
|
||
if f == nil { | ||
if opts.fallbackFunc != nil { | ||
return opts.fallbackFunc(feature, ctx) | ||
return api.StrategyResult{ | ||
Enabled: opts.fallbackFunc(feature, ctx), | ||
} | ||
} else if opts.fallback != nil { | ||
return *opts.fallback | ||
return api.StrategyResult{ | ||
Enabled: *opts.fallback, | ||
} | ||
} | ||
return api.StrategyResult{ | ||
Enabled: false, | ||
} | ||
return false | ||
} | ||
|
||
if !f.Enabled { | ||
return false | ||
return api.StrategyResult{ | ||
Enabled: false, | ||
} | ||
} | ||
|
||
if len(f.Strategies) == 0 { | ||
return f.Enabled | ||
return api.StrategyResult{ | ||
Enabled: f.Enabled, | ||
} | ||
} | ||
|
||
for _, s := range f.Strategies { | ||
|
@@ -294,7 +304,9 @@ func (uc *Client) isEnabled(feature string, options ...FeatureOption) (enabled b | |
|
||
if err != nil { | ||
uc.errors <- err | ||
return false | ||
return api.StrategyResult{ | ||
Enabled: false, | ||
} | ||
} | ||
|
||
allConstraints := make([]api.Constraint, 0) | ||
|
@@ -304,11 +316,25 @@ func (uc *Client) isEnabled(feature string, options ...FeatureOption) (enabled b | |
if ok, err := constraints.Check(ctx, allConstraints); err != nil { | ||
uc.errors <- err | ||
} else if ok && foundStrategy.IsEnabled(s.Parameters, ctx) { | ||
return true | ||
if s.Variants != nil && len(s.Variants) > 0 { | ||
return api.StrategyResult{ | ||
Enabled: true, | ||
Variant: api.VariantCollection{ | ||
GroupId: f.Name, | ||
Variants: s.Variants, | ||
}.GetVariant(ctx), | ||
} | ||
} else { | ||
return api.StrategyResult{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If strategy does not have variants, just return true and use old variants. |
||
Enabled: true, | ||
} | ||
} | ||
} | ||
} | ||
|
||
return false | ||
return api.StrategyResult{ | ||
Enabled: false, | ||
} | ||
} | ||
|
||
// GetVariant queries a variant as the specified feature is enabled. | ||
|
@@ -335,14 +361,16 @@ func (uc *Client) getVariantWithoutMetrics(feature string, options ...VariantOpt | |
ctx = ctx.Override(*opts.ctx) | ||
} | ||
|
||
var strategyResult api.StrategyResult | ||
|
||
if opts.resolver != nil { | ||
if !uc.isEnabled(feature, WithContext(*ctx), WithResolver(opts.resolver)) { | ||
return defaultVariant | ||
} | ||
strategyResult = uc.isEnabled(feature, WithContext(*ctx), WithResolver(opts.resolver)) | ||
} else { | ||
if !uc.isEnabled(feature, WithContext(*ctx)) { | ||
return defaultVariant | ||
} | ||
strategyResult = uc.isEnabled(feature, WithContext(*ctx)) | ||
} | ||
|
||
if !strategyResult.Enabled { | ||
return defaultVariant | ||
} | ||
|
||
var f *api.Feature | ||
|
@@ -365,11 +393,18 @@ func (uc *Client) getVariantWithoutMetrics(feature string, options ...VariantOpt | |
return defaultVariant | ||
} | ||
|
||
if strategyResult.Variant != nil { | ||
return strategyResult.Variant | ||
} | ||
|
||
if len(f.Variants) == 0 { | ||
return defaultVariant | ||
} | ||
|
||
return f.GetVariant(ctx) | ||
return api.VariantCollection{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If code runs here, then strategy variants were not found, use old variants |
||
GroupId: f.Name, | ||
Variants: f.Variants, | ||
}.GetVariant(ctx) | ||
} | ||
|
||
// Close stops the client from syncing data from the server. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update client spec to latest