-
Notifications
You must be signed in to change notification settings - Fork 11
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
Dependent features #176
Dependent features #176
Conversation
a15a9a3
to
012093c
Compare
012093c
to
38b2757
Compare
src/DefaultUnleash.php
Outdated
@@ -59,21 +63,157 @@ public function isEnabled(string $featureName, ?Context $context = null, bool $d | |||
} | |||
} | |||
|
|||
return $this->isFeatureEnabled($feature, $context, $default)->isEnabled(); | |||
return $this->isFeatureEnabled($feature, $context, $dependencies, $default)->isEnabled(); |
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.
why can't dependencies be taken from the feature and need to be passed as arguments
src/DefaultUnleash.php
Outdated
} | ||
|
||
$dependencyVariants = $dependency->getVariants(); | ||
if (!empty($dependencyVariants)) { |
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.
Please don't ever use empty()
, that does like a million things. Be specific, you probably want count()
instead of empty()
.
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.
Me: Arrives on this pull request
Me: Reads this comment
Me: Can empty()
really be that bad?
Me: Reads for 5 minutes
Me: Please don't ever use empty()
, that does like a million things
src/DefaultUnleash.php
Outdated
@@ -43,7 +45,9 @@ public function __construct( | |||
public function isEnabled(string $featureName, ?Context $context = null, bool $default = false): bool | |||
{ | |||
$context ??= $this->configuration->getContextProvider()->getContext(); | |||
$feature = $this->findFeature($featureName, $context); | |||
$featureAndDependencies = $this->findFeatureAndDependencies($featureName, $context); |
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.
can findFeature return feature with dependencies same way as our API returns feature with dependencies?
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.
I think we're thinking the same thing here, I have a similar comment on materializing this from cache in the correct DTO format. I think we're saying the same thing. If we are, I definitely agree
foreach ($dependencies as $dependency) { | ||
$parentFeature = $parentFeatures[$dependency->getFeature()] ?? null; | ||
if (!$this->isDependencySatisfied($dependency, $parentFeature, $context)) { | ||
$this->metricsHandler->handleMetrics($feature, false); |
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.
this reminds me to add a test that verifies that metrics are not counted for the parent features
src/DefaultUnleash.php
Outdated
|
||
return [ | ||
'feature' => null, | ||
'dependencies' => [], |
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.
actualDependencies
Can we somehow solve it in another way? Right now the issue is that it's not possible to get feature toggles from repository multiple times in multiple places in main class. This should be doable by holding in-memory copy of the response or state of the |
@Tymek I'm not entirely sure what this feature is about (no one told me), but it seems that features now can have a dependency on another feature. If any parent feature evaluates to false, all child features are also false. Is that a correct assumption? In that case, why not construct it recursively? The DTOs pretty much should be the JSON response but as PHP DTOs. There's also the method If you describe the issue in more detail, I might be able to assist further. |
src/DefaultUnleash.php
Outdated
* dependencies: array<string, Feature>, | ||
* } | ||
*/ | ||
public function findFeatureAndDependencies(string $featureName, ?Context $context): ?array |
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.
Does this have to be public?
Also, I'm not sure this is the right layer to be doing this in. Not hitting the cache twice? Yes! Doing this... less yes.
Can we not materialise this from cache in a more complete state rather? I want to say the parseFeatures
method in the repository is a good target for this. That can return a DTO that looks something like (in very pseudocode)
DefaultFeature {
name: String,
parent?: DefaultFeature
}
src/DefaultUnleash.php
Outdated
* dependencies: array<string, Feature>, | ||
* } | ||
*/ | ||
public function findFeatureAndDependencies(string $featureName, ?Context $context): ?array |
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.
Also, I don't think this method should take context. It's strange. It doesn't affect the outcome in any way outside of meta data in the events. Especially for a function marked public that feels off
@@ -83,9 +90,9 @@ public function findFeature(string $featureName): ?Feature | |||
* @throws ClientExceptionInterface | |||
* @throws JsonException | |||
* | |||
* @return iterable<Feature> | |||
* @return array<Feature> |
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.
I see we're shifted from an iterable to an array in a few places in this PR but I can't see why we would need to. What's happening here?
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.
I don't believe it's enough to rely on the client specs here. This is feature that can go wrong in a lot of subtle ways, even though it's simple on the surface. The PHP implementation is always going to have details that most of the other SDK implementations don't - you know the details better than anyone, cover them with tests
If not to make me happy, then to make the quality gates on this repo happy
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.
Can we also put a proper description on this PR? This is a significant feature and it's helpful for others to understand what it does, why it does it and how it works. Especially for reviewers who are seeing this feature for the first time
I think we can and should do that!
|
* refactor: load dependencies when parsing * fix: phpstan errors * fix: variant hash seed * chore: update client specification * fix: stickiness calculator type * fix: variant selection * fix: finding dependency
Replaced with #187 |
Edit: Moved this into draft so can iterate a little on it
Description
Related to Unleash/unleash#2255
Introducing concept of "dependency" on a feature toggle. Feature toggle, in order to be enabled an return a variant, can require the specified state of another feature toggle. There will be a
dependencies
property on each feature.Type of change
Please delete options that are not relevant.
How Has This Been Tested?
Please describe the tests that you ran to verify your changes.
Checklist: