Commit ffde907c authored by alexpott's avatar alexpott

Issue #2321609 by benjy, dawehner: Fixed Provide a helpful message in case...

Issue #2321609 by benjy, dawehner: Fixed Provide a helpful message in case requirements are not met.
parent 801d850a
......@@ -7,7 +7,9 @@
namespace Drupal\migrate\Entity;
use Drupal\Component\Utility\String;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\migrate\Exception\RequirementsException;
use Drupal\migrate\MigrateException;
use Drupal\migrate\Plugin\MigrateIdMapInterface;
use Drupal\migrate\Plugin\RequirementsInterface;
......@@ -195,6 +197,13 @@ class Migration extends ConfigEntityBase implements MigrationInterface, Requirem
*/
public $migration_dependencies = array();
/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
protected $entityManager;
/**
* {@inheritdoc}
*/
......@@ -315,28 +324,39 @@ public function saveHighWater($high_water) {
public function checkRequirements() {
// Check whether the current migration source and destination plugin
// requirements are met or not.
try {
if ($this->getSourcePlugin() instanceof RequirementsInterface && !$this->getSourcePlugin()->checkRequirements()) {
return FALSE;
}
if ($this->getDestinationPlugin() instanceof RequirementsInterface && !$this->getDestinationPlugin()->checkRequirements()) {
return FALSE;
}
if ($this->getSourcePlugin() instanceof RequirementsInterface) {
$this->getSourcePlugin()->checkRequirements();
}
if ($this->getDestinationPlugin() instanceof RequirementsInterface) {
$this->getDestinationPlugin()->checkRequirements();
}
/** @var \Drupal\migrate\Entity\MigrationInterface[] $required_migrations */
$required_migrations = \Drupal::entityManager()->getStorage('migration')->loadMultiple($this->requirements);
// Check if the dependencies are in good shape.
foreach ($required_migrations as $required_migration) {
if (!$required_migration->isComplete()) {
return FALSE;
}
/** @var \Drupal\migrate\Entity\MigrationInterface[] $required_migrations */
$required_migrations = $this->getEntityManager()->getStorage('migration')->loadMultiple($this->requirements);
$missing_migrations = array_diff($this->requirements, array_keys($required_migrations));
// Check if the dependencies are in good shape.
foreach ($required_migrations as $migration_id => $required_migration) {
if (!$required_migration->isComplete()) {
$missing_migrations[] = $migration_id;
}
}
catch (\Exception $e) {
return FALSE;
if ($missing_migrations) {
throw new RequirementsException(String::format('Missing migrations @requirements.', ['@requirements' => implode(', ', $missing_migrations)]), ['requirements' => $missing_migrations]);
}
}
return TRUE;
/**
* Get the entity manager.
*
* @return \Drupal\Core\Entity\EntityManagerInterface
* The entity manager.
*/
protected function getEntityManager() {
if (!isset($this->entityManager)) {
$this->entityManager = \Drupal::entityManager();
}
return $this->entityManager;
}
/**
......
<?php
/**
* @file
* Contains \Drupal\migrate\Exception\RequirementsException.
*/
namespace Drupal\migrate\Exception;
use Exception;
/**
* Defines an
*
* @see \Drupal\migrate\Plugin\RequirementsInterface
*/
class RequirementsException extends \RuntimeException {
/**
* The missing requirements.
*
* @var array
*/
protected $requirements;
/**
* Constructs a new RequirementsException instance.
*
* @param string $message
* (optional) The Exception message to throw.
* @param array $requirements
* (optional) The missing requirements.
* @param int $code
* (optional) The Exception code.
* @param \Exception $previous
* (optional) The previous exception used for the exception chaining.
*/
public function __construct($message = "", array $requirements = [], $code = 0, Exception $previous = NULL) {
parent::__construct($message, $code, $previous);
$this->requirements = $requirements;
}
/**
* Get an array of requirements.
*
* @return array
* The requirements.
*/
public function getRequirements() {
return $this->requirements;
}
/**
* Get the requirements as a string.
*
* @return string
* A formatted requirements string.
*/
public function getRequirementsString() {
$output = '';
foreach ($this->requirements as $requirement_type => $requirements) {
foreach ($requirements as $value) {
$output .= "$requirement_type: $value. ";
}
}
return trim($output);
}
}
......@@ -10,6 +10,7 @@
use Drupal\Core\Utility\Error;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\migrate\Entity\MigrationInterface;
use Drupal\migrate\Exception\RequirementsException;
use Drupal\migrate\Plugin\MigrateIdMapInterface;
/**
......@@ -233,11 +234,19 @@ public function getSource() {
*/
public function import() {
// Knock off migration if the requirements haven't been met.
if (!$this->migration->checkRequirements()) {
try {
$this->migration->checkRequirements();
}
catch (RequirementsException $e) {
$this->message->display(
$this->t('Migration @id did not meet the requirements', array('@id' => $this->migration->id())), 'error');
$this->t('Migration @id did not meet the requirements. @message @requirements', array(
'@id' => $this->migration->id(),
'@message' => $e->getMessage(),
'@requirements' => $e->getRequirementsString(),
)), 'error');
return MigrationInterface::RESULT_FAILED;
}
$return = MigrationInterface::RESULT_COMPLETED;
$source = $this->getSource();
$id_map = $this->migration->getIdMap();
......
......@@ -15,8 +15,8 @@ interface RequirementsInterface {
/**
* Checks if requirements for this plugin are OK.
*
* @return bool
* TRUE if it is possible to use the plugin, FALSE if not.
* @throws \Drupal\migrate\Exception\RequirementsException
* Thrown when requirements are not met.
*/
public function checkRequirements();
......
......@@ -10,6 +10,7 @@
use Drupal\Core\Plugin\PluginBase;
use Drupal\migrate\Entity\MigrationInterface;
use Drupal\migrate\Exception\RequirementsException;
use Drupal\migrate\Plugin\MigrateDestinationInterface;
use Drupal\migrate\Plugin\RequirementsInterface;
......@@ -53,7 +54,9 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition
* {@inheritdoc}
*/
public function checkRequirements() {
return $this->pluginDefinition['requirements_met'];
if (empty($this->pluginDefinition['requirements_met'])) {
throw new RequirementsException();
}
}
/**
......
<?php
/**
* @file
* Contains \Drupal\Tests\migrate\Unit\MigrationTest.
*/
namespace Drupal\Tests\migrate\Unit;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\migrate\Entity\Migration;
use Drupal\migrate\Exception\RequirementsException;
use Drupal\migrate\Plugin\MigrateDestinationInterface;
use Drupal\migrate\Plugin\MigrateSourceInterface;
use Drupal\migrate\Plugin\RequirementsInterface;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\migrate\Entity\Migration
* @group Migration
*/
class MigrationTest extends UnitTestCase {
/**
* Tests checking requirements for source plugins.
*
* @covers ::checkRequirements
*
* @expectedException \Drupal\migrate\Exception\RequirementsException
* @expectedExceptionMessage Missing source requirement
*/
public function testRequirementsForSourcePlugin() {
$migration = new TestMigration();
$source_plugin = $this->getMock('Drupal\Tests\migrate\Unit\RequirementsAwareSourceInterface');
$source_plugin->expects($this->once())
->method('checkRequirements')
->willThrowException(new RequirementsException('Missing source requirement', ['key' => 'value']));
$destination_plugin = $this->getMock('Drupal\Tests\migrate\Unit\RequirementsAwareDestinationInterface');
$migration->setSourcePlugin($source_plugin);
$migration->setDestinationPlugin($destination_plugin);
$migration->checkRequirements();
}
/**
* Tests checking requirements for destination plugins.
*
* @covers ::checkRequirements
*
* @expectedException \Drupal\migrate\Exception\RequirementsException
* @expectedExceptionMessage Missing destination requirement
*/
public function testRequirementsForDestinationPlugin() {
$migration = new TestMigration();
$source_plugin = $this->getMock('Drupal\migrate\Plugin\MigrateSourceInterface');
$destination_plugin = $this->getMock('Drupal\Tests\migrate\Unit\RequirementsAwareDestinationInterface');
$destination_plugin->expects($this->once())
->method('checkRequirements')
->willThrowException(new RequirementsException('Missing destination requirement', ['key' => 'value']));
$migration->setSourcePlugin($source_plugin);
$migration->setDestinationPlugin($destination_plugin);
$migration->checkRequirements();
}
/**
* Tests checking requirements for destination plugins.
*
* @covers ::checkRequirements
*
* @expectedException \Drupal\migrate\Exception\RequirementsException
* @expectedExceptionMessage Missing migrations test_a, test_c
*/
public function testRequirementsForMigrations() {
$migration = new TestMigration();
// Setup source and destination plugins without any requirements.
$source_plugin = $this->getMock('Drupal\migrate\Plugin\MigrateSourceInterface');
$destination_plugin = $this->getMock('Drupal\migrate\Plugin\MigrateDestinationInterface');
$migration->setSourcePlugin($source_plugin);
$migration->setDestinationPlugin($destination_plugin);
$entity_manager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
$migration->setEntityManager($entity_manager);
// We setup the requirements that test_a doesn't exist and test_c is not
// completed yet.
$migration->setRequirements(['test_a', 'test_b', 'test_c', 'test_d']);
$migration_b = $this->getMock('Drupal\migrate\Entity\MigrationInterface');
$migration_c = $this->getMock('Drupal\migrate\Entity\MigrationInterface');
$migration_d = $this->getMock('Drupal\migrate\Entity\MigrationInterface');
$migration_b->expects($this->once())
->method('isComplete')
->willReturn(TRUE);
$migration_c->expects($this->once())
->method('isComplete')
->willReturn(FALSE);
$migration_d->expects($this->once())
->method('isComplete')
->willReturn(TRUE);
$migration_storage = $this->getMock('Drupal\Core\Entity\EntityStorageInterface');
$migration_storage->expects($this->once())
->method('loadMultiple')
->with(['test_a', 'test_b', 'test_c', 'test_d'])
->willReturn(['test_b' => $migration_b, 'test_c' => $migration_c, 'test_d' => $migration_d]);
$entity_manager->expects($this->once())
->method('getStorage')
->with('migration')
->willReturn($migration_storage);
$migration->checkRequirements();
}
}
class TestMigration extends Migration {
public function __construct() {
}
public function setRequirements(array $requirements) {
$this->requirements = $requirements;
}
public function setSourcePlugin(MigrateSourceInterface $source_plugin) {
$this->sourcePlugin = $source_plugin;
}
public function setDestinationPlugin(MigrateDestinationInterface $destination_plugin) {
$this->destinationPlugin = $destination_plugin;
}
public function setEntityManager(EntityManagerInterface $entity_manager) {
$this->entityManager = $entity_manager;
}
}
interface RequirementsAwareSourceInterface extends MigrateSourceInterface, RequirementsInterface {}
interface RequirementsAwareDestinationInterface extends MigrateDestinationInterface, RequirementsInterface {}
......@@ -11,6 +11,7 @@
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Plugin\PluginBase;
use Drupal\migrate\Entity\MigrationInterface;
use Drupal\migrate\Exception\RequirementsException;
use Drupal\migrate\MigrateException;
use Drupal\migrate\Plugin\SourceEntityInterface;
use Drupal\migrate_drupal\Plugin\MigrateLoadInterface;
......@@ -79,11 +80,15 @@ public function loadMultiple(EntityStorageInterface $storage, array $sub_ids = N
$values['source']['bundle'] = $id;
/** @var \Drupal\migrate_drupal\Entity\MigrationInterface $migration */
$migration = $storage->create($values);
if ($migration->getSourcePlugin()->checkRequirements()) {
try {
$migration->getSourcePlugin()->checkRequirements();
$fields = array_keys($migration->getSourcePlugin()->fields());
$migration->process += array_combine($fields, $fields);
$migrations[$migration->id()] = $migration;
}
catch (RequirementsException $e) {
}
}
return $migrations;
......
......@@ -7,8 +7,10 @@
namespace Drupal\migrate_drupal\Plugin\migrate\source;
use Drupal\Component\Utility\String;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\migrate\Entity\MigrationInterface;
use Drupal\migrate\Exception\RequirementsException;
use Drupal\migrate\Plugin\migrate\source\SqlBase;
use Drupal\migrate\Plugin\RequirementsInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
......@@ -63,36 +65,30 @@ public function getSystemData() {
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) {
$plugin = new static(
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$migration
);
/** @var \Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase $plugin */
if ($plugin_definition['requirements_met'] === TRUE) {
if (isset($plugin_definition['source_provider'])) {
if ($plugin->moduleExists($plugin_definition['source_provider'])) {
if (isset($plugin_definition['minimum_schema_version']) && !$plugin->getModuleSchemaVersion($plugin_definition['source_provider']) < $plugin_definition['minimum_schema_version']) {
$plugin->checkRequirements(FALSE);
}
}
else {
$plugin->checkRequirements(FALSE);
}
}
}
return $plugin;
}
/**
* {@inheritdoc}
*/
public function checkRequirements($new_value = NULL) {
if (isset($new_value)) {
$this->requirements = $new_value;
public function checkRequirements() {
if ($this->pluginDefinition['requirements_met'] === TRUE) {
if (isset($this->pluginDefinition['source_provider'])) {
if ($this->moduleExists($this->pluginDefinition['source_provider'])) {
if (isset($this->pluginDefinition['minimum_schema_version']) && !$this->getModuleSchemaVersion($this->pluginDefinition['source_provider']) < $this->pluginDefinition['minimum_schema_version']) {
throw new RequirementsException(String::format('Required minimum schema version @minimum_schema_version', ['@minimum_schema_version' => $this->pluginDefinition['minimum_schema_version']]), ['minimum_schema_version' => $this->pluginDefinition['minimum_schema_version']]);
}
}
else {
throw new RequirementsException(String::format('Missing source provider @provider', ['@provider' => $this->pluginDefinition['source_provider']]), ['source_provider' => $this->pluginDefinition['source_provider']]);
}
}
}
return $this->requirements;
}
/**
......
......@@ -64,7 +64,7 @@ public function testAggregatorMigrateDependencies() {
$executable = new MigrateExecutable($migration, $this);
$this->startCollectingMessages();
$executable->import();
$this->assertEqual($this->migrateMessages['error'], array(String::format('Migration @id did not meet the requirements', array('@id' => $migration->id()))));
$this->assertEqual($this->migrateMessages['error'], array(String::format('Migration @id did not meet the requirements. Missing migrations d6_aggregator_feed. requirements: d6_aggregator_feed.', array('@id' => $migration->id()))));
$this->collectMessages = FALSE;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment