diff --git a/core/lib/Drupal/Core/Config/ConfigImporter.php b/core/lib/Drupal/Core/Config/ConfigImporter.php index 6f1a173..bcbbf83 100644 --- a/core/lib/Drupal/Core/Config/ConfigImporter.php +++ b/core/lib/Drupal/Core/Config/ConfigImporter.php @@ -12,6 +12,7 @@ use Drupal\Core\DependencyInjection\DependencySerialization; use Drupal\Core\Entity\EntityStorageException; use Drupal\Core\Lock\LockBackendInterface; +use Guzzle\Common\Exception\ExceptionCollection; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** @@ -90,6 +91,13 @@ class ConfigImporter extends DependencySerialization { protected $validated; /** + * The config importer log. + * + * @var \Drupal\Core\Config\ConfigImporterLogInterface + */ + protected $log; + + /** * Constructs a configuration import object. * * @param \Drupal\Core\Config\StorageComparerInterface $storage_comparer @@ -113,6 +121,17 @@ public function __construct(StorageComparerInterface $storage_comparer, EventDis $this->processed = $this->storageComparer->getEmptyChangelist(); } + public function setLog(ConfigImporterLogInterface $log = NULL) { + $this->log = $log; + } + + protected function log($message, $severity) { + if ($this->log) { + $this->log->log($message, $severity); + return TRUE; + } + } + /** * Gets the configuration storage comparer. * @@ -256,8 +275,15 @@ public function validate() { * The name of the configuration to process. */ protected function process($op, $name) { - if (!$this->importInvokeOwner($op, $name)) { - $this->importConfig($op, $name); + try { + if (!$this->importInvokeOwner($op, $name)) { + $this->importConfig($op, $name); + } + } + catch (\Exception $e) { + if (!$this->log($e->getMessage(), 'error')) { + throw $e; + } } } @@ -299,6 +325,7 @@ protected function checkOp($op, $name) { $entity_type = $this->configManager->getEntityManager()->getDefinition($entity_type_id); $entity = $entity_storage->load($entity_storage->getIDFromConfigName($name, $entity_type->getConfigPrefix())); $entity->delete(); + $this->log(String::format('Deleted and replaced configuration entity "@name"', array('@name' => $name)), 'warning'); } else { $this->storageComparer->getTargetStorage()->delete($name); @@ -309,7 +336,8 @@ protected function checkOp($op, $name) { case 'update': if (!$target_exists) { - throw new ConfigImporterException(String::format('Update target "@name" is missing.', array('@name' => $name))); + $this->log(String::format('Update target "@name" is missing.', array('@name' => $name)), 'error'); + return FALSE; } break; } diff --git a/core/lib/Drupal/Core/Config/ConfigImporterLogInterface.php b/core/lib/Drupal/Core/Config/ConfigImporterLogInterface.php new file mode 100644 index 0000000..2d77ca1 --- /dev/null +++ b/core/lib/Drupal/Core/Config/ConfigImporterLogInterface.php @@ -0,0 +1,19 @@ +logs[] = array( + 'message' => $message, + 'severity' => $severity, + ); + } + + public function getLogMessages() { + return $this->logs; + } + +} diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php index 2d773dc..a5d634c 100644 --- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php @@ -9,6 +9,7 @@ use Drupal\Component\Utility\String; use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\Config\ConfigImporterException; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityMalformedException; use Drupal\Core\Entity\EntityStorageBase; @@ -425,6 +426,9 @@ public function importCreate($name, Config $new_config, Config $old_config) { public function importUpdate($name, Config $new_config, Config $old_config) { $id = static::getIDFromConfigName($name, $this->entityType->getConfigPrefix()); $entity = $this->load($id); + if (!$entity) { + throw new ConfigImporterException(String::format('Attempt to update non-existing entity "@id".', array('@id' => $id))); + } $entity->setSyncing(TRUE); $entity->original = clone $entity; diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigImporterTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigImporterTest.php index e679f5b..f2d15a2 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigImporterTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigImporterTest.php @@ -9,7 +9,9 @@ use Drupal\Core\Config\ConfigImporter; use Drupal\Core\Config\ConfigImporterException; +use Drupal\Core\Config\ConfigImporterLogState; use Drupal\Core\Config\StorageComparer; +use Drupal\Component\Utility\String; use Drupal\simpletest\DrupalUnitTestBase; /** @@ -25,6 +27,13 @@ class ConfigImporterTest extends DrupalUnitTestBase { protected $configImporter; /** + * Config importer log. + * + * @var \Drupal\Core\Config\ConfigImporterLogInterface + */ + protected $configLog; + + /** * Modules to enable. * * @var array @@ -62,6 +71,8 @@ function setUp() { $this->container->get('lock'), $this->container->get('config.typed') ); + $this->configLog = new ConfigImporterLogState(); + $this->configImporter->setLog($this->configLog); $this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.staging')); } @@ -147,6 +158,8 @@ function testDeleted() { // Verify that there is nothing more to import. $this->assertFalse($this->configImporter->hasUnprocessedChanges()); + $logs = $this->configLog->getLogMessages(); + $this->assertEqual(count($logs), 0); } /** @@ -194,6 +207,8 @@ function testNew() { // Verify that there is nothing more to import. $this->assertFalse($this->configImporter->hasUnprocessedChanges()); + $logs = $this->configLog->getLogMessages(); + $this->assertEqual(count($logs), 0); } /** @@ -236,6 +251,11 @@ function testSecondaryWritePrimaryFirst() { $this->assertEqual($secondary->id(), 'secondary'); $this->assertEqual($secondary->uuid(), $values_secondary['uuid']); $this->assertEqual($secondary->label(), $values_secondary['label']); + + $logs = $this->configLog->getLogMessages(); + $this->assertEqual(count($logs), 1); + $this->assertEqual($logs[0]['message'], String::format('Deleted and replaced configuration entity "@name"', array('@name' => $name_secondary))); + $this->assertEqual($logs[0]['severity'], 'warning'); } /** @@ -278,6 +298,12 @@ function testSecondaryWriteSecondaryFirst() { $this->assertEqual($secondary->id(), 'secondary'); $this->assertEqual($secondary->uuid(), $values_secondary['uuid']); $this->assertEqual($secondary->label(), $values_secondary['label']); + + $logs = $this->configLog->getLogMessages(); + $this->assertEqual(count($logs), 1); + $this->assertEqual($logs[0]['message'], String::format('config_test entity with ID @name already exists.', array('@name' => 'secondary'))); + $this->assertEqual($logs[0]['severity'], 'error'); + } /** @@ -321,10 +347,13 @@ function testSecondaryUpdateDeletedDeleterFirst() { $this->assertEqual($deleter->id(), 'deleter'); $this->assertEqual($deleter->uuid(), $values_deleter['uuid']); $this->assertEqual($deleter->label(), $values_deleter['label']); - $deletee = $entity_storage->load('deletee'); - $this->assertEqual($deletee->id(), 'deletee'); - $this->assertEqual($deletee->uuid(), $values_deletee['uuid']); - $this->assertEqual($deletee->label(), $values_deletee['label']); + // @todo The deletee entity does not exist as the update failed. Should the + // importer attempt a create instead? + + $logs = $this->configLog->getLogMessages(); + $this->assertEqual(count($logs), 1); + $this->assertEqual($logs[0]['message'], String::format('Update target "@name" is missing.', array('@name' => $name_deletee))); + $this->assertEqual($logs[0]['severity'], 'error'); } /** @@ -368,10 +397,11 @@ function testSecondaryUpdateDeletedDeleteeFirst() { $this->assertEqual($deleter->id(), 'deleter'); $this->assertEqual($deleter->uuid(), $values_deleter['uuid']); $this->assertEqual($deleter->label(), $values_deleter['label']); - $deletee = $entity_storage->load('deletee'); - $this->assertEqual($deletee->id(), 'deletee'); - $this->assertEqual($deletee->uuid(), $values_deletee['uuid']); - $this->assertEqual($deletee->label(), $values_deletee['label']); + // @todo The deletee entity does not exist as the update worked but the + // entity was deleted after that. There is also no log message as this + // happened outside of the config importer. + $logs = $this->configLog->getLogMessages(); + $this->assertEqual(count($logs), 0); } /** @@ -427,6 +457,8 @@ function testUpdated() { // Verify that there is nothing more to import. $this->assertFalse($this->configImporter->hasUnprocessedChanges()); + $logs = $this->configLog->getLogMessages(); + $this->assertEqual(count($logs), 0); } }