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

Feature/env variables override config #3842

Closed
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
618d0b3
wip: override config from env vars
Feb 4, 2024
1824a0d
docs and phpcs changes
Feb 4, 2024
1e1d30f
feat: refactor config environment loader
Feb 11, 2024
b6c75ed
add unittest and some changes for mocking and making the logic work
Flyingmana Feb 18, 2024
c6eabc3
tests: finish some more tests w/ website and store data overrides
Feb 18, 2024
63a9c95
Merge branch 'main' into feature/ENV-variables-override-config
fballiano Feb 19, 2024
f6743b3
Merge branch 'main' into feature/ENV-variables-override-config
fballiano Feb 25, 2024
9f85ccd
Changed "unload" to "beforeunload" in extjs to avoid deprecation warn…
andrewcbi Feb 25, 2024
cfac8e0
Bump phpunit/phpunit from 9.6.16 to 9.6.17 (#3854)
dependabot[bot] Feb 26, 2024
9ec1e69
Bump phpseclib/mcrypt_compat from 2.0.4 to 2.0.5 (#3855)
dependabot[bot] Feb 26, 2024
0ea5dd7
Bump friendsofphp/php-cs-fixer from 3.49.0 to 3.50.0 (#3857)
dependabot[bot] Feb 26, 2024
3f6db4d
Removed Mage_Backup and Mage_PageCache from PHPStan configuration (#3…
fballiano Feb 27, 2024
6f441b5
New feature: added support for type "label" to system.xml (#3849)
fballiano Feb 27, 2024
cbf384a
Bump phpstan/phpstan from 1.10.58 to 1.10.59 (#3856)
dependabot[bot] Feb 27, 2024
fef1c08
Fixed column default sorting in customer's orders grid (#3852)
addison74 Feb 27, 2024
63293af
Fixed null parameter warning in DataFlow Profiles (#3827)
addison74 Feb 27, 2024
8d00914
Merge pull request from GHSA-gp6m-fq6h-cjcx
Judx Feb 27, 2024
4176402
Version bump in preparation for next release
fballiano Feb 27, 2024
5b84ee1
PHPCS fix for commit 8d00914fb5238efbb242d8d95b395e35b9a21fe4
fballiano Feb 27, 2024
40dcee7
Fixed zIndex to avoid conflicts between TinyMCE and OpenMage's overla…
fballiano Feb 28, 2024
94f08e1
Fixed default width for input/textarea in OpenMage backend theme (#3850)
fballiano Feb 29, 2024
ac50a6a
Improved fields dependence (show/hide) in backend's config section (#…
luigifab Mar 2, 2024
50bdaf8
wip: override config from env vars
Feb 4, 2024
67b82e7
docs and phpcs changes
Feb 4, 2024
fe15216
feat: refactor config environment loader
Feb 11, 2024
2da4951
add unittest and some changes for mocking and making the logic work
Flyingmana Feb 18, 2024
3eda641
tests: finish some more tests w/ website and store data overrides
Feb 18, 2024
b2abcf0
Merge branch 'feature/ENV-variables-override-config' of https://githu…
Mar 2, 2024
b3da428
fix: PHP8 test error when constructing Mage_Core_Model_Config
Mar 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 132 additions & 0 deletions app/code/core/Mage/Core/Helper/EnvironmentConfigLoader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<?php
/**
* OpenMage
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available at https://opensource.org/license/osl-3-0-php
*
* @category Mage
* @package Mage_Core
* @copyright Copyright (c) 2006-2020 Magento, Inc. (https://www.magento.com)
* @copyright Copyright (c) 2016-present The OpenMage Contributors (https://www.openmage.org)
* @license https://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
*/

/**
* Core Environment helper
*
* @category Mage
* @package Mage_Core
*/
class Mage_Core_Helper_EnvironmentConfigLoader extends Mage_Core_Helper_Abstract
{
protected $_moduleName = 'Mage_Core';
protected const ENV_STARTS_WITH = 'OPENMAGE_CONFIG';
protected const ENV_KEY_SEPARATOR = '__';
protected const CONFIG_KEY_DEFAULT = 'DEFAULT';
protected const CONFIG_KEY_WEBSITES = 'WEBSITES';
protected const CONFIG_KEY_STORES = 'STORES';

protected array $envStore = [];

/**
* Load configuration values from ENV variables into xml config object
*
* Environment variables work on this schema:
*
* self::ENV_STARTS_WITH . self::ENV_KEY_SEPARATOR (OPENMAGE_CONFIG__)
* ^ Prefix (required)
* <SCOPE>__
* ^ Where scope is DEFAULT, WEBSITES__<WEBSITE_CODE> or STORES__<STORE_CODE>
* <SYSTEM_VARIABLE_NAME>
* ^ Where GROUP, SECTION and FIELD are separated by self::ENV_KEY_SEPARATOR
*
* Each example will override the 'general/store_information/name' value.
* Override from the default configuration:
* @example OPENMAGE_CONFIG__DEFAULT__GENERAL__STORE_INFORMATION__NAME=default
* Override the website 'base' configuration:
* @example OPENMAGE_CONFIG__WEBSITES__BASE__GENERAL__STORE_INFORMATION__NAME=website
* Override the store 'german' configuration:
* @example OPENMAGE_CONFIG__STORES__GERMAN__GENERAL__STORE_INFORMATION__NAME=store_german
*
* @param Mage_Core_Model_Config $xmlConfig
* @return void
*/
public function overrideEnvironment(Mage_Core_Model_Config $xmlConfig)
{
$env = $this->getEnv();

foreach ($env as $configKey => $value) {
if (!str_starts_with($configKey, static::ENV_STARTS_WITH)) {
continue;
}

$configKeyParts = array_filter(
explode(
static::ENV_KEY_SEPARATOR,
$configKey
),
'trim'
);
list($_, $scope) = $configKeyParts;

switch ($scope) {
case static::CONFIG_KEY_DEFAULT:
list($_, $_, $section, $group, $field) = $configKeyParts;
$path = $this->buildPath($section, $group, $field);
$xmlConfig->setNode($this->buildNodePath($scope, $path), $value);
break;

case static::CONFIG_KEY_WEBSITES:
case static::CONFIG_KEY_STORES:
list($_, $_, $code, $section, $group, $field) = $configKeyParts;
$path = $this->buildPath($section, $group, $field);
$nodePath = sprintf('%s/%s/%s', strtolower($scope), strtolower($code), $path);
$xmlConfig->setNode($nodePath, $value);
break;
}
}
}

/**
* @internal method mostly for mocking
*/
public function setEnvStore(array $envStorage): void
{
$this->envStore = $envStorage;
}

public function getEnv(): array
{
if (empty($this->envStore)) {
$this->envStore = getenv();
}
return $this->envStore;
}

/**
* Build configuration path.
*
* @param string $section
* @param string $group
* @param string $field
* @return string
*/
public function buildPath($section, $group, $field): string
{
return strtolower(implode('/', [$section, $group, $field]));
}

/**
* Build configuration node path.
*
* @param string $scope
* @param string $path
* @return string
*/
public function buildNodePath($scope, $path): string
{
return strtolower($scope) . '/' . $path;
}
}
1 change: 1 addition & 0 deletions app/code/core/Mage/Core/Model/App.php
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,7 @@ protected function _initModules()
Varien_Profiler::stop('mage::app::init::apply_db_schema_updates');
}
$this->_config->loadDb();
$this->_config->loadEnv();
$this->_config->saveCache();
}
} finally {
Expand Down
18 changes: 18 additions & 0 deletions app/code/core/Mage/Core/Model/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ public function init($options = [])
$this->_useCache = false;
$this->loadModules();
$this->loadDb();
$this->loadEnv();
$this->saveCache();
}
} finally {
Expand Down Expand Up @@ -422,6 +423,23 @@ public function loadDb()
return $this;
}

/**
* Load environment variables and override config
*
* @return self
*/
public function loadEnv(): Mage_Core_Model_Config
{
if ($this->_isLocalConfigLoaded && Mage::isInstalled()) {
Varien_Profiler::start('config/load-env');
/** @var Mage_Core_Helper_EnvironmentConfigLoader $environmentConfigLoaderHelper */
$environmentConfigLoaderHelper = Mage::helper('core/environmentConfigLoader');
$environmentConfigLoaderHelper->overrideEnvironment($this);
Varien_Profiler::stop('config/load-env');
}
return $this;
}

/**
* Reinitialize configuration
*
Expand Down
153 changes: 153 additions & 0 deletions dev/tests/unit/Mage/Core/Helper/EnvironmentConfigLoaderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
<?php
declare(strict_types=1);

namespace OpenMage\Tests\Unit\Core\Helper;

use PHPUnit\Framework\TestCase;
use Mage_Core_Helper_EnvironmentConfigLoader;
use Mage_Core_Model_Config;

class EnvironmentConfigLoaderTest extends TestCase
{
protected const ENV_CONFIG_DEFAULT_PATH = 'OPENMAGE_CONFIG__DEFAULT__GENERAL__STORE_INFORMATION__NAME';
protected const ENV_CONFIG_DEFAULT_VALUE = 'default_new_value';
protected const ENV_CONFIG_WEBSITE_PATH = 'OPENMAGE_CONFIG__WEBSITES__BASE__GENERAL__STORE_INFORMATION__NAME';
protected const ENV_CONFIG_WEBSITE_VALUE = 'website_new_value';
protected const ENV_CONFIG_STORE_PATH = 'OPENMAGE_CONFIG__STORES__GERMAN__GENERAL__STORE_INFORMATION__NAME';
protected const ENV_CONFIG_STORE_VALUE = 'store_german_new_value';

public function setup(): void
{
\Mage::setRoot('');
}

public function testBuildPath()
{
$environmentConfigLoaderHelper = new Mage_Core_Helper_EnvironmentConfigLoader();
$path = $environmentConfigLoaderHelper->buildPath('GENERAL', 'STORE_INFORMATION', 'NAME');
$this->assertEquals('general/store_information/name', $path);
}

public function testBuildNodePath()
{
$loader = new Mage_Core_Helper_EnvironmentConfigLoader();
$nodePath = $loader->buildNodePath('DEFAULT', 'general/store_information/name');
$this->assertEquals('default/general/store_information/name', $nodePath);
}

/**
* @dataProvider envOverridesDataProvider
*
*/
public function testEnvOverrides(array $config)
{
error_reporting(0);
$xmlStruct = $this->getTestXml();

$xmlDefault = new Mage_Core_Model_Config($xmlStruct);
$xmlDefault = $xmlDefault->loadModulesConfiguration('config.xml', $xmlDefault);
$xml = new Mage_Core_Model_Config($xmlStruct);
$xml = $xml->loadModulesConfiguration('config.xml', $xml);
fballiano marked this conversation as resolved.
Show resolved Hide resolved

$this->assertEquals('test_default', (string)$xml->getNode('default/general/store_information/name'));
$this->assertEquals('test_website', (string)$xml->getNode('websites/base/general/store_information/name'));
$this->assertEquals('test_store', (string)$xml->getNode('stores/german/general/store_information/name'));

// act
$loader = new Mage_Core_Helper_EnvironmentConfigLoader();
$loader->setEnvStore([
$config['path'] => $config['value']
]);
$loader->overrideEnvironment($xml);

switch ($config['case']) {
case 'DEFAULT':
$defaultValue = $xmlDefault->getNode('default/general/store_information/name');
$valueAfterOverride = $xml->getNode('default/general/store_information/name');
break;
case 'STORE':
$defaultValue = $xmlDefault->getNode('stores/german/general/store_information/name');
$valueAfterOverride = $xml->getNode('general/store_information/name');
break;
case 'WEBSITE':
$defaultValue = $xmlDefault->getNode('default/general/store_information/name');
$valueAfterOverride = $xml->getNode('website/base/store_information/name');
break;
}

// assert
$this->assertNotEquals((string)$defaultValue, (string)$valueAfterOverride, 'Default value was not overridden.');
}

public function envOverridesDataProvider(): array
{
return [
[
'Case DEFAULT with ' . static::ENV_CONFIG_DEFAULT_PATH . ' overrides.' => [
'case' => 'DEFAULT',
'path' => static::ENV_CONFIG_DEFAULT_PATH,
'value' => static::ENV_CONFIG_DEFAULT_VALUE
]
],
[
'Case STORE with ' . static::ENV_CONFIG_STORE_PATH . ' overrides.' => [
'case' => 'STORE',
'path' => static::ENV_CONFIG_STORE_PATH,
'value' => static::ENV_CONFIG_STORE_VALUE
]
],
[
'Case WEBSITE with ' . static::ENV_CONFIG_WEBSITE_PATH . ' overrides.' => [
'case' => 'WEBSITE',
'path' => static::ENV_CONFIG_WEBSITE_PATH,
'value' => static::ENV_CONFIG_WEBSITE_VALUE
]
]
];
}

/**
* @return string
*/
public function getTestXml(): string
{
return <<<XML
<?xml version="1.0" encoding="UTF-8" ?>
<config>
<modules>
<Mage_Core>
<active>true</active>
<codePool>core</codePool>
</Mage_Core>
</modules>

<default>
<general>
<store_information>
<name>test_default</name>
</store_information>
</general>
</default>

<websites>
<base>
<general>
<store_information>
<name>test_website</name>
</store_information>
</general>
</base>
</websites>
<stores>
<german>
<general>
<store_information>
<name>test_store</name>
</store_information>
</general>
</german>
</stores>
</config>
XML;
}
}
Loading