From 1d76926083d12935f6485c133267304fb8cc4ec4 Mon Sep 17 00:00:00 2001 From: Filip Golonka Date: Wed, 27 Jul 2016 17:25:08 +0200 Subject: [PATCH] Allow to enable feature flag based on request parameters --- src/Feature.php | 60 ++++++++++++++++++++++++++++++++++++++----- src/Rollout.php | 32 ++++++++++++++++++++--- tests/FeatureTest.php | 28 ++++++++++++++++++++ tests/RolloutTest.php | 35 ++++++++++++++++++++++++- 4 files changed, 145 insertions(+), 10 deletions(-) create mode 100644 tests/FeatureTest.php diff --git a/src/Feature.php b/src/Feature.php index b5c9c2d..0592437 100644 --- a/src/Feature.php +++ b/src/Feature.php @@ -11,7 +11,7 @@ class Feature { /** - * @var array + * @var string */ private $name; @@ -30,6 +30,11 @@ class Feature */ private $percentage = 0; + /** + * @var string|null + */ + private $requestParam; + /** * @param string $name * @param string|null $settings @@ -38,7 +43,13 @@ public function __construct($name, $settings = null) { $this->name = $name; if ($settings) { - list($rawPercentage, $rawUsers, $rawGroups) = explode('|', $settings); + $settings = explode('|', $settings); + if (count($settings) == 4) { + $rawRequestParam = array_pop($settings); + $this->requestParam = $rawRequestParam; + } + + list($rawPercentage, $rawUsers, $rawGroups) = $settings; $this->percentage = (int) $rawPercentage; $this->users = !empty($rawUsers) ? explode(',', $rawUsers) : array(); $this->groups = !empty($rawGroups) ? explode(',', $rawGroups) : array(); @@ -79,7 +90,8 @@ public function serialize() return implode('|', array( $this->percentage, implode(',', $this->users), - implode(',', $this->groups) + implode(',', $this->groups), + $this->requestParam, )); } @@ -139,6 +151,22 @@ public function getGroups() return $this->groups; } + /** + * @return string|null + */ + public function getRequestParam() + { + return $this->requestParam; + } + + /** + * @param string|null $requestParam + */ + public function setRequestParam($requestParam) + { + $this->requestParam = $requestParam; + } + /** * Clear the feature of all configuration */ @@ -147,6 +175,7 @@ public function clear() $this->groups = array(); $this->users = array(); $this->percentage = 0; + $this->requestParam = ''; } /** @@ -154,15 +183,19 @@ public function clear() * * @param Rollout $rollout * @param RolloutUserInterface|null $user + * @param array $requestParameters * @return bool */ - public function isActive(Rollout $rollout, RolloutUserInterface $user = null) + public function isActive(Rollout $rollout, RolloutUserInterface $user = null, array $requestParameters = array()) { if (null == $user) { - return $this->percentage == 100; + return $this->isParamInRequestParams($requestParameters) || $this->percentage == 100; } - return $this->isUserInPercentage($user) || $this->isUserInActiveUsers($user) || $this->isUserInActiveGroup($user, $rollout); + return $this->isParamInRequestParams($requestParameters) || + $this->isUserInPercentage($user) || + $this->isUserInActiveUsers($user) || + $this->isUserInActiveGroup($user, $rollout); } /** @@ -174,9 +207,24 @@ public function toArray() 'percentage' => $this->percentage, 'groups' => $this->groups, 'users' => $this->users, + 'requestParam' => $this->requestParam, ); } + /** + * @param array $requestParameters + * @return bool + */ + private function isParamInRequestParams(array $requestParameters) + { + $param = explode('=', $this->requestParam); + $key = array_shift($param); + $value = array_shift($param); + + return $key && array_key_exists($key, $requestParameters) && + (empty($value) || $requestParameters[$key] == $value); + } + /** * @param RolloutUserInterface $user * @return bool diff --git a/src/Rollout.php b/src/Rollout.php index 69ef994..ea4caba 100644 --- a/src/Rollout.php +++ b/src/Rollout.php @@ -119,15 +119,16 @@ public function defineGroup($group, \Closure $closure) } /** - * @param string $feature + * @param string $feature * @param RolloutUserInterface|null $user + * @param array $requestParameters * @return bool */ - public function isActive($feature, RolloutUserInterface $user = null) + public function isActive($feature, RolloutUserInterface $user = null, array $requestParameters = array()) { $feature = $this->get($feature); - return $feature ? $feature->isActive($this, $user) : false; + return $feature ? $feature->isActive($this, $user, $requestParameters) : false; } /** @@ -155,6 +156,31 @@ public function deactivatePercentage($feature) } } + /** + * @param string $feature + * @param string $requestParam + */ + public function activateRequestParam($feature, $requestParam) + { + $feature = $this->get($feature); + if ($feature) { + $feature->setRequestParam($requestParam); + $this->save($feature); + } + } + + /** + * @param string $feature + */ + public function deactivateRequestParam($feature) + { + $feature = $this->get($feature); + if ($feature) { + $feature->setRequestParam(''); + $this->save($feature); + } + } + /** * @param string $group * @param RolloutUserInterface $user diff --git a/tests/FeatureTest.php b/tests/FeatureTest.php new file mode 100644 index 0000000..fdf5421 --- /dev/null +++ b/tests/FeatureTest.php @@ -0,0 +1,28 @@ + + */ +class FeatureTest extends \PHPUnit_Framework_TestCase +{ + public function testParseOldSettingsFormat() + { + $feature = new Feature('chat', '100|4,12|fivesonly'); + + $this->assertEquals(100, $feature->getPercentage()); + $this->assertEquals([4, 12], $feature->getUsers()); + $this->assertEquals(['fivesonly'], $feature->getGroups()); + } + + public function testParseNewSettingsFormat() + { + $feature = new Feature('chat', '100|4,12|fivesonly|FF_facebookIntegration=1'); + + $this->assertEquals(100, $feature->getPercentage()); + $this->assertEquals([4, 12], $feature->getUsers()); + $this->assertEquals(['fivesonly'], $feature->getGroups()); + $this->assertEquals('FF_facebookIntegration=1', $feature->getRequestParam()); + } +} diff --git a/tests/RolloutTest.php b/tests/RolloutTest.php index 1869a15..0654d9c 100644 --- a/tests/RolloutTest.php +++ b/tests/RolloutTest.php @@ -72,6 +72,7 @@ public function testDeactivatingAFeatureCompletely() $this->rollout->activateGroup('chat', 'fivesonly'); $this->rollout->activateUser('chat', new RolloutUser(51)); $this->rollout->activatePercentage('chat', 100); + $this->rollout->activateRequestParam('chat', 'FF_facebookIntegration=1'); $this->rollout->activate('chat'); $this->rollout->deactivate('chat'); @@ -84,6 +85,9 @@ public function testDeactivatingAFeatureCompletely() // it should remove the percentage $this->assertFalse($this->rollout->isActive('chat', new RolloutUser(24))); + // it should remove the request param + $this->assertFalse($this->rollout->isActive('chat', null, array('FF_facebookIntegration', true))); + // it should be removed globally $this->assertFalse($this->rollout->isActive('chat')); } @@ -204,6 +208,25 @@ public function testDeactivatingThePercentageOfUsers() $this->assertFalse($this->rollout->isActive('chat', new RolloutUser(24))); } + public function testActivatingRequestParam() + { + $this->rollout->activateRequestParam('chat', 'FF_facebookIntegration=1'); + + $this->assertTrue($this->rollout->isActive('chat', null, ['FF_facebookIntegration' => true])); + + $this->assertFalse($this->rollout->isActive('chat', null, ['FF_anotherFeature' => true])); + } + + public function testDeactivatingRequestParam() + { + $this->rollout->activateRequestParam('chat', 'FF_facebookIntegration=1'); + $this->rollout->deactivateRequestParam('chat'); + + $this->assertFalse($this->rollout->isActive('chat', null, ['FF_facebookIntegration' => true])); + + $this->assertFalse($this->rollout->isActive('chat', null, ['FF_anotherFeature' => true])); + } + public function testDeactivatingTheFeatureGlobally() { $this->rollout->activate('chat'); @@ -232,6 +255,7 @@ public function testGet() $this->rollout->activateGroup('chat', 'greeters'); $this->rollout->activate('signup'); $this->rollout->activateUser('chat', new RolloutUser(42)); + $this->rollout->activateRequestParam('chat', 'FF_facebookIntegration=1'); // it should return the feature object $feature = $this->rollout->get('chat'); @@ -239,12 +263,21 @@ public function testGet() $this->assertContains('greeters', $feature->getGroups()); $this->assertEquals(10, $feature->getPercentage()); $this->assertContains(42, $feature->getUsers()); - $this->assertEquals(array('groups' => array('caretakers', 'greeters'), 'percentage' => 10, 'users' => array('42')), $feature->toArray()); + $this->assertEquals( + array( + 'groups' => array('caretakers', 'greeters'), + 'percentage' => 10, + 'users' => array('42'), + 'requestParam' => 'FF_facebookIntegration=1' + ), + $feature->toArray() + ); $feature = $this->rollout->get('signup'); $this->assertEmpty($feature->getGroups()); $this->assertEmpty($feature->getUsers()); $this->assertEquals(100, $feature->getPercentage()); + $this->assertEmpty($feature->getRequestParam()); } public function testRemove()