Skip to content
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

[1.x] Allow retrieving all features for a scope when some features are defined for differing scopes #121

Conversation

cosmastech
Copy link
Contributor

@cosmastech cosmastech commented Aug 17, 2024

This should resolve #112

We are building a feature flags endpoint for our mobile and web consumers. We need to be able to get all features for 3-4 scopes which are related to the user, but not the user object itself.

I ran into this exact issue earlier today. I saw that this issue had been open for a few weeks without a PR so I figured I'd take a stab at it.


Given we have features defined for different scopes

Feature::define('feature-for-user', fn(User $u) => true);
Feature::define('feature-for-team', fn(Team $t) => true);

When we attempt to load all features for a particular scope

$features = Feature::for($user)->all()

Then we expect to receive feature flag definitions ONLY for that apply to Users.

dd($features);
/*
array:1 [
  "feature-for-user" => true
]
*/

Currently, however, an exception is thrown: TypeError: Tests\Feature\DatabaseDriverTest::Tests\Feature{closure}(): Argument #1 ($t) must be of type Workbench\App\Models\Team, Workbench\App\Models\User given, called in /Users/luke/Projects/laravel-pennant/src/Drivers/Decorator.php on line 173

Non-goals of this PR

One outstanding question is: do we expect to allow for union or intersection types in the resolving function? Like Feature::define('team-or-user-feature', fn(Team|User $v) => true); Seems like this probably wouldn't work with how scopes are set up in general. I am sure that my change will not work in this case.

Also, you can still call Feature::for($user)->activate('team-scoped-feature'); I believe that ideally there would be a guard for this, but it seems like it's beyond the scope of this PR.

Copy link

Thanks for submitting a PR!

Note that draft PR's are not reviewed. If you would like a review, please mark your pull request as ready for review in the GitHub user interface.

Pull requests that are abandoned in draft may be closed due to inactivity.

@cosmastech cosmastech force-pushed the no-error-when-feature-does-not-apply-to-scope branch from 5663d44 to c89423f Compare August 17, 2024 02:07
@cosmastech cosmastech marked this pull request as ready for review August 17, 2024 02:36
@cosmastech cosmastech changed the title No error when feature does not apply to scope [1.x] Allow for retrieving all features for a scope when some features are defined for differing scopes Aug 17, 2024
@cosmastech cosmastech marked this pull request as draft August 17, 2024 10:41
@cosmastech cosmastech marked this pull request as ready for review August 17, 2024 12:35
@cosmastech cosmastech changed the title [1.x] Allow for retrieving all features for a scope when some features are defined for differing scopes [1.x] Allow retrieving all features for a scope when some features are defined for differing scopes Aug 17, 2024
@taylorotwell
Copy link
Member

Drafting pending review from @timacdonald

@taylorotwell taylorotwell marked this pull request as draft August 17, 2024 20:59
@timacdonald timacdonald self-assigned this Aug 27, 2024
@timacdonald timacdonald force-pushed the no-error-when-feature-does-not-apply-to-scope branch 4 times, most recently from 8aa3338 to 503b8a4 Compare November 6, 2024 05:05
@timacdonald timacdonald force-pushed the no-error-when-feature-does-not-apply-to-scope branch from 503b8a4 to ab01fed Compare November 6, 2024 05:07
@timacdonald
Copy link
Member

@cosmastech, I have re-worked this one. There were a few problems with the original implementation. The main issue was when a value was resolved for a bad type, it was saved to the database.

The new implementation does the check before attempting to resolve. Supports union and intersection types.

What do you think?

Feature::for($scope)->all() and Feature::for($scope)->loadAll() will now only return or load the features for that scope type.

@cosmastech
Copy link
Contributor Author

@cosmastech, I have re-worked this one. There were a few problems with the original implementation. The main issue was when a value was resolved for a bad type, it was saved to the database.

The new implementation does the check before attempting to resolve. Supports union and intersection types.

What do you think?

Feature::for($scope)->all() and Feature::for($scope)->loadAll() will now only return or load the features for that scope type.

Thanks for working on this @timacdonald. Sad you removed my given-when-then 😆 but otherwise, nice.

I didn't run any local testing on this, but it seems to solve the problem. Good catch on the DB queries, didn't consider that fact.

@timacdonald timacdonald marked this pull request as ready for review November 7, 2024 22:23
@timacdonald timacdonald marked this pull request as draft November 7, 2024 22:25
@timacdonald timacdonald marked this pull request as ready for review November 7, 2024 22:26
@taylorotwell taylorotwell merged commit 06106b8 into laravel:1.x Nov 8, 2024
6 checks passed
@cosmastech cosmastech deleted the no-error-when-feature-does-not-apply-to-scope branch November 8, 2024 13:39
public function definedFeaturesForScope($scope)
{
return collect($this->nameMap)
->only($this->defined())
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@timacdonald Unfortunately this doesn't work with 3rd party services like LaunchDarkly where the features are defined in their platform.

Any thoughts on adding a check before this to see the namedMap is empty and if it is we load all of the features from $this->defined()?

An alternative could be that I retrieve all of the features from LaunchDarkly within a service provider and define them. But this just seems wrong as I can do the same thing within the defined method on the driver.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are your thoughts on having a contract the drivers can implement to provide a list of features for the given scope?

Screenshot 2024-11-11 at 11 17 37

I suppose the thing we are trying to solve in this PR also impacts 3rd party drivers as well, so feels like we need a way for them to handle things themselves.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This approach would be perfect!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened a PR: #127

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Using multiple scopes across features disables use of all() and values()
4 participants