Add commands to get, set and delete app config values

This commit is contained in:
Joas Schilling 2015-06-10 17:06:51 +02:00
parent 130b5d8587
commit 2b86eb0b29
10 changed files with 673 additions and 3 deletions

View File

@ -0,0 +1,81 @@
<?php
/**
* @author Joas Schilling <nickvergessen@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Core\Command\Config\App;
use OC\Core\Command\Base;
use OCP\IConfig;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class DeleteConfig extends Base {
/** * @var IConfig */
protected $config;
/**
* @param IConfig $config
*/
public function __construct(IConfig $config) {
parent::__construct();
$this->config = $config;
}
protected function configure() {
parent::configure();
$this
->setName('config:app:delete')
->setDescription('Delete an app config value')
->addArgument(
'app',
InputArgument::REQUIRED,
'Name of the app'
)
->addArgument(
'name',
InputArgument::REQUIRED,
'Name of the config to delete'
)
->addOption(
'error-if-not-exists',
null,
InputOption::VALUE_NONE,
'Checks whether the config exists before deleting it'
)
;
}
protected function execute(InputInterface $input, OutputInterface $output) {
$appName = $input->getArgument('app');
$configName = $input->getArgument('name');
if ($input->hasParameterOption('--error-if-not-exists') && !in_array($configName, $this->config->getAppKeys($appName))) {
$output->writeln('<error>Config ' . $configName . ' could not be deleted because it did not exist</error>');
return 1;
}
$this->config->deleteAppValue($appName, $configName);
$output->writeln('<info>System config value ' . $configName . ' deleted</info>');
return 0;
}
}

View File

@ -0,0 +1,93 @@
<?php
/**
* @author Joas Schilling <nickvergessen@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Core\Command\Config\App;
use OC\Core\Command\Base;
use OCP\IConfig;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class GetConfig extends Base {
/** * @var IConfig */
protected $config;
/**
* @param IConfig $config
*/
public function __construct(IConfig $config) {
parent::__construct();
$this->config = $config;
}
protected function configure() {
parent::configure();
$this
->setName('config:app:get')
->setDescription('Set an app config value')
->addArgument(
'app',
InputArgument::REQUIRED,
'Name of the app'
)
->addArgument(
'name',
InputArgument::REQUIRED,
'Name of the config to get'
)
->addOption(
'default-value',
null,
InputOption::VALUE_OPTIONAL,
'If no default value is set and the config does not exist, the command will exit with 1'
)
;
}
/**
* Executes the current command.
*
* @param InputInterface $input An InputInterface instance
* @param OutputInterface $output An OutputInterface instance
* @return null|int null or 0 if everything went fine, or an error code
*/
protected function execute(InputInterface $input, OutputInterface $output) {
$appName = $input->getArgument('app');
$configName = $input->getArgument('name');
$defaultValue = $input->getOption('default-value');
if (!in_array($configName, $this->config->getAppKeys($appName)) && !$input->hasParameterOption('--default-value')) {
return 1;
}
if (!in_array($configName, $this->config->getAppKeys($appName))) {
$configValue = $defaultValue;
} else {
$configValue = $this->config->getAppValue($appName, $configName);
}
$this->writeMixedInOutputFormat($input, $output, $configValue);
return 0;
}
}

View File

@ -0,0 +1,89 @@
<?php
/**
* @author Joas Schilling <nickvergessen@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Core\Command\Config\App;
use OC\Core\Command\Base;
use OCP\IConfig;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class SetConfig extends Base {
/** * @var IConfig */
protected $config;
/**
* @param IConfig $config
*/
public function __construct(IConfig $config) {
parent::__construct();
$this->config = $config;
}
protected function configure() {
parent::configure();
$this
->setName('config:app:set')
->setDescription('Set an app config value')
->addArgument(
'app',
InputArgument::REQUIRED,
'Name of the app'
)
->addArgument(
'name',
InputArgument::REQUIRED,
'Name of the config to set'
)
->addOption(
'value',
null,
InputOption::VALUE_REQUIRED,
'The new value of the config'
)
->addOption(
'update-only',
null,
InputOption::VALUE_NONE,
'Only updates the value, if it is not set before, it is not being added'
)
;
}
protected function execute(InputInterface $input, OutputInterface $output) {
$appName = $input->getArgument('app');
$configName = $input->getArgument('name');
if (!in_array($configName, $this->config->getAppKeys($appName)) && $input->hasParameterOption('--update-only')) {
$output->writeln('<comment>Value not updated, as it has not been set before.</comment>');
return 1;
}
$configValue = $input->getOption('value');
$this->config->setAppValue($appName, $configName, $configValue);
$output->writeln('<info>Config value ' . $configName . ' for app ' . $appName . ' set to ' . $configValue . '</info>');
return 0;
}
}

View File

@ -49,7 +49,7 @@ class DeleteConfig extends Base {
->addArgument(
'name',
InputArgument::REQUIRED,
'Name of the config to set'
'Name of the config to delete'
)
->addOption(
'error-if-not-exists',

View File

@ -55,7 +55,7 @@ class GetConfig extends Base {
'default-value',
null,
InputOption::VALUE_OPTIONAL,
'If no default value is set and the config does not exist, an error is thrown'
'If no default value is set and the config does not exist, the command will exit with 1'
)
;
}

View File

@ -70,7 +70,7 @@ class SetConfig extends Base {
$configName = $input->getArgument('name');
if (!in_array($configName, $this->systemConfig->getKeys()) && $input->hasParameterOption('--update-only')) {
$output->writeln('<comment>Value has not been updated, as it has not been set before.</comment>');
$output->writeln('<comment>Value not updated, as it has not been set before.</comment>');
return 1;
}
$configValue = $input->getOption('value');

View File

@ -40,6 +40,9 @@ if (\OC::$server->getConfig()->getSystemValue('installed', false)) {
$application->add(new OC\Core\Command\Background\WebCron(\OC::$server->getConfig()));
$application->add(new OC\Core\Command\Background\Ajax(\OC::$server->getConfig()));
$application->add(new OC\Core\Command\Config\App\DeleteConfig(\OC::$server->getConfig()));
$application->add(new OC\Core\Command\Config\App\GetConfig(\OC::$server->getConfig()));
$application->add(new OC\Core\Command\Config\App\SetConfig(\OC::$server->getConfig()));
$application->add(new OC\Core\Command\Config\Import(\OC::$server->getConfig()));
$application->add(new OC\Core\Command\Config\ListConfigs(\OC::$server->getSystemConfig(), \OC::$server->getAppConfig()));
$application->add(new OC\Core\Command\Config\System\DeleteConfig(\OC::$server->getSystemConfig()));

View File

@ -0,0 +1,123 @@
<?php
/**
* @author Joas Schilling <nickvergessen@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace Tests\Core\Command\Config\App;
use OC\Core\Command\Config\App\DeleteConfig;
use Test\TestCase;
class DeleteConfigTest extends TestCase {
/** @var \PHPUnit_Framework_MockObject_MockObject */
protected $config;
/** @var \PHPUnit_Framework_MockObject_MockObject */
protected $consoleInput;
/** @var \PHPUnit_Framework_MockObject_MockObject */
protected $consoleOutput;
/** @var \Symfony\Component\Console\Command\Command */
protected $command;
protected function setUp() {
parent::setUp();
$config = $this->config = $this->getMockBuilder('OCP\IConfig')
->disableOriginalConstructor()
->getMock();
$this->consoleInput = $this->getMock('Symfony\Component\Console\Input\InputInterface');
$this->consoleOutput = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
/** @var \OCP\IConfig $config */
$this->command = new DeleteConfig($config);
}
public function deleteData() {
return [
[
'name',
true,
true,
0,
'info',
],
[
'name',
true,
false,
0,
'info',
],
[
'name',
false,
false,
0,
'info',
],
[
'name',
false,
true,
1,
'error',
],
];
}
/**
* @dataProvider deleteData
*
* @param string $configName
* @param bool $configExists
* @param bool $checkIfExists
* @param int $expectedReturn
* @param string $expectedMessage
*/
public function testDelete($configName, $configExists, $checkIfExists, $expectedReturn, $expectedMessage) {
$this->config->expects(($checkIfExists) ? $this->once() : $this->never())
->method('getAppKeys')
->with('app-name')
->willReturn($configExists ? [$configName] : []);
$this->config->expects(($expectedReturn === 0) ? $this->once() : $this->never())
->method('deleteAppValue')
->with('app-name', $configName);
$this->consoleInput->expects($this->exactly(2))
->method('getArgument')
->willReturnMap([
['app', 'app-name'],
['name', $configName],
]);
$this->consoleInput->expects($this->any())
->method('hasParameterOption')
->with('--error-if-not-exists')
->willReturn($checkIfExists);
$this->consoleOutput->expects($this->any())
->method('writeln')
->with($this->stringContains($expectedMessage));
$this->assertSame($expectedReturn, \Test_Helper::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]));
}
}

View File

@ -0,0 +1,163 @@
<?php
/**
* @author Joas Schilling <nickvergessen@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace Tests\Core\Command\Config\App;
use OC\Core\Command\Config\App\GetConfig;
use Test\TestCase;
class GetConfigTest extends TestCase {
/** @var \PHPUnit_Framework_MockObject_MockObject */
protected $config;
/** @var \PHPUnit_Framework_MockObject_MockObject */
protected $consoleInput;
/** @var \PHPUnit_Framework_MockObject_MockObject */
protected $consoleOutput;
/** @var \Symfony\Component\Console\Command\Command */
protected $command;
protected function setUp() {
parent::setUp();
$config = $this->config = $this->getMockBuilder('OCP\IConfig')
->disableOriginalConstructor()
->getMock();
$this->consoleInput = $this->getMock('Symfony\Component\Console\Input\InputInterface');
$this->consoleOutput = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
/** @var \OCP\IConfig $config */
$this->command = new GetConfig($config);
}
public function getData() {
return [
// String output as json
['name', 'newvalue', true, null, false, 'json', 0, json_encode('newvalue')],
// String output as plain text
['name', 'newvalue', true, null, false, 'plain', 0, 'newvalue'],
// String falling back to default output as json
['name', null, false, 'newvalue', true, 'json', 0, json_encode('newvalue')],
// String falling back without default: error
['name', null, false, null, false, 'json', 1, null],
// Int "0" output as json/plain
['name', 0, true, null, false, 'json', 0, json_encode(0)],
['name', 0, true, null, false, 'plain', 0, '0'],
// Int "1" output as json/plain
['name', 1, true, null, false, 'json', 0, json_encode(1)],
['name', 1, true, null, false, 'plain', 0, '1'],
// Bool "true" output as json/plain
['name', true, true, null, false, 'json', 0, json_encode(true)],
['name', true, true, null, false, 'plain', 0, 'true'],
// Bool "false" output as json/plain
['name', false, true, null, false, 'json', 0, json_encode(false)],
['name', false, true, null, false, 'plain', 0, 'false'],
// Null output as json/plain
['name', null, true, null, false, 'json', 0, json_encode(null)],
['name', null, true, null, false, 'plain', 0, 'null'],
// Array output as json/plain
['name', ['a', 'b'], true, null, false, 'json', 0, json_encode(['a', 'b'])],
['name', ['a', 'b'], true, null, false, 'plain', 0, "a\nb"],
// Key array output as json/plain
['name', [0 => 'a', 1 => 'b'], true, null, false, 'json', 0, json_encode(['a', 'b'])],
['name', [0 => 'a', 1 => 'b'], true, null, false, 'plain', 0, "a\nb"],
// Associative array output as json/plain
['name', ['a' => 1, 'b' => 2], true, null, false, 'json', 0, json_encode(['a' => 1, 'b' => 2])],
['name', ['a' => 1, 'b' => 2], true, null, false, 'plain', 0, "a: 1\nb: 2"],
];
}
/**
* @dataProvider getData
*
* @param string $configName
* @param mixed $value
* @param bool $configExists
* @param mixed $defaultValue
* @param bool $hasDefault
* @param string $outputFormat
* @param int $expectedReturn
* @param string $expectedMessage
*/
public function testGet($configName, $value, $configExists, $defaultValue, $hasDefault, $outputFormat, $expectedReturn, $expectedMessage) {
$this->config->expects($this->atLeastOnce())
->method('getAppKeys')
->with('app-name')
->willReturn($configExists ? [$configName] : []);
if (!$expectedReturn) {
if ($configExists) {
$this->config->expects($this->once())
->method('getAppValue')
->with('app-name', $configName)
->willReturn($value);
}
}
$this->consoleInput->expects($this->exactly(2))
->method('getArgument')
->willReturnMap([
['app', 'app-name'],
['name', $configName],
]);
$this->consoleInput->expects($this->any())
->method('getOption')
->willReturnMap([
['default-value', $defaultValue],
['output', $outputFormat],
]);
$this->consoleInput->expects($this->any())
->method('hasParameterOption')
->willReturnMap([
['--output', true],
['--default-value', $hasDefault],
]);
if ($expectedMessage !== null) {
global $output;
$output = '';
$this->consoleOutput->expects($this->any())
->method('writeln')
->willReturnCallback(function($value) {
global $output;
$output .= $value . "\n";
return $output;
});
}
$this->assertSame($expectedReturn, \Test_Helper::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]));
if ($expectedMessage !== null) {
global $output;
// Remove the trailing newline
$this->assertSame($expectedMessage, substr($output, 0, -1));
}
}
}

