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

Allow to KEEP configs unchanged on the environment #75

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
69 changes: 49 additions & 20 deletions Model/Processor/ImportProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
class ImportProcessor extends AbstractProcessor implements ImportProcessorInterface
{
private const DELETE_CONFIG_FLAG = '!!DELETE';
private const KEEP_CONFIG_FLAG = '!!KEEP';

/**
* @var WriterInterface
*/
Expand Down Expand Up @@ -92,39 +94,66 @@ public function process()
$this->getOutput()->writeln('Maybe this is expected behaviour, because you passed the --allow-empty-directories option.');
}

$configs = $this->collectConfigs($files);

foreach ($configs as $configPath => $configValue) {
foreach ($configValue as $scopeType => $scopeValue) {
foreach ($scopeValue as $scopeId => $value) {

if ($value === self::DELETE_CONFIG_FLAG) {
$this->configWriter->delete($configPath, $scopeType, $scopeId);
$this->getOutput()->writeln(sprintf('<comment>[%s] [%s] %s => %s</comment>', $scopeType, $scopeId, $configPath, 'DELETED'));

continue;
}

if ($value === self::KEEP_CONFIG_FLAG) {
$this->getOutput()->writeln(sprintf('<comment>[%s] [%s] %s => %s</comment>', $scopeType, $scopeId, $configPath, 'KEPT'));

continue;
}

$this->configWriter->save($configPath, $value, $scopeType, $scopeId);
$this->getOutput()->writeln(sprintf('<comment>[%s] [%s] %s => %s</comment>', $scopeType, $scopeId, $configPath, $value));
}
}
}
}

/**
* @param array $files
*
* @return array
*/
private function collectConfigs(array $files): array
{
$buffer = [];

foreach ($files as $file) {
$valuesSet = 0;
$configurations = $this->getConfigurationsFromFile($file);

foreach ($configurations as $configPath => $configValues) {
$scopeConfigValues = $this->transformConfigToScopeConfig($configPath, $configValues);
foreach ($scopeConfigValues as $scopeConfigValue) {
if ($scopeConfigValue['value'] === self::DELETE_CONFIG_FLAG) {
$this->configWriter->delete(
$configPath,
$scopeConfigValue['scope'],
$this->scopeConverter->convert($scopeConfigValue['scope_id'], $scopeConfigValue['scope'])
);

$this->getOutput()->writeln(sprintf('<comment>%s => %s</comment>', $configPath, 'DELETED'));
$valuesSet++;
if (!isset($buffer[$configPath])) {
$buffer[$configPath] = [];
}

continue;
}
$scopeConfigValues = $this->transformConfigToScopeConfig($configPath, $configValues);

$this->configWriter->save(
$configPath,
$scopeConfigValue['value'],
$scopeConfigValue['scope'],
$this->scopeConverter->convert($scopeConfigValue['scope_id'], $scopeConfigValue['scope'])
);
foreach ($scopeConfigValues as $scopeConfigValue) {

$this->getOutput()->writeln(sprintf('<comment>%s => %s</comment>', $configPath, $scopeConfigValue['value']));
$scopeType = $scopeConfigValue['scope'];
$scopeId = $this->scopeConverter->convert($scopeConfigValue['scope_id'], $scopeConfigValue['scope']);
$buffer[$configPath][$scopeType][$scopeId] = $scopeConfigValue['value'];
$valuesSet++;
}
}

$this->getOutput()->writeln(sprintf('<info>Processed: %s with %s value(s).</info>', $file, $valuesSet));
$this->getOutput()->writeln(sprintf('<info>Configs collected: %s with %s value(s).</info>', $file, $valuesSet));
}

return $buffer;
}

/**
Expand Down
16 changes: 15 additions & 1 deletion Test/Unit/Model/Processor/ImportProcessorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Semaio\ConfigImportExport\Model\File\Reader\YamlReader;
use Semaio\ConfigImportExport\Model\Processor\ImportProcessor;
use Semaio\ConfigImportExport\Model\Validator\ScopeValidatorInterface;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class ImportProcessorTest extends TestCase
Expand Down Expand Up @@ -67,6 +68,9 @@ public function processWithoutFiles(): void
$this->expectException(InvalidArgumentException::class);

$processor = new ImportProcessor($this->configWriterMock, $this->scopeValidatorMock, $this->scopeConverterMock, []);
$inputMock = $this->getMockBuilder(InputInterface::class)->getMock();
$inputMock->method('getOption')->with('allow-empty-directories')->willReturn(false);
$processor->setInput($inputMock);
$processor->setFinder($finderMock);
$processor->process();
}
Expand Down Expand Up @@ -126,14 +130,24 @@ public function process(): void
0 => '!!DELETE',
],
],
'test/config/custom_field_to_be_keeped' => [
'default' => [
0 => 'VALUE_THAT_SHOULD_NOT_BE_PROCESSED',
],
],
'test/config/custom_field_to_be_keeped' => [
'default' => [
0 => '!!KEEP',
],
],
];

$readerMock = $this->getMockBuilder(YamlReader::class)
->onlyMethods(['parse'])
->getMock();
$readerMock->expects($this->once())->method('parse')->willReturn($parseResult);

$this->scopeValidatorMock->expects($this->exactly(2))->method('validate')->willReturn(true);
$this->scopeValidatorMock->expects($this->exactly(3))->method('validate')->willReturn(true);
$this->configWriterMock->expects($this->once())->method('save');
$this->configWriterMock->expects($this->once())->method('delete');

Expand Down
15 changes: 15 additions & 0 deletions docs/config-import.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,21 @@ vendorx/general/api_key:
0: "!!DELETE"
```

### Keep Config

If you want to make sure that config value will not be affected by config importer - use `!!KEEP` magic-ish string.

#### When would it be helpful?
When you have a bunch of same configs in different environments, but there one environment ( `X` env ) where you want to keep it as is, but not dispose the exact value of `X` env in config files.
Security sensitive data are good examples of such cases - you want them to be kept only in env DB, and definitely not in your GIT repo.

```yaml
vendorx/general/api_key:
default:
0: "!!KEEP"
```


### Recursive folder setup

If you choose to store your configuration files in subdirectories, e.g. per vendor, the recommended folder setup should look like this:
Expand Down
Loading