Skip to content

Commit 316bd24

Browse files
MAGETWO-64226: Add paths validation
1 parent c98828b commit 316bd24

File tree

6 files changed

+350
-8
lines changed

6 files changed

+350
-8
lines changed

app/code/Magento/Config/Console/Command/ConfigSetCommand.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
namespace Magento\Config\Console\Command;
77

88
use Magento\Config\Console\Command\ConfigSet\ConfigSetProcessorFactory;
9+
use Magento\Config\Model\Config\PathValidatorFactory;
910
use Magento\Framework\App\Area;
1011
use Magento\Framework\App\Config\ScopeConfigInterface;
1112
use Magento\Framework\App\Scope\ValidatorInterface;
@@ -47,19 +48,27 @@ class ConfigSetCommand extends Command
4748
*/
4849
private $scope;
4950

51+
/**
52+
* @var PathValidatorFactory
53+
*/
54+
private $pathValidatorFactory;
55+
5056
/**
5157
* @param ConfigSetProcessorFactory $configSetProcessorFactory
5258
* @param ValidatorInterface $validator
5359
* @param ScopeInterface $scope
60+
* @param PathValidatorFactory $pathValidatorFactory
5461
*/
5562
public function __construct(
5663
ConfigSetProcessorFactory $configSetProcessorFactory,
5764
ValidatorInterface $validator,
58-
ScopeInterface $scope
65+
ScopeInterface $scope,
66+
PathValidatorFactory $pathValidatorFactory
5967
) {
6068
$this->configSetProcessorFactory = $configSetProcessorFactory;
6169
$this->validator = $validator;
6270
$this->scope = $scope;
71+
$this->pathValidatorFactory = $pathValidatorFactory;
6372

6473
parent::__construct();
6574
}
@@ -118,6 +127,14 @@ protected function execute(InputInterface $input, OutputInterface $output)
118127
// Emulating adminhtml scope to be able to read configs.
119128
$this->scope->setCurrentScope(Area::AREA_ADMINHTML);
120129

130+
/**
131+
* Validates the entered config path.
132+
* Requires emulated area.
133+
*/
134+
$this->pathValidatorFactory->create()->validate(
135+
$input->getArgument(ConfigSetCommand::ARG_PATH)
136+
);
137+
121138
$processor = $input->getOption(static::OPTION_LOCK)
122139
? $this->configSetProcessorFactory->create(ConfigSetProcessorFactory::TYPE_LOCK)
123140
: $this->configSetProcessorFactory->create(ConfigSetProcessorFactory::TYPE_DEFAULT);
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
/**
3+
* Copyright © 2013-2017 Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Config\Model\Config;
7+
8+
use Magento\Framework\Exception\ValidatorException;
9+
10+
/**
11+
* Validates the config path by config structure schema.
12+
*/
13+
class PathValidator
14+
{
15+
/**
16+
* The config structure.
17+
*
18+
* @var Structure
19+
*/
20+
private $structure;
21+
22+
/**
23+
* @param Structure $structure The config structure
24+
*/
25+
public function __construct(Structure $structure)
26+
{
27+
$this->structure = $structure;
28+
}
29+
30+
/**
31+
* Validates the config path by config structure schema.
32+
*
33+
* @param string $path The config path
34+
* @return bool
35+
* @throws ValidatorException If provided path can not be validated
36+
*/
37+
public function validate($path)
38+
{
39+
$allPaths = $this->structure->getFieldPaths();
40+
41+
if (!array_key_exists($path, $allPaths)) {
42+
throw new ValidatorException(__('The "%1" path does not exists', $path));
43+
}
44+
45+
return true;
46+
}
47+
}

app/code/Magento/Config/Model/Config/Structure.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,4 +251,57 @@ protected function _getGroupFieldPathsByAttribute(array $fields, $parentPath, $a
251251
}
252252
return $result;
253253
}
254+
255+
/**
256+
* Collects config field paths recursively from config schema.
257+
* Returns an array map of fields specification.
258+
*
259+
* ```php
260+
* [
261+
* 'test_config/test_config/test_config' => [
262+
* 'test_structure/test_structure/test_structure'
263+
* ]
264+
* ```
265+
*
266+
* @return array An array of config path to config structure path map
267+
*/
268+
public function getFieldPaths()
269+
{
270+
$sections = !empty($this->_data['sections']) ? $this->_data['sections'] : [];
271+
272+
return $this->getFieldsRecursively($sections);
273+
}
274+
275+
/**
276+
* Iteration that collects config field paths recursively from config schema.
277+
*
278+
* @param array $elements
279+
* @return array
280+
*/
281+
private function getFieldsRecursively(array $elements = [])
282+
{
283+
$result = [];
284+
285+
foreach ($elements as $element) {
286+
if (isset($element['children'])) {
287+
$result = array_replace_recursive(
288+
$result,
289+
$this->getFieldsRecursively($element['children'])
290+
);
291+
} else {
292+
if ($element['_elementType'] === 'field' && isset($element['label'])) {
293+
$structurePath = (isset($element['path']) ? $element['path'] . '/' : '') . $element['id'];
294+
$configPath = isset($element['config_path']) ? $element['config_path'] : $structurePath;
295+
296+
if (!isset($result[$configPath])) {
297+
$result[$configPath] = [];
298+
}
299+
300+
$result[$configPath][] = $structurePath;
301+
}
302+
}
303+
}
304+
305+
return $result;
306+
}
254307
}

app/code/Magento/Config/Test/Unit/Console/Command/ConfigSetCommandTest.php

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@
88
use Magento\Config\Console\Command\ConfigSet\ConfigSetProcessorFactory;
99
use Magento\Config\Console\Command\ConfigSet\ConfigSetProcessorInterface;
1010
use Magento\Config\Console\Command\ConfigSetCommand;
11+
use Magento\Config\Model\Config\PathValidator;
12+
use Magento\Config\Model\Config\PathValidatorFactory;
1113
use Magento\Framework\App\Scope\ValidatorInterface;
1214
use Magento\Framework\Config\ScopeInterface;
1315
use Magento\Framework\Console\Cli;
1416
use Magento\Framework\Exception\LocalizedException;
17+
use Magento\Framework\Exception\ValidatorException;
1518
use PHPUnit_Framework_MockObject_MockObject as Mock;
1619
use Symfony\Component\Console\Tester\CommandTester;
1720

@@ -48,6 +51,16 @@ class ConfigSetCommandTest extends \PHPUnit_Framework_TestCase
4851
*/
4952
private $scopeMock;
5053

54+
/**
55+
* @var PathValidatorFactory|Mock
56+
*/
57+
private $pathValidatorFactoryMock;
58+
59+
/**
60+
* @var PathValidator|Mock
61+
*/
62+
private $pathValidator;
63+
5164
/**
5265
* @inheritdoc
5366
*/
@@ -63,11 +76,23 @@ protected function setUp()
6376
->getMockForAbstractClass();
6477
$this->scopeMock = $this->getMockBuilder(ScopeInterface::class)
6578
->getMockForAbstractClass();
79+
$this->pathValidatorFactoryMock = $this->getMockBuilder(PathValidatorFactory::class)
80+
->disableOriginalConstructor()
81+
->setMethods(['create'])
82+
->getMock();
83+
$this->pathValidator = $this->getMockBuilder(PathValidator::class)
84+
->disableOriginalConstructor()
85+
->getMock();
86+
87+
$this->pathValidatorFactoryMock->expects($this->any())
88+
->method('create')
89+
->willReturn($this->pathValidator);
6690

6791
$this->command = new ConfigSetCommand(
6892
$this->configSetProcessorFactoryMock,
6993
$this->validatorMock,
70-
$this->scopeMock
94+
$this->scopeMock,
95+
$this->pathValidatorFactoryMock
7196
);
7297
}
7398

@@ -120,4 +145,28 @@ public function testExecuteNotValidScope()
120145

121146
$this->assertSame(Cli::RETURN_FAILURE, $tester->getStatusCode());
122147
}
148+
149+
public function testExecuteWithException()
150+
{
151+
$this->validatorMock->expects($this->once())
152+
->method('isValid')
153+
->willReturn(true);
154+
$this->pathValidator->expects($this->once())
155+
->method('validate')
156+
->willThrowException(new ValidatorException(__('The "test/test/test" path does not exists')));
157+
$this->configSetProcessorFactoryMock->expects($this->never())
158+
->method('create');
159+
160+
$tester = new CommandTester($this->command);
161+
$tester->execute([
162+
ConfigSetCommand::ARG_PATH => 'test/test/test',
163+
ConfigSetCommand::ARG_VALUE => 'value'
164+
]);
165+
166+
$this->assertContains(
167+
__('The "test/test/test" path does not exists')->render(),
168+
$tester->getDisplay()
169+
);
170+
$this->assertSame(Cli::RETURN_FAILURE, $tester->getStatusCode());
171+
}
123172
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
/**
3+
* Copyright © 2013-2017 Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Config\Test\Unit\Model\Config;
7+
8+
use Magento\Config\Model\Config\PathValidator;
9+
use Magento\Config\Model\Config\Structure;
10+
use PHPUnit_Framework_MockObject_MockObject as Mock;
11+
12+
/**
13+
* Test class for PathValidator.
14+
*
15+
* @see PathValidator
16+
*/
17+
class PathValidatorTest extends \PHPUnit_Framework_TestCase
18+
{
19+
/**
20+
* @var PathValidator
21+
*/
22+
private $model;
23+
24+
/**
25+
* @var Structure|Mock
26+
*/
27+
private $structureMock;
28+
29+
/**
30+
* @inheritdoc
31+
*/
32+
protected function setUp()
33+
{
34+
$this->structureMock = $this->getMockBuilder(Structure::class)
35+
->disableOriginalConstructor()
36+
->getMock();
37+
38+
$this->model = new PathValidator(
39+
$this->structureMock
40+
);
41+
}
42+
43+
public function testValidate()
44+
{
45+
$this->structureMock->expects($this->once())
46+
->method('getFieldPaths')
47+
->willReturn([
48+
'test/test/test' => [
49+
'test/test/test'
50+
]
51+
]);
52+
53+
$this->assertTrue($this->model->validate('test/test/test'));
54+
}
55+
56+
/**
57+
* @expectedException \Magento\Framework\Exception\ValidatorException
58+
* @expectedExceptionMessage The "test/test/test" path does not exists
59+
*/
60+
public function testValidateIwthException()
61+
{
62+
$this->structureMock->expects($this->once())
63+
->method('getFieldPaths')
64+
->willReturn([]);
65+
66+
$this->assertTrue($this->model->validate('test/test/test'));
67+
}
68+
}

0 commit comments

Comments
 (0)