View File

@ -0,0 +1,118 @@
<?php
/**
* @author Joas Schilling <nickvergessen@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace Tests\Core\Command\Config\App;
use OC\Core\Command\Config\App\SetConfig;
use Test\TestCase;
class SetConfigTest extends TestCase {
/** @var \PHPUnit_Framework_MockObject_MockObject */
protected $config;
/** @var \PHPUnit_Framework_MockObject_MockObject */
protected $consoleInput;
/** @var \PHPUnit_Framework_MockObject_MockObject */
protected $consoleOutput;
/** @var \Symfony\Component\Console\Command\Command */
protected $command;
protected function setUp() {
parent::setUp();
$config = $this->config = $this->getMockBuilder('OCP\IConfig')
->disableOriginalConstructor()
->getMock();
$this->consoleInput = $this->getMock('Symfony\Component\Console\Input\InputInterface');
$this->consoleOutput = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
/** @var \OCP\IConfig $config */
$this->command = new SetConfig($config);
}
public function setData() {
return [
[
'name',
'newvalue',
true,
true,
true,
'info',
],
[
'name',
'newvalue',
false,
true,
false,
'comment',
],
];
}
/**
* @dataProvider setData
*
* @param string $configName
* @param mixed $newValue
* @param bool $configExists
* @param bool $updateOnly
* @param bool $updated
* @param string $expectedMessage
*/
public function testSet($configName, $newValue, $configExists, $updateOnly, $updated, $expectedMessage) {
$this->config->expects($this->once())
->method('getAppKeys')
->with('app-name')
->willReturn($configExists ? [$configName] : []);
if ($updated) {
$this->config->expects($this->once())
->method('setAppValue')
->with('app-name', $configName, $newValue);
}
$this->consoleInput->expects($this->exactly(2))
->method('getArgument')
->willReturnMap([
['app', 'app-name'],
['name', $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));
\Test_Helper::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
}
}