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
Feature flags improvements #32923
Feature flags improvements #32923
Conversation
6740a71
to
4aab36d
Compare
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.
looks good to me 👍
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.
Nice improvements 👍
src/PrestaShopBundle/Entity/Repository/FeatureFlagRepository.php
Outdated
Show resolved
Hide resolved
73fefbc
to
f320ce1
Compare
f320ce1
to
a622bb4
Compare
b616f07
to
7c01599
Compare
7c01599
to
15ecae8
Compare
What's going on there @jolelievre ? QA approved, no merge and further reviews? 🤔 Is it ok to merge it or not? |
@kpodemski not ready to be merged no, I performed the QA by dev but I didn't see there was only one approval And @lartist made relevant comments since then, so still in progress |
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.
👍
*/ | ||
public function isReadonly(): bool | ||
{ | ||
// It's always editable via DB layer! |
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.
what if database is read only
if ($pathDotenv = $this->locateDotEnvFile($featureFlagName)) { | ||
file_put_contents( | ||
$pathDotenv, | ||
preg_replace( | ||
"/({$this->getVarName($featureFlagName)})=(.*)/", | ||
"$1={$this->boolLabel($status)}", | ||
file_get_contents($pathDotenv) | ||
) | ||
); | ||
|
||
return; | ||
} |
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 there is a few other PHP classes in prestashop that perform this operation "modify a key/value file row". Maybe this can be abstracted
private function setStatus(string $featureFlagName, bool $status): void
{
$this->myAwesomeFileManager->modifyLineValue($this->getVarName($featureFlagName), $this->boolLabel($status));
https://github.com/PrestaShop/PrestaShop/blob/develop/src/Adapter/Debug/DebugMode.php#L191
c479cca
to
4aa6c6c
Compare
admin-dev/index.php
Outdated
@@ -38,6 +39,7 @@ | |||
define('PS_ADMIN_DIR', _PS_ADMIN_DIR_); | |||
} | |||
|
|||
//define('_PS_MODE_DEV_', 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.
//define('_PS_MODE_DEV_', false); |
/** | ||
* Type consts | ||
*/ | ||
public const TYPE_DEFAULT = 'env,query,dotenv,db'; |
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.
public const TYPE_DEFAULT = 'env,query,dotenv,db'; | |
public const TYPE_DEFAULT = 'env,dotenv,db'; |
$filesToCheck = [".env.$env.local", ".env.$env", '.env']; | ||
|
||
foreach ($filesToCheck as $file) { | ||
$path = $this->configuration->get('_PS_ROOT_DIR_') . '/' . $file; |
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 should be injected in the constructor and in the DI you can inject the const
if ($pathDotenv = $this->locateDotEnvFile($featureFlagName)) { | ||
file_put_contents( | ||
$pathDotenv, | ||
preg_replace( | ||
"/({$this->getVarName($featureFlagName)})=(.*)/", | ||
"$1={$this->boolLabel($status)}", | ||
file_get_contents($pathDotenv) | ||
) | ||
); | ||
|
||
return; | ||
} |
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 is a very specific code, it's not really a good practice in the first place so moving this into a dedicated util class would incite doing this in other places We don't want to encourage this kind of thing so keeping this only in this context seems safer
Here we allow it only because it can only impact .env variables related to feature flags, since it's based on a prefix it can't modify any other future variable
{ | ||
public function __construct( | ||
private EnvironmentInterface $environment, | ||
private ?Request $request = null |
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.
private ?Request $request = null | |
private ?RequestStack $requestStack = null |
You can then use $requestStack->getMainRequest()
which can be null, so in this case this layer becomes unusabled
{ | ||
private const FEATURE_FLAG_TEST = 'feature_flag_test'; | ||
private const VAR_FEATURE_FLAG_TEST = 'PS_FF_FEATURE_FLAG_TEST'; | ||
private const DOTENV_PATH = _PS_ROOT_DIR_ . '/.env.unit.local'; |
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.
You can inject another folder that is part of test resources
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.
private const DOTENV_PATH = _PS_ROOT_DIR_ . '/.env.unit.local'; | |
private const DOTENV_PATH = _PS_ROOT_DIR_ . '/tests/Resources/env/.env.unit.local'; |
yield ['no']; | ||
} | ||
|
||
public function createLayer(): QueryLayer |
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.
public function createLayer(): QueryLayer | |
private function createLayer(): QueryLayer |
$this->resetEnv(); | ||
} | ||
|
||
private function resetEnv(): void |
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 be moved at the end of the file as well
|
||
private function modeDev(bool $status): void | ||
{ | ||
static::$environment = new Environment($status, 'unit'); |
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.
You should use some newly constructed mocks in each test functions instead of relying on static fields
@@ -57,6 +57,8 @@ public function testAssertFeatureFlagProperties() | |||
$this->assertEquals('A.B.C', $featureFlag->getDescriptionDomain()); | |||
$this->assertEquals('a b c d', $featureFlag->getLabelWording()); | |||
$this->assertEquals('A.B.L', $featureFlag->getLabelDomain()); | |||
$this->assertEquals('env,query,dotenv,db', $featureFlag->getType()); |
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->assertEquals('env,query,dotenv,db', $featureFlag->getType()); | |
$this->assertEquals('env,dotenv,db', $featureFlag->getType()); |
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.
Unless you force the value in your test entity which is also fine here
private function resetEnv(): void | ||
{ | ||
unset($_ENV[self::VAR_FEATURE_FLAG_TEST]); | ||
$_ENV['SYMFONY_DOTENV_VARS'] = static::$save_dotenv_vars; |
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.
$_ENV['SYMFONY_DOTENV_VARS'] = static::$save_dotenv_vars; | |
$_ENV['SYMFONY_DOTENV_VARS'] = static::$save_dotenv_vars; | |
unlink(self::DOTENV_PATH); |
{ | ||
private const FEATURE_FLAG_TEST = 'feature_flag_test'; | ||
private const VAR_FEATURE_FLAG_TEST = 'PS_FF_FEATURE_FLAG_TEST'; | ||
private const DOTENV_PATH = _PS_ROOT_DIR_ . '/.env.unit.local'; |
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.
private const DOTENV_PATH = _PS_ROOT_DIR_ . '/.env.unit.local'; | |
private const DOTENV_PATH = _PS_ROOT_DIR_ . '/tests/Resources/env/.env.unit.local'; |
.env
Outdated
@@ -0,0 +1 @@ | |||
PS_FF_PRODUCT_PAGE_V2=true |
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.
Should be empty (maybe add a comment to explain why this file is added following the Symfony convention, but we have no default values to force for now)
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'm more infavor of using a .env.dist
file though, it follows a convention that is widely used in many other projects so I don't really understand why we should use .env
as our dist file
Especially since DotEnv
does handle the .env.dist
file as its default fallback
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.
Ok after comparing a few projects it seems like Symfony projects mostly follow the .env
convention It feels kind of weird but I guess we'd better follow the framework conventions
4aa6c6c
to
2895629
Compare
2895629
to
21b71ab
Compare
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.
Thanks @boherm
Validated by QA by dev waiting for https://github.com/jolelievre/ga.tests.ui.pr/actions/runs/5519072374 before merge |
What I have done?
type
column. exemple: If a feature flag hasenv,query,dotenv,db
in type column, this class will check if it can be possible to get a value from environnement, if not, it check if we can retrieve value by query, if not it check if we can use dotenv file, and if not, we use DB (that can be always used!)type
display inNew & experimental features
page, and the handler used is in bold for each feature flags.type
display inprestashop:feature-flags list
command, and the handler used is surrounded by brackets.FeatureFlagStateCheckerInterface
to retrieve feature flag states simply.Database Layer (type:
db
)DotEnv Layer (type
dotenv
).env
files in function of current environnement (dev, prod, etc...).New & experimental features
page, and the Handler will localise the.env
file used to set this feature flag.PS_FF_{UPPERCASE FEATURE FLAG NAME}
.Env Layer (type
env
)PS_FF_{UPPERCASE FEATURE FLAG NAME}
.Query Layer (type
query
)PS_FF_{UPPERCASE FEATURE FLAG NAME}
.How to test?
db
type only.PREFIX_feature_flag
table to addenv,query,dotenv,db
on the one you want.SetEnv PS_FF_{FEATURE_FLAG_NAME} true
.New & experimental features
page the value of this feature flag and the handler used!PS_FF_{FEATURE_FLAG_NAME}=true
.New & experimental features
page the value of this feature flag and the handler used!.env
file in project root, and addPS_FF_{FEATURE_FLAG_NAME}=true
in it.New & experimental features
page the value of this feature flag and the handler used!.env
file has changed.