diff options
author | Joas Schilling <nickvergessen@owncloud.com> | 2015-05-15 13:01:12 +0200 |
---|---|---|
committer | Joas Schilling <nickvergessen@owncloud.com> | 2015-07-07 11:18:25 +0200 |
commit | 86952f88cbcd1a24930ad69c1c3d52f7f982b035 (patch) | |
tree | fb002f297e13f638b70f33e1fc85d0f94a05fa6c | |
parent | bc084c40a6745a947e3650196df17f55344ca72f (diff) | |
download | nextcloud-server-86952f88cbcd1a24930ad69c1c3d52f7f982b035.tar.gz nextcloud-server-86952f88cbcd1a24930ad69c1c3d52f7f982b035.zip |
Add a command to import an json array into the config
-rw-r--r-- | core/command/config/import.php | 164 | ||||
-rw-r--r-- | core/register_command.php | 1 | ||||
-rw-r--r-- | tests/core/command/config/importtest.php | 185 | ||||
-rw-r--r-- | tests/core/command/config/listconfigstest.php | 1 |
4 files changed, 350 insertions, 1 deletions
diff --git a/core/command/config/import.php b/core/command/config/import.php new file mode 100644 index 00000000000..08c37570174 --- /dev/null +++ b/core/command/config/import.php @@ -0,0 +1,164 @@ +<?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; + +use OCP\IConfig; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class Import extends Command { + protected $validRootKeys = ['system', 'apps']; + + /** @var IConfig */ + protected $config; + + /** + * @param IConfig $config + */ + public function __construct(IConfig $config) { + parent::__construct(); + $this->config = $config; + } + + protected function configure() { + $this + ->setName('config:import') + ->setDescription('Import a list of configs') + ->addArgument( + 'file', + InputArgument::REQUIRED, + 'File with the json array to import' + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) { + $importFile = $input->getArgument('file'); + try { + $configs = $this->getArrayFromFile($importFile); + } catch (\UnexpectedValueException $e) { + $output->writeln('<error>' . $e->getMessage(). '</error>'); + } + + if (!empty($configs['system'])) { + $this->config->setSystemValues($configs['system']); + } + + if (!empty($configs['apps'])) { + foreach ($configs['apps'] as $app => $appConfigs) { + foreach ($appConfigs as $key => $value) { + if ($value === null) { + $this->config->deleteAppValue($app, $key); + } else { + $this->config->setAppValue($app, $key, $value); + } + } + } + } + + $output->writeln('<info>Config successfully imported from: ' . $importFile . '</info>'); + } + + /** + * @param string $importFile + * @return array + * @throws \UnexpectedValueException when the array is invalid + */ + protected function getArrayFromFile($importFile) { + $helo = file_get_contents($importFile); + $decodedContent = json_decode($helo, true); + if (!is_array($decodedContent) || empty($decodedContent)) { + throw new \UnexpectedValueException('The file must contain a valid json array'); + } + + $this->validateArray($decodedContent); + + return $decodedContent; + } + + /** + * Validates that the array only contains `system` and `apps` + * + * @param array $array + */ + protected function validateArray($array) { + $arrayKeys = array_keys($array); + $additionalKeys = array_diff($arrayKeys, $this->validRootKeys); + $commonKeys = array_intersect($arrayKeys, $this->validRootKeys); + if (!empty($additionalKeys)) { + throw new \UnexpectedValueException('Found invalid entries in root: ' . implode(', ', $additionalKeys)); + } + if (empty($commonKeys)) { + throw new \UnexpectedValueException('At least one key of the following is expected: ' . implode(', ', $this->validRootKeys)); + } + + if (isset($array['system'])) { + if (is_array($array['system'])) { + foreach ($array['system'] as $name => $value) { + $this->checkTypeRecursively($value, $name); + } + } else { + throw new \UnexpectedValueException('The system config array is not an array'); + } + } + + if (isset($array['apps'])) { + if (is_array($array['apps'])) { + $this->validateAppsArray($array['apps']); + } else { + throw new \UnexpectedValueException('The apps config array is not an array'); + } + } + } + + /** + * @param mixed $configValue + * @param string $configName + */ + protected function checkTypeRecursively($configValue, $configName) { + if (!is_array($configValue) && !is_bool($configValue) && !is_int($configValue) && !is_string($configValue) && !is_null($configValue)) { + throw new \UnexpectedValueException('Invalid system config value for "' . $configName . '". Only arrays, bools, integers, strings and null (delete) are allowed.'); + } + if (is_array($configValue)) { + foreach ($configValue as $key => $value) { + $this->checkTypeRecursively($value, $configName); + } + } + } + + /** + * Validates that app configs are only integers and strings + * + * @param array $array + */ + protected function validateAppsArray($array) { + foreach ($array as $app => $configs) { + foreach ($configs as $name => $value) { + if (!is_int($value) && !is_string($value) && !is_null($value)) { + throw new \UnexpectedValueException('Invalid app config value for "' . $app . '":"' . $name . '". Only integers, strings and null (delete) are allowed.'); + } + } + } + } +} diff --git a/core/register_command.php b/core/register_command.php index 9ef8ca5e58b..2f057ed56a1 100644 --- a/core/register_command.php +++ b/core/register_command.php @@ -40,6 +40,7 @@ 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\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())); $application->add(new OC\Core\Command\Config\System\GetConfig(\OC::$server->getSystemConfig())); diff --git a/tests/core/command/config/importtest.php b/tests/core/command/config/importtest.php new file mode 100644 index 00000000000..d465c8ca479 --- /dev/null +++ b/tests/core/command/config/importtest.php @@ -0,0 +1,185 @@ +<?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; + + +use OC\Core\Command\Config\Import; +use Test\TestCase; + +class ImportTest 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 Import($config); + } + + public function validateAppsArrayData() { + return [ + [0], + [1], + [null], + ['new \Exception()'], + [json_encode([])], + ]; + } + + /** + * @dataProvider validateAppsArrayData + * + * @param mixed $configValue + */ + public function testValidateAppsArray($configValue) { + \Test_Helper::invokePrivate($this->command, 'validateAppsArray', [['app' => ['name' => $configValue]]]); + $this->assertTrue(true, 'Asserting that no exception is thrown'); + } + + public function validateAppsArrayThrowsData() { + return [ + [false], + [true], + [[]], + [new \Exception()], + ]; + } + + /** + * @dataProvider validateAppsArrayThrowsData + * + * @param mixed $configValue + */ + public function testValidateAppsArrayThrows($configValue) { + try { + \Test_Helper::invokePrivate($this->command, 'validateAppsArray', [['app' => ['name' => $configValue]]]); + $this->fail('Did not throw expected UnexpectedValueException'); + } catch (\UnexpectedValueException $e) { + $this->assertStringStartsWith('Invalid app config value for "app":"name".', $e->getMessage()); + } + } + + public function checkTypeRecursivelyData() { + return [ + [0], + [1], + [null], + ['new \Exception()'], + [json_encode([])], + [false], + [true], + [[]], + [['string']], + [['test' => 'string']], + [['test' => ['sub' => 'string']]], + ]; + } + + /** + * @dataProvider checkTypeRecursivelyData + * + * @param mixed $configValue + */ + public function testCheckTypeRecursively($configValue) { + \Test_Helper::invokePrivate($this->command, 'checkTypeRecursively', [$configValue, 'name']); + $this->assertTrue(true, 'Asserting that no exception is thrown'); + } + + public function checkTypeRecursivelyThrowsData() { + return [ + [new \Exception()], + [[new \Exception()]], + [['test' => new \Exception()]], + [['test' => ['sub' => new \Exception()]]], + ]; + } + + /** + * @dataProvider checkTypeRecursivelyThrowsData + * + * @param mixed $configValue + */ + public function testCheckTypeRecursivelyThrows($configValue) { + try { + \Test_Helper::invokePrivate($this->command, 'checkTypeRecursively', [$configValue, 'name']); + $this->fail('Did not throw expected UnexpectedValueException'); + } catch (\UnexpectedValueException $e) { + $this->assertStringStartsWith('Invalid system config value for "name"', $e->getMessage()); + } + } + + public function validateArrayData() { + return [ + [['system' => []]], + [['apps' => []]], + [['system' => [], 'apps' => []]], + ]; + } + + /** + * @dataProvider validateArrayData + * + * @param array $configArray + */ + public function testValidateArray($configArray) { + \Test_Helper::invokePrivate($this->command, 'validateArray', [$configArray]); + $this->assertTrue(true, 'Asserting that no exception is thrown'); + } + + public function validateArrayThrowsData() { + return [ + [[], 'At least one key of the following is expected:'], + [[0 => []], 'Found invalid entries in root'], + [['string' => []], 'Found invalid entries in root'], + ]; + } + + /** + * @dataProvider validateArrayThrowsData + * + * @param mixed $configArray + * @param string $expectedException + */ + public function testValidateArrayThrows($configArray, $expectedException) { + try { + \Test_Helper::invokePrivate($this->command, 'validateArray', [$configArray]); + $this->fail('Did not throw expected UnexpectedValueException'); + } catch (\UnexpectedValueException $e) { + $this->assertStringStartsWith($expectedException, $e->getMessage()); + } + } +} diff --git a/tests/core/command/config/listconfigstest.php b/tests/core/command/config/listconfigstest.php index 4ea4a92cf81..f6d78232167 100644 --- a/tests/core/command/config/listconfigstest.php +++ b/tests/core/command/config/listconfigstest.php @@ -56,7 +56,6 @@ class ListConfigsTest extends TestCase { $this->command = new ListConfigs($systemConfig, $appConfig); } - public function listData() { return [ [ |