Skip to content

Commit 6eec092

Browse files
author
Bohdan Korablov
committed
Merge remote-tracking branch 'falcons/MAGETWO-69383' into MAGETWO-69983
# Conflicts: # app/code/Magento/Store/Test/Unit/Model/Config/Importer/Processor/CreateTest.php
2 parents de9ec9d + 31474c5 commit 6eec092

File tree

21 files changed

+476
-33
lines changed

21 files changed

+476
-33
lines changed

app/code/Magento/Config/etc/di.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,8 @@
288288
<arguments>
289289
<argument name="importers" xsi:type="array">
290290
<item name="system" xsi:type="array">
291-
<item name="class" xsi:type="string">Magento\Config\Model\Config\Importer</item>
291+
<item name="importer_class" xsi:type="string">Magento\Config\Model\Config\Importer</item>
292+
<item name="sort_order" xsi:type="number">30</item>
292293
</item>
293294
</argument>
294295
</arguments>

app/code/Magento/Deploy/Console/Command/App/ConfigImport/Processor.php

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use Magento\Deploy\Model\DeploymentConfig\ChangeDetector;
1212
use Magento\Deploy\Model\DeploymentConfig\ImporterPool;
1313
use Magento\Deploy\Model\DeploymentConfig\Hash;
14+
use Magento\Framework\Exception\ValidatorException;
1415
use Symfony\Component\Console\Input\InputInterface;
1516
use Symfony\Component\Console\Output\OutputInterface;
1617
use Magento\Deploy\Model\DeploymentConfig\ImporterFactory;
@@ -113,10 +114,10 @@ public function execute(InputInterface $input, OutputInterface $output)
113114
if (!$importers || !$this->changeDetector->hasChanges()) {
114115
$output->writeln('<info>Nothing to import.</info>');
115116
return;
116-
} else {
117-
$output->writeln('<info>Processing configurations data from configuration file...</info>');
118117
}
119118

119+
$output->writeln('<info>Processing configurations data from configuration file...</info>');
120+
120121
/**
121122
* @var string $section
122123
* @var string $importerClassName
@@ -126,9 +127,11 @@ public function execute(InputInterface $input, OutputInterface $output)
126127
continue;
127128
}
128129

130+
$data = (array)$this->deploymentConfig->getConfigData($section);
131+
$this->validateSectionData($section, $data);
132+
129133
/** @var ImporterInterface $importer */
130134
$importer = $this->importerFactory->create($importerClassName);
131-
$data = (array)$this->deploymentConfig->getConfigData($section);
132135
$warnings = $importer->getWarningMessages($data);
133136
$questions = array_merge($warnings, ['Do you want to continue [yes/no]?']);
134137

@@ -149,4 +152,23 @@ public function execute(InputInterface $input, OutputInterface $output)
149152
throw new RuntimeException(__('Import failed: %1', $exception->getMessage()), $exception);
150153
}
151154
}
155+
156+
/**
157+
* Validates that current section has valid import data
158+
*
159+
* @param string $section Name of configuration section
160+
* @param array $data Configuration data for given section
161+
* @return void
162+
* @throws ValidatorException If current section has wrong data
163+
*/
164+
private function validateSectionData($section, array $data)
165+
{
166+
$validator = $this->configImporterPool->getValidator($section);
167+
if (null !== $validator) {
168+
$messages = $validator->validate($data);
169+
if (!empty($messages)) {
170+
throw new ValidatorException(__(implode(PHP_EOL, $messages)));
171+
}
172+
}
173+
}
152174
}

app/code/Magento/Deploy/Model/DeploymentConfig/ChangeDetector.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ public function hasChanges($sectionName = null)
6868
$hashes = $this->configHash->get();
6969

7070
foreach ($configs as $section => $config) {
71+
if (null === $config) {
72+
continue;
73+
}
7174
$savedHash = isset($hashes[$section]) ? $hashes[$section] : null;
7275
$generatedHash = empty($config) && !$savedHash ? null : $this->hashGenerator->generate($config);
7376
if ($generatedHash !== $savedHash) {

app/code/Magento/Deploy/Model/DeploymentConfig/ImporterFactory.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public function create($className, array $data = [])
4444

4545
if (!$importer instanceof ImporterInterface) {
4646
throw new \InvalidArgumentException(
47-
'Type "' . $className . '" is not instance on ' . ImporterInterface::class
47+
'Type "' . $className . '" is not instance of ' . ImporterInterface::class
4848
);
4949
}
5050

app/code/Magento/Deploy/Model/DeploymentConfig/ImporterPool.php

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66
namespace Magento\Deploy\Model\DeploymentConfig;
77

8+
use Magento\Framework\App\DeploymentConfig\ValidatorInterface;
89
use Magento\Framework\Exception\ConfigurationMismatchException;
910
use Magento\Framework\ObjectManagerInterface;
1011

@@ -18,19 +19,20 @@ class ImporterPool
1819
/**
1920
* List of sections and their importers.
2021
*
21-
* Sections are defined with importers in di.xml
22+
* Sections are defined with importers in di.xml. Every section may have data validator
2223
* E.g.
2324
* ```xml
2425
* <type name="Magento\Deploy\Model\DeploymentConfig\ImporterPool">
2526
* <arguments>
2627
* <argument name="importers" xsi:type="array">
2728
* <item name="scopes" xsi:type="array">
28-
* <item name="sortOrder" xsi:type="number">20</item>
29-
* <item name="class" xsi:type="string">Magento\Store\Model\StoreImporter</item>
29+
* <item name="sort_order" xsi:type="number">20</item>
30+
* <item name="importer_class" xsi:type="string">Magento\Store\Model\StoreImporter</item>
31+
* <item name="validator_class" xsi:type="string">Magento\Store\Model\Config\StoreValidator</item>
3032
* </item>
3133
* <item name="themes" xsi:type="array">
32-
* <item name="sortOrder" xsi:type="number">10</item>
33-
* <item name="class" xsi:type="string">Magento\Theme\Model\ThemeImporter</item>
34+
* <item name="sort_order" xsi:type="number">10</item>
35+
* <item name="importer_class" xsi:type="string">Magento\Theme\Model\ThemeImporter</item>
3436
* </item>
3537
* </argument>
3638
* </arguments>
@@ -62,7 +64,7 @@ class ImporterPool
6264
/**
6365
* Sorted list of importers class names.
6466
*
65-
* This list sorted by parameter "sortOrder", that defined in di.xml
67+
* This list sorted by parameter "sort_order", that defined in di.xml
6668
*
6769
* ```php
6870
* [
@@ -83,13 +85,26 @@ class ImporterPool
8385
*/
8486
private $objectManager;
8587

88+
/**
89+
* Factory that creates validator objects by class name.
90+
* Validators should be instances of Magento\Framework\App\DeploymentConfig\ValidatorInterface
91+
*
92+
* @var ValidatorFactory
93+
*/
94+
private $validatorFactory;
95+
8696
/**
8797
* @param ObjectManagerInterface $objectManager the Magento object manager
98+
* @param ValidatorFactory $validatorFactory the validator factory
8899
* @param array $importers the list of sections and their importers
89100
*/
90-
public function __construct(ObjectManagerInterface $objectManager, array $importers = [])
91-
{
101+
public function __construct(
102+
ObjectManagerInterface $objectManager,
103+
ValidatorFactory $validatorFactory,
104+
array $importers = []
105+
) {
92106
$this->objectManager = $objectManager;
107+
$this->validatorFactory = $validatorFactory;
93108
$this->importers = $importers;
94109
}
95110

@@ -113,7 +128,7 @@ public function getSections()
113128
}
114129

115130
/**
116-
* Retrieves list of all sections with their importer class names, sorted by sortOrder.
131+
* Retrieves list of all sections with their importer class names, sorted by sort_order.
117132
*
118133
* E.g.
119134
* ```php
@@ -132,11 +147,11 @@ public function getImporters()
132147
$sortedImporters = [];
133148

134149
foreach ($this->sort($this->importers) as $section => $importer) {
135-
if (empty($importer['class'])) {
136-
throw new ConfigurationMismatchException(__('Parameter "class" must be present.'));
150+
if (empty($importer['importer_class'])) {
151+
throw new ConfigurationMismatchException(__('Parameter "importer_class" must be present.'));
137152
}
138153

139-
$sortedImporters[$section] = $importer['class'];
154+
$sortedImporters[$section] = $importer['importer_class'];
140155
}
141156

142157
$this->sortedImporters = $sortedImporters;
@@ -145,6 +160,21 @@ public function getImporters()
145160
return $this->sortedImporters;
146161
}
147162

163+
/**
164+
* Returns validator object for section if it was declared, otherwise returns null.
165+
*
166+
* @param string $section Section name
167+
* @return ValidatorInterface|null
168+
* @throws \InvalidArgumentException
169+
*/
170+
public function getValidator($section)
171+
{
172+
if (isset($this->importers[$section]) && !empty($this->importers[$section]['validator_class'])) {
173+
return $this->validatorFactory->create($this->importers[$section]['validator_class']);
174+
}
175+
return null;
176+
}
177+
148178
/**
149179
* Sorts importers according to sort order.
150180
*
@@ -154,14 +184,14 @@ public function getImporters()
154184
private function sort(array $data)
155185
{
156186
uasort($data, function (array $a, array $b) {
157-
$a['sortOrder'] = $this->getSortOrder($a);
158-
$b['sortOrder'] = $this->getSortOrder($b);
187+
$a['sort_order'] = $this->getSortOrder($a);
188+
$b['sort_order'] = $this->getSortOrder($b);
159189

160-
if ($a['sortOrder'] == $b['sortOrder']) {
190+
if ($a['sort_order'] == $b['sort_order']) {
161191
return 0;
162192
}
163193

164-
return ($a['sortOrder'] < $b['sortOrder']) ? -1 : 1;
194+
return ($a['sort_order'] < $b['sort_order']) ? -1 : 1;
165195
});
166196

167197
return $data;
@@ -175,6 +205,6 @@ private function sort(array $data)
175205
*/
176206
private function getSortOrder(array $variable)
177207
{
178-
return !empty($variable['sortOrder']) ? $variable['sortOrder'] : 0;
208+
return !empty($variable['sort_order']) ? $variable['sort_order'] : 0;
179209
}
180210
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Deploy\Model\DeploymentConfig;
7+
8+
use Magento\Framework\App\DeploymentConfig\ValidatorInterface;
9+
use Magento\Framework\ObjectManagerInterface;
10+
11+
/**
12+
* Factory for validators.
13+
*
14+
* Creates object instance that implements Magento\Framework\App\DeploymentConfig\ValidatorInterface interface.
15+
*/
16+
class ValidatorFactory
17+
{
18+
/**
19+
* Magento object manager.
20+
*
21+
* @var ObjectManagerInterface
22+
*/
23+
private $objectManager;
24+
25+
/**
26+
* @param ObjectManagerInterface $objectManager the magento object manager
27+
*/
28+
public function __construct(ObjectManagerInterface $objectManager)
29+
{
30+
$this->objectManager = $objectManager;
31+
}
32+
33+
/**
34+
* Creates object instance by class name.
35+
*
36+
* @param string $className the name of class for creation of its object instance
37+
* @param array $data the array with some additional configuration data for creation of object instance
38+
* @return ValidatorInterface the created object instance
39+
* @throws \InvalidArgumentException is thrown when object instance does not implement ValidatorInterface
40+
*/
41+
public function create($className, array $data = [])
42+
{
43+
$importer = $this->objectManager->create($className, $data);
44+
45+
if (!$importer instanceof ValidatorInterface) {
46+
throw new \InvalidArgumentException(
47+
'Type "' . $className . '" is not instance of ' . ValidatorInterface::class
48+
);
49+
}
50+
51+
return $importer;
52+
}
53+
}

app/code/Magento/Deploy/Test/Unit/Console/Command/App/ConfigImport/ProcessorTest.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\Component\Console\Input\InputInterface;
1717
use Magento\Deploy\Model\DeploymentConfig\ImporterFactory;
1818
use Magento\Framework\Console\QuestionPerformer\YesNo;
19+
use Magento\Framework\App\DeploymentConfig\ValidatorInterface;
1920

2021
/**
2122
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -234,6 +235,48 @@ public function testImportWithException()
234235
$this->processor->execute($this->inputMock, $this->outputMock);
235236
}
236237

238+
/**
239+
* @expectedException \Magento\Framework\Exception\RuntimeException
240+
* @expectedExceptionMessage Import failed: error message
241+
*/
242+
public function testImportWithValidation()
243+
{
244+
$configData = ['config data'];
245+
$importerClassName = 'someImporterClassName';
246+
$importers = ['someSection' => $importerClassName];
247+
$errorMessages = ['error message'];
248+
249+
$validatorMock = $this->getMockBuilder(ValidatorInterface::class)
250+
->getMockForAbstractClass();
251+
$validatorMock->expects($this->once())
252+
->method('validate')
253+
->with($configData)
254+
->willReturn($errorMessages);
255+
$this->configImporterPoolMock->expects($this->once())
256+
->method('getImporters')
257+
->willReturn($importers);
258+
$this->changeDetectorMock->expects($this->exactly(2))
259+
->method('hasChanges')
260+
->withConsecutive(
261+
[],
262+
['someSection']
263+
)
264+
->willReturnOnConsecutiveCalls(true, true);
265+
$this->deploymentConfigMock->expects($this->once())
266+
->method('getConfigData')
267+
->with('someSection')
268+
->willReturn($configData);
269+
$this->configImporterPoolMock->expects($this->once())
270+
->method('getValidator')
271+
->willReturn($validatorMock);
272+
$this->importerFactoryMock->expects($this->never())
273+
->method('create');
274+
$this->loggerMock->expects($this->once())
275+
->method('error');
276+
277+
$this->processor->execute($this->inputMock, $this->outputMock);
278+
}
279+
237280
/**
238281
* @param array $importers
239282
* @param bool $isValid

app/code/Magento/Deploy/Test/Unit/Model/DeploymentConfig/ImporterFactoryTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public function testCreate()
4747
/**
4848
* @expectedException \InvalidArgumentException
4949
* @codingStandardsIgnoreStart
50-
* @expectedExceptionMessage Type "some/class/name" is not instance on Magento\Framework\App\DeploymentConfig\ImporterInterface
50+
* @expectedExceptionMessage Type "some/class/name" is not instance of Magento\Framework\App\DeploymentConfig\ImporterInterface
5151
* @codingStandardsIgnoreEnd
5252
*/
5353
public function testCreateWithInvalidArgumentException()

0 commit comments

Comments
 (0)