-
Notifications
You must be signed in to change notification settings - Fork 73
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
fix: Always check parent dependency if present #248
Changes from all commits
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 |
---|---|---|
|
@@ -179,56 +179,53 @@ private FeatureEvaluationResult getFeatureEvaluationResult( | |
fallbackAction.test(toggleName, enhancedContext), defaultVariant); | ||
} else if (!featureToggle.isEnabled()) { | ||
return new FeatureEvaluationResult(false, defaultVariant); | ||
} else if (featureToggle.getStrategies().isEmpty()) { | ||
return new FeatureEvaluationResult( | ||
true, VariantUtil.selectVariant(featureToggle, context, defaultVariant)); | ||
} else { | ||
} else if (isParentDependencySatisfied(featureToggle, context, fallbackAction)) { | ||
// Dependent toggles, no point in evaluating child strategies if our dependencies are | ||
// not satisfied | ||
if (isParentDependencySatisfied(featureToggle, context, fallbackAction)) { | ||
for (ActivationStrategy strategy : featureToggle.getStrategies()) { | ||
Strategy configuredStrategy = getStrategy(strategy.getName()); | ||
if (configuredStrategy == UNKNOWN_STRATEGY) { | ||
LOGGER.warn( | ||
"Unable to find matching strategy for toggle:{} strategy:{}", | ||
toggleName, | ||
strategy.getName()); | ||
} | ||
if (featureToggle.getStrategies().isEmpty()) { | ||
return new FeatureEvaluationResult( | ||
true, VariantUtil.selectVariant(featureToggle, context, defaultVariant)); | ||
} | ||
for (ActivationStrategy strategy : featureToggle.getStrategies()) { | ||
Strategy configuredStrategy = getStrategy(strategy.getName()); | ||
if (configuredStrategy == UNKNOWN_STRATEGY) { | ||
LOGGER.warn( | ||
"Unable to find matching strategy for toggle:{} strategy:{}", | ||
toggleName, | ||
strategy.getName()); | ||
} | ||
|
||
FeatureEvaluationResult result = | ||
configuredStrategy.getResult( | ||
strategy.getParameters(), | ||
enhancedContext, | ||
ConstraintMerger.mergeConstraints(featureRepository, strategy), | ||
strategy.getVariants()); | ||
|
||
if (result.isEnabled()) { | ||
Variant variant = result.getVariant(); | ||
// If strategy variant is null, look for a variant in the featureToggle | ||
if (variant == null) { | ||
variant = | ||
VariantUtil.selectVariant( | ||
featureToggle, context, defaultVariant); | ||
} | ||
result.setVariant(variant); | ||
return result; | ||
FeatureEvaluationResult result = | ||
configuredStrategy.getResult( | ||
strategy.getParameters(), | ||
enhancedContext, | ||
ConstraintMerger.mergeConstraints(featureRepository, strategy), | ||
strategy.getVariants()); | ||
Comment on lines
+198
to
+203
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. I find it awkward that if we don't find strategies we return 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. I don't disagree, it's not because of this PR though. Maybe a rethink here; as a separate thing. I'm open to some sparring 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.
I think you have to take this up with v0.0.1 of the SDK spec. That's the correct behavior. An empty list of strategies is valid. A custom strategy that you defined server side but didn't implement client side is a broken and is expected to short circuit to false 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.
I am completely with @sighphyre here. If a strategy is not found, the safe and correct way is to return false for the toggle if the strategy is not found |
||
|
||
if (result.isEnabled()) { | ||
Variant variant = result.getVariant(); | ||
// If strategy variant is null, look for a variant in the featureToggle | ||
if (variant == null) { | ||
variant = VariantUtil.selectVariant(featureToggle, context, defaultVariant); | ||
} | ||
result.setVariant(variant); | ||
return result; | ||
} | ||
} | ||
return new FeatureEvaluationResult(false, defaultVariant); | ||
} | ||
return new FeatureEvaluationResult(false, defaultVariant); | ||
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. Something feels very subtly off here but it's not breaking tests sooo... it might be okay? What's going through my head? Previously, we'd fall through to true, now we fall through to false. In theory, a toggle that has no strategies should fallback to the 'enabled' property, not true or false. That's distinct from a toggle that doesn't have strategies defined, which should never happen server side but when it does we turn off the toggle So really we're into undefined territory here. It's probably okay but there's nothing in the client spec that enforces this behavior, so it's possible we break this again subtly 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. But we've already checked the enabled property. If that evaluates to false, we short-circuit the entire evaluation. The one thing this does, that it didn't do previously, is the case where strategies was empty, now we have to check parent as well 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. But, since we now have 3 people saying that we struggle with following the flow; I'm all for taking the time to make a new PR (Separate from this one) trying to clean up the logic, still making sure we pass all the tests, but so we don't need clarification talks every time we touch this. |
||
} | ||
|
||
/** | ||
* Uses the old, statistically broken Variant seed for finding the correct variant | ||
* | ||
* @deprecated | ||
* @param toggleName Name of the toggle | ||
* @param context The UnleashContext | ||
* @param fallbackAction What to do if we fail to find the toggle | ||
* @param defaultVariant If we can't resolve a variant, what are we returning | ||
* @return A wrapper containing whether the feature was enabled as well which Variant was | ||
* selected | ||
* @deprecated | ||
*/ | ||
private FeatureEvaluationResult deprecatedGetFeatureEvaluationResult( | ||
String toggleName, | ||
|
@@ -244,46 +241,43 @@ private FeatureEvaluationResult deprecatedGetFeatureEvaluationResult( | |
fallbackAction.test(toggleName, enhancedContext), defaultVariant); | ||
} else if (!featureToggle.isEnabled()) { | ||
return new FeatureEvaluationResult(false, defaultVariant); | ||
} else if (featureToggle.getStrategies().isEmpty()) { | ||
return new FeatureEvaluationResult( | ||
true, | ||
VariantUtil.selectDeprecatedVariantHashingAlgo( | ||
featureToggle, context, defaultVariant)); | ||
} else { | ||
// Dependent toggles, no point in evaluating child strategies if our dependencies are | ||
// not satisfied | ||
if (isParentDependencySatisfied(featureToggle, context, fallbackAction)) { | ||
for (ActivationStrategy strategy : featureToggle.getStrategies()) { | ||
Strategy configuredStrategy = getStrategy(strategy.getName()); | ||
if (configuredStrategy == UNKNOWN_STRATEGY) { | ||
LOGGER.warn( | ||
"Unable to find matching strategy for toggle:{} strategy:{}", | ||
toggleName, | ||
strategy.getName()); | ||
} | ||
} else if (isParentDependencySatisfied(featureToggle, context, fallbackAction)) { | ||
if (featureToggle.getStrategies().isEmpty()) { | ||
return new FeatureEvaluationResult( | ||
true, | ||
VariantUtil.selectDeprecatedVariantHashingAlgo( | ||
featureToggle, context, defaultVariant)); | ||
} | ||
for (ActivationStrategy strategy : featureToggle.getStrategies()) { | ||
Strategy configuredStrategy = getStrategy(strategy.getName()); | ||
if (configuredStrategy == UNKNOWN_STRATEGY) { | ||
LOGGER.warn( | ||
"Unable to find matching strategy for toggle:{} strategy:{}", | ||
toggleName, | ||
strategy.getName()); | ||
} | ||
|
||
FeatureEvaluationResult result = | ||
configuredStrategy.getDeprecatedHashingAlgoResult( | ||
strategy.getParameters(), | ||
enhancedContext, | ||
ConstraintMerger.mergeConstraints(featureRepository, strategy), | ||
strategy.getVariants()); | ||
|
||
if (result.isEnabled()) { | ||
Variant variant = result.getVariant(); | ||
// If strategy variant is null, look for a variant in the featureToggle | ||
if (variant == null) { | ||
variant = | ||
VariantUtil.selectDeprecatedVariantHashingAlgo( | ||
featureToggle, context, defaultVariant); | ||
} | ||
result.setVariant(variant); | ||
return result; | ||
FeatureEvaluationResult result = | ||
configuredStrategy.getDeprecatedHashingAlgoResult( | ||
strategy.getParameters(), | ||
enhancedContext, | ||
ConstraintMerger.mergeConstraints(featureRepository, strategy), | ||
strategy.getVariants()); | ||
|
||
if (result.isEnabled()) { | ||
Variant variant = result.getVariant(); | ||
// If strategy variant is null, look for a variant in the featureToggle | ||
if (variant == null) { | ||
variant = | ||
VariantUtil.selectDeprecatedVariantHashingAlgo( | ||
featureToggle, context, defaultVariant); | ||
} | ||
result.setVariant(variant); | ||
return result; | ||
} | ||
} | ||
return new FeatureEvaluationResult(false, defaultVariant); | ||
} | ||
return new FeatureEvaluationResult(false, defaultVariant); | ||
} | ||
|
||
private boolean isParentDependencySatisfied( | ||
|
@@ -393,10 +387,10 @@ public Variant getVariant(String toggleName, Variant defaultValue) { | |
/** | ||
* Uses the old, statistically broken Variant seed for finding the correct variant | ||
* | ||
* @deprecated | ||
* @param toggleName | ||
* @param context | ||
* @return | ||
* @deprecated | ||
*/ | ||
@Override | ||
public Variant deprecatedGetVariant(String toggleName, UnleashContext context) { | ||
|
@@ -406,11 +400,11 @@ public Variant deprecatedGetVariant(String toggleName, UnleashContext context) { | |
/** | ||
* Uses the old, statistically broken Variant seed for finding the correct variant | ||
* | ||
* @deprecated | ||
* @param toggleName | ||
* @param context | ||
* @param defaultValue | ||
* @return | ||
* @deprecated | ||
*/ | ||
@Override | ||
public Variant deprecatedGetVariant( | ||
|
@@ -437,9 +431,9 @@ private Variant deprecatedGetVariant( | |
/** | ||
* Uses the old, statistically broken Variant seed for finding the correct variant | ||
* | ||
* @deprecated | ||
* @param toggleName | ||
* @return | ||
* @deprecated | ||
*/ | ||
@Override | ||
public Variant deprecatedGetVariant(String toggleName) { | ||
|
@@ -449,10 +443,10 @@ public Variant deprecatedGetVariant(String toggleName) { | |
/** | ||
* Uses the old, statistically broken Variant seed for finding the correct variant | ||
* | ||
* @deprecated | ||
* @param toggleName | ||
* @param defaultValue | ||
* @return | ||
* @deprecated | ||
*/ | ||
@Override | ||
public Variant deprecatedGetVariant(String toggleName, Variant defaultValue) { | ||
|
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.
Should we do something like:
?
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.
Rethinking this is probably a good idea, but it doesn't relate to this PR, so I'd suggest leaving it for a separate sparring + PR session