occ config:system:set can now set other value types

Integers, doubles, booleans and even arrays can now be set, with the
--type=... option. Array setting can be specified by passing multiple
name arguments, e.g. `./occ config:system:set redis port --value=123
--type=integer`
This commit is contained in:
Robin McCorkell 2015-08-20 00:13:20 +01:00 committed by Joas Schilling
parent 8f89e3520d
commit b9d384d837
2 changed files with 233 additions and 53 deletions

View File

@ -48,8 +48,15 @@ class SetConfig extends Base {
->setDescription('Set a system config value')
->addArgument(
'name',
InputArgument::REQUIRED,
'Name of the config to set'
InputArgument::REQUIRED | InputArgument::IS_ARRAY,
'Name of the config parameter, specify multiple for array parameter'
)
->addOption(
'type',
null,
InputOption::VALUE_REQUIRED,
'Value type [string, integer, double, boolean]',
'string'
)
->addOption(
'value',
@ -67,16 +74,119 @@ class SetConfig extends Base {
}
protected function execute(InputInterface $input, OutputInterface $output) {
$configName = $input->getArgument('name');
$configNames = $input->getArgument('name');
$configName = $configNames[0];
$configValue = $this->castValue($input->getOption('value'), $input->getOption('type'));
$updateOnly = $input->getOption('update-only');
if (!in_array($configName, $this->systemConfig->getKeys()) && $input->hasParameterOption('--update-only')) {
$output->writeln('<comment>Config value ' . $configName . ' not updated, as it has not been set before.</comment>');
return 1;
if (count($configNames) > 1) {
$existingValue = $this->systemConfig->getValue($configName);
$newValue = $this->mergeArrayValue(
array_slice($configNames, 1), $existingValue, $configValue, $updateOnly
);
$this->systemConfig->setValue($configName, $newValue);
} else {
if ($updateOnly && !in_array($configName, $this->systemConfig->getKeys(), true)) {
throw new \UnexpectedValueException('Config parameter does not exist');
}
$this->systemConfig->setValue($configName, $configValue);
}
$configValue = $input->getOption('value');
$this->systemConfig->setValue($configName, $configValue);
$output->writeln('<info>System config value ' . $configName . ' set to ' . $configValue . '</info>');
$output->writeln('<info>System config value ' . implode(' => ', $configNames) . ' set to ' . $configValue . '</info>');
return 0;
}
/**
* @param string $value
* @param string $type
* @return mixed
* @throws \InvalidArgumentException
*/
protected function castValue($value, $type) {
if ($value === null) {
return null;
}
$type = strtolower($type);
switch ($type) {
case 'string':
case 'str':
case 's':
return $value;
case 'integer':
case 'int':
case 'i':
if (!is_numeric($value)) {
throw new \InvalidArgumentException('Non-numeric value specified');
}
return (int) $value;
case 'double':
case 'd':
case 'float':
case 'f':
if (!is_numeric($value)) {
throw new \InvalidArgumentException('Non-numeric value specified');
}
return (double) $value;
case 'boolean':
case 'bool':
case 'b':
$value = strtolower($value);
switch ($value) {
case 'true':
case 'yes':
case 'y':
case '1':
return true;
case 'false':
case 'no':
case 'n':
case '0':
return false;
default:
throw new \InvalidArgumentException('Unable to parse value as boolean');
}
default:
throw new \InvalidArgumentException('Invalid type');
}
}
/**
* @param array $configNames
* @param mixed $existingValues
* @param mixed $value
* @param bool $updateOnly
* @return array merged value
* @throws \UnexpectedValueException
*/
protected function mergeArrayValue(array $configNames, $existingValues, $value, $updateOnly) {
$configName = array_shift($configNames);
if (!is_array($existingValues)) {
$existingValues = [];
}
if (!empty($configNames)) {
if (isset($existingValues[$configName])) {
$existingValue = $existingValues[$configName];
} else {
$existingValue = [];
}
$existingValues[$configName] = $this->mergeArrayValue($configNames, $existingValue, $value, $updateOnly);
} else {
if (!isset($existingValues[$configName]) && $updateOnly) {
throw new \UnexpectedValueException('Config parameter does not exist');
}
$existingValues[$configName] = $value;
}
return $existingValues;
}
}

View File

@ -53,63 +53,133 @@ class SetConfigTest extends TestCase {
public function setData() {
return [
[
'name',
'newvalue',
true,
true,
true,
'info',
],
[
'name',
'newvalue',
false,
true,
false,
'comment',
],
[['name'], 'newvalue', null, 'newvalue'],
[['a', 'b', 'c'], 'foobar', null, ['b' => ['c' => 'foobar']]],
[['a', 'b', 'c'], 'foobar', ['b' => ['d' => 'barfoo']], ['b' => ['d' => 'barfoo', 'c' => 'foobar']]],
];
}
/**
* @dataProvider setData
*
* @param string $configName
* @param mixed $newValue
* @param bool $configExists
* @param bool $updateOnly
* @param bool $updated
* @param string $expectedMessage
* @param array $configNames
* @param string $newValue
* @param mixed $existingData
* @param mixed $expectedValue
*/
public function testSet($configName, $newValue, $configExists, $updateOnly, $updated, $expectedMessage) {
public function testSet($configNames, $newValue, $existingData, $expectedValue) {
$this->systemConfig->expects($this->once())
->method('getKeys')
->willReturn($configExists ? [$configName] : []);
if ($updated) {
$this->systemConfig->expects($this->once())
->method('setValue')
->with($configName, $newValue);
}
->method('setValue')
->with($configNames[0], $expectedValue);
$this->systemConfig->method('getValue')
->with($configNames[0])
->willReturn($existingData);
$this->consoleInput->expects($this->once())
->method('getArgument')
->with('name')
->willReturn($configName);
$this->consoleInput->expects($this->any())
->method('getOption')
->with('value')
->willReturn($newValue);
$this->consoleInput->expects($this->any())
->method('hasParameterOption')
->with('--update-only')
->willReturn($updateOnly);
$this->consoleOutput->expects($this->any())
->method('writeln')
->with($this->stringContains($expectedMessage));
->willReturn($configNames);
$this->consoleInput->method('getOption')
->will($this->returnValueMap([
['value', $newValue],
['type', 'string'],
]));
$this->invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
}
public function setUpdateOnlyProvider() {
return [
[['name'], null],
[['a', 'b', 'c'], null],
[['a', 'b', 'c'], ['b' => 'foobar']],
[['a', 'b', 'c'], ['b' => ['d' => 'foobar']]],
];
}
/**
* @dataProvider setUpdateOnlyProvider
* @expectedException \UnexpectedValueException
*/
public function testSetUpdateOnly($configNames, $existingData) {
$this->systemConfig->expects($this->never())
->method('setValue');
$this->systemConfig->method('getValue')
->with($configNames[0])
->willReturn($existingData);
$this->systemConfig->method('getKeys')
->willReturn($existingData ? $configNames[0] : []);
$this->consoleInput->expects($this->once())
->method('getArgument')
->with('name')
->willReturn($configNames);
$this->consoleInput->method('getOption')
->will($this->returnValueMap([
['value', 'foobar'],
['type', 'string'],
['update-only', true],
]));
$this->invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
}
public function castValueProvider() {
return [
[null, 'integer', null],
[null, 'string', null],
['abc', 'string', 'abc'],
['dEF', 'str', 'dEF'],
['123', 's', '123'],
['123', 'integer', 123],
['456', 'int', 456],
['-666', 'i', -666],
// only use powers of 2 to avoid precision errors
['2', 'double', 2.0],
['0.25', 'd', 0.25],
['0.5', 'float', 0.5],
['0.125', 'f', 0.125],
['true', 'boolean', true],
['false', 'bool', false],
['yes', 'b', true],
['no', 'b', false],
['y', 'b', true],
['n', 'b', false],
['1', 'b', true],
['0', 'b', false],
];
}
/**
* @dataProvider castValueProvider
*/
public function testCastValue($value, $type, $expectedValue) {
$this->assertSame($expectedValue,
$this->invokePrivate($this->command, 'castValue', [$value, $type])
);
}
public function castValueInvalidProvider() {
return [
['123', 'foobar'],
['abc', 'integer'],
['76ggg', 'double'],
['true', 'float'],
['foobar', 'boolean'],
];
}
/**
* @dataProvider castValueInvalidProvider
* @expectedException \InvalidArgumentException
*/
public function testCastValueInvalid($value, $type) {
$this->invokePrivate($this->command, 'castValue', [$value, $type]);
}
}