diff --git a/.gitignore b/.gitignore index e9e3186..3644874 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ vendor tests/logs build composer.lock +.phpunit.result.cache diff --git a/.phpunit.result.cache b/.phpunit.result.cache index a622c60..6c6c459 100644 --- a/.phpunit.result.cache +++ b/.phpunit.result.cache @@ -1 +1 @@ -{"version":1,"defects":{"Tests\\ExportImportRepositoryTest::testShouldExportFeatureFlags":4,"Tests\\ExportImportRepositoryTest::testShouldImportResults":4,"Tests\\ExportImportRepositoryTest::testShouldNotDuplicateResults":4,"Tests\\ExportImportRepositoryTest::testUpdatesExistingResult":4,"Tests\\FeatureFlagHelperTest::testCacheSettings":4,"Tests\\FeatureFlagHelperTest::testFormatRemoveQuotes":4,"Tests\\FeatureFlagSettingControllerTest::testShouldSeeNewSettings":4,"Tests\\FeatureFlagSettingControllerTest::testShouldGetSettings":4,"Tests\\FeatureFlagSettingControllerTest::testShouldImportSettings":3,"Tests\\FeatureFlagSettingControllerTest::testShouldImportSettingsFail":4,"Tests\\FeatureFlagSettingControllerTest::testStripQuotes":4,"Tests\\FeatureFlagSettingControllerTest::testStripSingleQuotes":4,"Tests\\FeatureFlagSettingControllerTest::testShouldStore":4,"Tests\\FeatureFlagSettingControllerTest::testShouldStoreFail":4,"Tests\\FeatureFlagSettingControllerTest::testShouldEdit":4,"Tests\\FeatureFlagSettingControllerTest::testShouldEditFail":4,"Tests\\FeatureFlagSettingControllerTest::testShouldUpdate":4,"Tests\\FeatureFlagSettingControllerTest::testShouldUpdateFail":4,"Tests\\FeatureFlagSettingControllerTest::testShouldDestroy":4,"Tests\\FeatureFlagSettingControllerTest::testShouldDestroyFail":4,"Tests\\FeatureFlagTest::testOn":4,"Tests\\FeatureFlagTest::testOff":4,"Tests\\FeatureFlagTest::testOnForUserEmail":4,"Tests\\FeatureFlagTest::testOffForUserEmail":4,"Tests\\FeatureFlagTest::testForNotFindFeature":4,"Tests\\FeatureFlagTest::testOnForUserRole":4,"Tests\\FeatureFlagTest::testOffForUserRole":4,"Tests\\FeatureFlagsEnablerTest::testItWorksWhenInterfaceImplented with data set #0":4,"Tests\\FeatureFlagsEnablerTest::testItWorksWhenInterfaceImplented with data set #1":4,"Tests\\FeatureFlagsEnablerTest::testItWorksWhenInterfaceImplented with data set #2":4,"Tests\\FeatureFlagsForJavascriptTest::testGetNoResults":4,"Tests\\FeatureFlagsForJavascriptTest::testGetWithResults":4,"Tests\\FeatureTest::testExists":4,"Tests\\FeatureTest::testIsEnabledPassingKey":4,"Tests\\FeatureTest::testIsEnabledPassingKeyAndUser":4,"Tests\\FeatureTest::testIsEnabledPassingVariant":4,"Tests\\FeatureTest::testGetConfigFromCache":4,"Tests\\SettingsPageTest::testShouldSeeSettings":1,"Tests\\SettingsPageTest::testCanEditSettings":1,"Tests\\SettingsPageTest::testCanCreateSettings":1,"Tests\\SyncFlagsCommandTest::testShouldSyncFeatureFlags":4,"Tests\\SyncFlagsCommandTest::testShouldSyncNotOverwriteFeatureFlags":4,"Tests\\SyncFlagsCommandTest::testShouldSyncFeatureFlagsSkippingCleanup":4},"times":{"Tests\\ExportImportRepositoryTest::testShouldExportFeatureFlags":0.177,"Tests\\ExportImportRepositoryTest::testShouldImportResults":0.02,"Tests\\ExportImportRepositoryTest::testShouldNotDuplicateResults":0.019,"Tests\\ExportImportRepositoryTest::testUpdatesExistingResult":0.016,"Tests\\FeatureFlagHelperTest::testCacheSettings":0.021,"Tests\\FeatureFlagHelperTest::testFormatRemoveQuotes":0.015,"Tests\\FeatureFlagSettingControllerTest::testShouldSeeNewSettings":0.018,"Tests\\FeatureFlagSettingControllerTest::testShouldGetSettings":0.016,"Tests\\FeatureFlagSettingControllerTest::testShouldImportSettings":0.018,"Tests\\FeatureFlagSettingControllerTest::testShouldImportSettingsFail":0.021,"Tests\\FeatureFlagSettingControllerTest::testStripQuotes":0.014,"Tests\\FeatureFlagSettingControllerTest::testStripSingleQuotes":0.014,"Tests\\FeatureFlagSettingControllerTest::testShouldStore":0.022,"Tests\\FeatureFlagSettingControllerTest::testShouldStoreFail":0.02,"Tests\\FeatureFlagSettingControllerTest::testShouldEdit":0.021,"Tests\\FeatureFlagSettingControllerTest::testShouldEditFail":0.021,"Tests\\FeatureFlagSettingControllerTest::testShouldUpdate":0.02,"Tests\\FeatureFlagSettingControllerTest::testShouldUpdateFail":0.017,"Tests\\FeatureFlagSettingControllerTest::testShouldDestroy":0.02,"Tests\\FeatureFlagSettingControllerTest::testShouldDestroyFail":0.018,"Tests\\FeatureFlagTest::testOn":0.075,"Tests\\FeatureFlagTest::testOff":0.068,"Tests\\FeatureFlagTest::testOnForUserEmail":0.071,"Tests\\FeatureFlagTest::testOffForUserEmail":0.064,"Tests\\FeatureFlagTest::testForNotFindFeature":0.065,"Tests\\FeatureFlagTest::testOnForUserRole":0.066,"Tests\\FeatureFlagTest::testOffForUserRole":0.065,"Tests\\FeatureFlagsEnablerTest::testItWorksWhenInterfaceImplented with data set #0":0.067,"Tests\\FeatureFlagsEnablerTest::testItWorksWhenInterfaceImplented with data set #1":0.066,"Tests\\FeatureFlagsEnablerTest::testItWorksWhenInterfaceImplented with data set #2":0.068,"Tests\\FeatureFlagsForJavascriptTest::testGetNoResults":0.015,"Tests\\FeatureFlagsForJavascriptTest::testGetWithResults":0.016,"Tests\\FeatureTest::testExists":0.068,"Tests\\FeatureTest::testIsEnabledPassingKey":0.065,"Tests\\FeatureTest::testIsEnabledPassingKeyAndUser":0.065,"Tests\\FeatureTest::testIsEnabledPassingVariant":0.065,"Tests\\FeatureTest::testGetConfigFromCache":0.015,"Tests\\SettingsPageTest::testShouldSeeSettings":0.001,"Tests\\SettingsPageTest::testCanEditSettings":0,"Tests\\SettingsPageTest::testCanCreateSettings":0,"Tests\\SyncFlagsCommandTest::testShouldSyncFeatureFlags":0.02,"Tests\\SyncFlagsCommandTest::testShouldSyncNotOverwriteFeatureFlags":0.018,"Tests\\SyncFlagsCommandTest::testShouldSyncFeatureFlagsSkippingCleanup":0.018}} \ No newline at end of file +{"version":1,"defects":{"Tests\\SettingsPageTest::testShouldSeeSettings":4,"Tests\\SettingsPageTest::testCanEditSettings":4,"Tests\\SettingsPageTest::testCanCreateSettings":4},"times":{"Tests\\ExportImportRepositoryTest::testShouldExportFeatureFlags":0.15,"Tests\\ExportImportRepositoryTest::testShouldImportResults":0.015,"Tests\\ExportImportRepositoryTest::testShouldNotDuplicateResults":0.014,"Tests\\ExportImportRepositoryTest::testUpdatesExistingResult":0.016,"Tests\\FeatureFlagHelperTest::testCacheSettings":0.024,"Tests\\FeatureFlagHelperTest::testFormatRemoveQuotes":0.019,"Tests\\FeatureFlagSettingControllerTest::testShouldSeeNewSettings":0.02,"Tests\\FeatureFlagSettingControllerTest::testShouldGetSettings":0.018,"Tests\\FeatureFlagSettingControllerTest::testShouldImportSettings":0.023,"Tests\\FeatureFlagSettingControllerTest::testShouldImportSettingsFail":0.019,"Tests\\FeatureFlagSettingControllerTest::testStripQuotes":0.016,"Tests\\FeatureFlagSettingControllerTest::testStripSingleQuotes":0.015,"Tests\\FeatureFlagSettingControllerTest::testShouldStore":0.015,"Tests\\FeatureFlagSettingControllerTest::testShouldStoreFail":0.016,"Tests\\FeatureFlagSettingControllerTest::testShouldEdit":0.016,"Tests\\FeatureFlagSettingControllerTest::testShouldEditFail":0.015,"Tests\\FeatureFlagSettingControllerTest::testShouldUpdate":0.016,"Tests\\FeatureFlagSettingControllerTest::testShouldUpdateFail":0.015,"Tests\\FeatureFlagSettingControllerTest::testShouldDestroy":0.017,"Tests\\FeatureFlagSettingControllerTest::testShouldDestroyFail":0.016,"Tests\\FeatureFlagTest::testOn":0.088,"Tests\\FeatureFlagTest::testOff":0.073,"Tests\\FeatureFlagTest::testOnForUserEmail":0.08,"Tests\\FeatureFlagTest::testOffForUserEmail":0.071,"Tests\\FeatureFlagTest::testForNotFindFeature":0.067,"Tests\\FeatureFlagTest::testOnForUserRole":0.077,"Tests\\FeatureFlagTest::testOffForUserRole":0.076,"Tests\\FeatureFlagTest::testOnForUserTeam":0.064,"Tests\\FeatureFlagTest::testOffForUserTeam":0.07,"Tests\\FeatureFlagsEnablerTest::testItWorksWhenInterfaceImplented with data set #0":0.068,"Tests\\FeatureFlagsEnablerTest::testItWorksWhenInterfaceImplented with data set #1":0.071,"Tests\\FeatureFlagsEnablerTest::testItWorksWhenInterfaceImplented with data set #2":0.065,"Tests\\FeatureFlagsForJavascriptTest::testGetNoResults":0.013,"Tests\\FeatureFlagsForJavascriptTest::testGetWithResults":0.012,"Tests\\FeatureTest::testExists":0.064,"Tests\\FeatureTest::testIsEnabledPassingKey":0.084,"Tests\\FeatureTest::testIsEnabledPassingKeyAndUser":0.07,"Tests\\FeatureTest::testIsEnabledPassingVariant":0.065,"Tests\\FeatureTest::testGetConfigFromCache":0.013,"Tests\\SettingsPageTest::testShouldSeeSettings":0.12,"Tests\\SettingsPageTest::testCanEditSettings":0.014,"Tests\\SettingsPageTest::testCanCreateSettings":0.014,"Tests\\SyncFlagsCommandTest::testShouldSyncFeatureFlags":0.024,"Tests\\SyncFlagsCommandTest::testShouldSyncNotOverwriteFeatureFlags":0.017,"Tests\\SyncFlagsCommandTest::testShouldSyncFeatureFlagsSkippingCleanup":0.017}} \ No newline at end of file diff --git a/readme.md b/readme.md index 889af3c..8b3db03 100644 --- a/readme.md +++ b/readme.md @@ -148,6 +148,27 @@ class User extends Authenticatable implements FeatureFlagsUserRoles } ~~~ +### Enable for User Teams +You can enable a feature flag for specific user teams, by using the **teams** variant in the configuration form + +i.e. + +~~~ +{ "teams": ["Team 1", "Team 2"]} +~~~ + +If you don't have a teams property in your User model, you just need to implement the **FeatureFlagsEnabler** Interface and use **FeatureFlagUserRoleTrait** + +~~~ +use FriendsOfCat\LaravelFeatureFlags\FeatureFlagsEnabler; +use FriendsOfCat\LaravelFeatureFlags\FeatureFlagUserRoleTrait; + +class User extends Authenticatable implements FeatureFlagsUserRoles +{ + use AuthenticableTrait, FeatureFlagUserRoleTrait; +} +~~~ + ## Usage Non Auth Sometimes you are not using this at the Auth user level, it is rare for most of our use cases but for non authenticated situations you can just use this diff --git a/src/Feature.php b/src/Feature.php index 8a093cb..22674da 100644 --- a/src/Feature.php +++ b/src/Feature.php @@ -50,7 +50,9 @@ public function isEnabled($featureKey, $variant = null, $user = null) } if ($variant != self::ON and $variant != self::OFF) { - return $this->isUserEnabled($variant, $user) || $this->isRoleEnabled($variant, $user); + return $this->isUserEnabled($variant, $user) + || $this->isRoleEnabled($variant, $user) + || $this->isTeamEnabled($variant, $user); } return $variant == self::ON; @@ -119,6 +121,11 @@ private function getUserRoles($user) return ($user && $user->roles) ? $user->roles : false; } + private function getUserTeams($user) + { + return ($user && $user->teams) ? $user->teams : false; + } + public function isRoleEnabled($feature_variant, $user = null) { $fieldName = 'roles'; @@ -145,4 +152,32 @@ function ($value, $key) use ($feature_variant, $fieldName) { return false; } + + public function isTeamEnabled($feature_variant, $user = null) + { + $fieldName = 'teams'; + + if (empty($feature_variant[$fieldName])) { + return false; + } + + $user = $user ?? Auth::user(); + + if ($user_teams= + ($user instanceof FeatureFlagsEnabler) + ? $user->getFieldValueForFeatureFlags($fieldName) + : $this->getUserTeams($user) + ) { + $filtered = Arr::where( + $user_teams, + function ($value, $key) use ($feature_variant, $fieldName) { + return in_array($value, $feature_variant[$fieldName]); + } + ); + + return ! empty($filtered); + } + + return false; + } } diff --git a/src/FeatureFlagHelper.php b/src/FeatureFlagHelper.php index fa6984e..ef5b324 100644 --- a/src/FeatureFlagHelper.php +++ b/src/FeatureFlagHelper.php @@ -42,12 +42,10 @@ private function transformFeatures($features, $value, $key) { $features[$value['key']] = $this->getAndSetValue($value); - if (isset($value['variants']['users'])) { - $features[$value['key']]['users'] = $value['variants']['users']; - } - - if (isset($value['variants']['roles'])) { - $features[$value['key']]['roles'] = $value['variants']['roles']; + foreach (['users', 'roles', 'teams'] as $variant) { + if (isset($value['variants'][$variant])) { + $features[$value['key']][$variant] = $value['variants'][$variant]; + } } return $features; diff --git a/tests/FeatureFlagTest.php b/tests/FeatureFlagTest.php index a4e4514..8d910b1 100644 --- a/tests/FeatureFlagTest.php +++ b/tests/FeatureFlagTest.php @@ -10,7 +10,8 @@ class FeatureFlagTest extends TestCase { - use RefreshDatabase, FeatureFlagHelper; + use RefreshDatabase; + use FeatureFlagHelper; protected $user; @@ -156,4 +157,51 @@ public function testOffForUserRole() $this->assertFalse($this->app->get(Gate::class)->allows('feature-flag', 'testing')); } + + public function testOnForUserTeam() + { + $this->user = factory(FeatureFlagUser::class)->create(['email' => 'foo5@gmail.com']); + $this->user->setRawAttributes(['teams' => ['Team 1']]); + + $this->be($this->user); + + factory(FeatureFlag::class)->create( + [ + 'key' => 'testing', + 'variants' => [ + 'teams' => [ + 'Team 1', + 'Team 2' + ] + ] + ] + ); + + $this->registerFeatureFlags(); + + $this->assertTrue($this->app->get(Gate::class)->allows('feature-flag', 'testing')); + } + + public function testOffForUserTeam() + { + $this->user = factory(FeatureFlagUser::class)->create(['email' => 'foo5@gmail.com']); + $this->user->setRawAttributes(['teams' => ['Team 1']]); + + $this->be($this->user); + + factory(FeatureFlag::class)->create( + [ + 'key' => 'testing', + 'variants' => [ + 'teams' => [ + 'Team 3' + ] + ] + ] + ); + + $this->registerFeatureFlags(); + + $this->assertFalse($this->app->get(Gate::class)->allows('feature-flag', 'testing')); + } } diff --git a/tests/fixtures/UserWithFeatureFlagsEnablerInterface.php b/tests/fixtures/UserWithFeatureFlagsEnablerInterface.php index 5ebaf6e..bcdee8e 100644 --- a/tests/fixtures/UserWithFeatureFlagsEnablerInterface.php +++ b/tests/fixtures/UserWithFeatureFlagsEnablerInterface.php @@ -10,10 +10,11 @@ class UserWithFeatureFlagsEnablerInterface extends Model implements Authenticatable, FeatureFlagsEnabler { - use AuthenticableTrait, FeatureFlagUserRoleTrait; + use AuthenticableTrait; + use FeatureFlagUserRoleTrait; protected $table = 'users'; - protected $fillable = ['name', 'email', 'password', 'roles']; + protected $fillable = ['name', 'email', 'password', 'roles', 'teams']; public function __construct(array $attributes = [ ]) {