-
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
tec: Add flag system for experimental features
- Loading branch information
1 parent
f2078d8
commit 599b8da
Showing
11 changed files
with
487 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
# Enable experimental features | ||
|
||
Some features might be available behind a feature flag. This allows to ship | ||
new code and tests it in production without showing unfinished work to users. | ||
You can enable them for specific users. | ||
|
||
If you’re unfamiliar with the CLI, you’re encouraged to take a look at [its | ||
documentation](/docs/cli.md). | ||
|
||
First of all, you can list the features with the following command: | ||
|
||
```console | ||
$ ./cli --request /features | ||
feeds | ||
``` | ||
|
||
Here, there is only one feature: feeds. | ||
|
||
You should now list the users to get there ids: | ||
|
||
```console | ||
$ ./cli --request /users | ||
44ff4da402379f91ab0b1cf2a12bf6d4 2021-04-07 [email protected] | ||
a97f04ac01bce558a06fca5023cd3b54 2021-04-07 [email protected] | ||
8dff621fbf93ee18b39ee48fe6ec44d4 2021-04-14 [email protected] | ||
``` | ||
|
||
Each line corresponds to a user, it shows: id, creation date and email. | ||
|
||
If you want to enable the feature `feeds` for the user `[email protected]`, you | ||
must run the following command: | ||
|
||
```console | ||
$ ./cli --request /features/enable -ptype=feeds -puser_id=8dff621fbf93ee18b39ee48fe6ec44d4 | ||
feeds is enabled for user 8dff621fbf93ee18b39ee48fe6ec44d4 ([email protected]) | ||
``` | ||
|
||
Then, the user should be able to access the `feeds` feature. | ||
|
||
You can list the enabled flags: | ||
|
||
```console | ||
$ ./cli --request /features/flags | ||
feeds 8dff621fbf93ee18b39ee48fe6ec44d4 [email protected] | ||
``` | ||
|
||
Each line corresponds to an enabled flag, it shows: flag type, user id, user | ||
email. | ||
|
||
You can disable the flags at any moment: | ||
|
||
```console | ||
$ ./cli --request /features/disable -ptype=feeds -puser_id=8dff621fbf93ee18b39ee48fe6ec44d4 | ||
feeds is disabled for user 8dff621fbf93ee18b39ee48fe6ec44d4 ([email protected]) | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
<?php | ||
|
||
namespace flusio\cli; | ||
|
||
use Minz\Response; | ||
use flusio\models; | ||
|
||
/** | ||
* @author Marien Fressinaud <[email protected]> | ||
* @license http://www.gnu.org/licenses/agpl-3.0.en.html AGPL | ||
*/ | ||
class FeatureFlags | ||
{ | ||
/** | ||
* List all the available feature flags types | ||
* | ||
* @response 200 | ||
*/ | ||
public function index($request) | ||
{ | ||
$types = models\FeatureFlag::VALID_TYPES; | ||
|
||
if ($types) { | ||
return Response::text(200, implode("\n", $types)); | ||
} else { | ||
return Response::text(200, 'No types are available'); | ||
} | ||
} | ||
|
||
/** | ||
* List all the users for which feature flags are enabled | ||
* | ||
* @response 200 | ||
*/ | ||
public function flags($request) | ||
{ | ||
$feature_flags = models\FeatureFlag::listAll(); | ||
$output = []; | ||
foreach ($feature_flags as $feature_flag) { | ||
$user = $feature_flag->user(); | ||
$output[] = "{$feature_flag->type} {$user->id} {$user->email}"; | ||
} | ||
sort($output); | ||
|
||
if (!$output) { | ||
$output[] = 'No feature flags'; | ||
} | ||
|
||
return Response::text(200, implode("\n", $output)); | ||
} | ||
|
||
/** | ||
* Enable a feature flag for a given user | ||
* | ||
* @request_param string type | ||
* @request_param string user_id | ||
* | ||
* @response 400 if type is invalid | ||
* @response 404 if user doesn’t exist | ||
* @response 200 | ||
*/ | ||
public function enable($request) | ||
{ | ||
$type = $request->param('type'); | ||
$user_id = $request->param('user_id'); | ||
|
||
if (!models\FeatureFlag::validateType($type)) { | ||
return Response::text(400, "{$type} is not a valid feature flag type"); | ||
} | ||
|
||
$user = models\User::find($user_id); | ||
if (!$user) { | ||
return Response::text(404, "User {$user_id} doesn’t exist"); | ||
} | ||
|
||
models\FeatureFlag::findOrCreateBy([ | ||
'type' => $type, | ||
'user_id' => $user->id, | ||
]); | ||
|
||
return Response::text(200, "{$type} is enabled for user {$user->id} ({$user->email})"); | ||
} | ||
|
||
/** | ||
* Disable a feature flag for a given user | ||
* | ||
* @request_param string type | ||
* @request_param string user_id | ||
* | ||
* @response 400 if the flag isn't enabled | ||
* @response 404 if user doesn’t exist | ||
* @response 200 | ||
*/ | ||
public function disable($request) | ||
{ | ||
$type = $request->param('type'); | ||
$user_id = $request->param('user_id'); | ||
|
||
$user = models\User::find($user_id); | ||
if (!$user) { | ||
return Response::text(404, "User {$user_id} doesn’t exist"); | ||
} | ||
|
||
$feature_flag = models\FeatureFlag::findBy([ | ||
'type' => $type, | ||
'user_id' => $user->id, | ||
]); | ||
if (!$feature_flag) { | ||
return Response::text(400, "Feature flag {$type} isn’t enabled for user {$user_id}"); | ||
} | ||
|
||
models\FeatureFlag::delete($feature_flag->id); | ||
|
||
return Response::text(200, "{$type} is disabled for user {$user->id} ({$user->email})"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
36 changes: 36 additions & 0 deletions
36
src/migrations/Migration202104190001CreateFeatureFlags.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<?php | ||
|
||
namespace flusio\migrations; | ||
|
||
class Migration202104190001CreateFeatureFlags | ||
{ | ||
public function migrate() | ||
{ | ||
$database = \Minz\Database::get(); | ||
|
||
$database->exec(<<<'SQL' | ||
CREATE TABLE feature_flags ( | ||
id SERIAL PRIMARY KEY, | ||
created_at TIMESTAMPTZ NOT NULL, | ||
type TEXT NOT NULL, | ||
user_id TEXT REFERENCES users ON DELETE CASCADE ON UPDATE CASCADE | ||
); | ||
CREATE UNIQUE INDEX idx_feature_flags_type_user_id ON feature_flags(type, user_id); | ||
SQL); | ||
|
||
return true; | ||
} | ||
|
||
public function rollback() | ||
{ | ||
$database = \Minz\Database::get(); | ||
|
||
$database->exec(<<<'SQL' | ||
DROP INDEX idx_feature_flags_type_user_id; | ||
DROP TABLE feature_flags; | ||
SQL); | ||
|
||
return true; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
<?php | ||
|
||
namespace flusio\models; | ||
|
||
/** | ||
* @author Marien Fressinaud <[email protected]> | ||
* @license http://www.gnu.org/licenses/agpl-3.0.en.html AGPL | ||
*/ | ||
class FeatureFlag extends \Minz\Model | ||
{ | ||
use DaoConnector; | ||
|
||
public const VALID_TYPES = ['feeds']; | ||
|
||
public const PROPERTIES = [ | ||
'id' => [ | ||
'type' => 'integer', | ||
], | ||
|
||
'created_at' => [ | ||
'type' => 'datetime', | ||
], | ||
|
||
'type' => [ | ||
'type' => 'string', | ||
'required' => true, | ||
'validator' => '\flusio\models\FeatureFlag::validateType', | ||
], | ||
|
||
'user_id' => [ | ||
'type' => 'string', | ||
'required' => true, | ||
], | ||
]; | ||
|
||
/** | ||
* Return the user associated to the feature flag. | ||
* | ||
* @return \flusio\models\User | ||
*/ | ||
public function user() | ||
{ | ||
return User::find($this->user_id); | ||
} | ||
|
||
/** | ||
* @param string $type | ||
* | ||
* @return boolean | ||
*/ | ||
public static function validateType($type) | ||
{ | ||
return in_array($type, self::VALID_TYPES); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<?php | ||
|
||
namespace flusio\models\dao; | ||
|
||
/** | ||
* @author Marien Fressinaud <[email protected]> | ||
* @license http://www.gnu.org/licenses/agpl-3.0.en.html AGPL | ||
*/ | ||
class FeatureFlag extends \Minz\DatabaseModel | ||
{ | ||
/** | ||
* @throws \Minz\Errors\DatabaseError | ||
*/ | ||
public function __construct() | ||
{ | ||
$properties = array_keys(\flusio\models\FeatureFlag::PROPERTIES); | ||
parent::__construct('feature_flags', 'id', $properties); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.