Unverified Commit f22b0385 authored by larowlan's avatar larowlan

Issue #2908282 by heddn, phenaproxima, Jo Fitzgerald, quietone, xjm, larowlan,...

Issue #2908282 by heddn, phenaproxima, Jo Fitzgerald, quietone, xjm, larowlan, mikeryan, dipakmdhrm: Throw exception for source plugins without a source_module property
parent 9d4f798f
...@@ -22,7 +22,7 @@ class BadPluginDefinitionException extends InvalidPluginDefinitionException { ...@@ -22,7 +22,7 @@ class BadPluginDefinitionException extends InvalidPluginDefinitionException {
* @see \Exception * @see \Exception
*/ */
public function __construct($plugin_id, $property, $code = 0, \Exception $previous = NULL) { public function __construct($plugin_id, $property, $code = 0, \Exception $previous = NULL) {
$message = sprintf('The %s plugin should define the %s property.', $plugin_id, $property); $message = sprintf('The %s plugin must define the %s property.', $plugin_id, $property);
parent::__construct($plugin_id, $message, $code, $previous); parent::__construct($plugin_id, $message, $code, $previous);
} }
......
...@@ -38,7 +38,8 @@ ...@@ -38,7 +38,8 @@
* @see \Drupal\migrate\Plugin\MigrateSourceInterface * @see \Drupal\migrate\Plugin\MigrateSourceInterface
* *
* @MigrateSource( * @MigrateSource(
* id = "embedded_data" * id = "embedded_data",
* source_module = "migrate"
* ) * )
*/ */
class EmbeddedDataSource extends SourcePluginBase { class EmbeddedDataSource extends SourcePluginBase {
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
namespace Drupal\Tests\migrate\Kernel\Plugin; namespace Drupal\Tests\migrate\Kernel\Plugin;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\KernelTests\FileSystemModuleDiscoveryDataProviderTrait; use Drupal\KernelTests\FileSystemModuleDiscoveryDataProviderTrait;
use Drupal\migrate\Plugin\Exception\BadPluginDefinitionException; use Drupal\migrate\Plugin\Exception\BadPluginDefinitionException;
use Drupal\migrate_drupal\Plugin\MigrateFieldPluginManager; use Drupal\migrate_drupal\Plugin\MigrateFieldPluginManager;
...@@ -18,50 +17,43 @@ class MigrationProvidersExistTest extends MigrateDrupalTestBase { ...@@ -18,50 +17,43 @@ class MigrationProvidersExistTest extends MigrateDrupalTestBase {
use FileSystemModuleDiscoveryDataProviderTrait; use FileSystemModuleDiscoveryDataProviderTrait;
/** /**
* {@inheritdoc} * Tests that a missing source_module property raises an exception.
*/ */
public static $modules = ['migration_provider_test']; public function testSourceProvider() {
$this->enableModules(['migration_provider_test']);
$this->setExpectedException(BadPluginDefinitionException::class, 'The no_source_module plugin must define the source_module property.');
$this->container->get('plugin.manager.migration')->getDefinition('migration_provider_no_annotation');
}
/** /**
* Tests that modules exist for all source and destination plugins. * Tests that modules exist for all source plugins.
*/ */
public function testProvidersExist() { public function testProvidersExist() {
// Install all available modules. $this->enableAllModules();
$module_handler = $this->container->get('module_handler');
$modules = $this->coreModuleListDataProvider();
$modules_enabled = $module_handler->getModuleList();
$modules_to_enable = array_keys(array_diff_key($modules, $modules_enabled));
$this->enableModules($modules_to_enable);
/** @var \Drupal\migrate\Plugin\MigrationPluginManager $plugin_manager */ /** @var \Drupal\migrate\Plugin\MigrationPluginManager $plugin_manager */
$plugin_manager = $this->container->get('plugin.manager.migration'); $plugin_manager = $this->container->get('plugin.manager.migration');
// Get all the migrations.
$migrations = $plugin_manager->createInstances(array_keys($plugin_manager->getDefinitions())); // Instantiate all migrations.
// Ensure the test module was enabled. $migrations = array_keys($plugin_manager->getDefinitions());
$this->assertTrue(array_key_exists('migration_provider_test', $migrations)); $migrations = $plugin_manager->createInstances($migrations);
$this->assertTrue(array_key_exists('migration_provider_no_annotation', $migrations));
/** @var \Drupal\migrate\Plugin\Migration $migration */ /** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
foreach ($migrations as $migration) { foreach ($migrations as $migration) {
$source_module = $migration->getSourcePlugin()->getSourceModule(); $this->assertInternalType('string', $migration->getSourcePlugin()->getSourceModule());
$destination_module = $migration->getDestinationPlugin()->getDestinationModule();
$migration_id = $migration->getPluginId();
if ($migration_id == 'migration_provider_test') {
$this->assertFalse($source_module, new FormattableMarkup('Source module not found for @migration_id.', ['@migration_id' => $migration_id]));
$this->assertFalse($destination_module, new FormattableMarkup('Destination module not found for @migration_id.', ['@migration_id' => $migration_id]));
}
elseif ($migration_id == 'migration_provider_no_annotation') {
$this->assertFalse($source_module, new FormattableMarkup('Source module not found for @migration_id.', ['@migration_id' => $migration_id]));
$this->assertTrue($destination_module, new FormattableMarkup('Destination module found for @migration_id.', ['@migration_id' => $migration_id]));
} }
else {
$this->assertTrue($source_module, new FormattableMarkup('Source module found for @migration_id.', ['@migration_id' => $migration_id]));
$this->assertTrue($destination_module, new FormattableMarkup('Destination module found for @migration_id.', ['@migration_id' => $migration_id]));
}
// Destination module can't be migrate or migrate_drupal or
// migrate_drupal_ui.
$invalid_destinations = ['migrate', 'migrate_drupal', 'migrate_drupal_ui'];
$this->assertNotContains($destination_module, $invalid_destinations, new FormattableMarkup('Invalid destination for @migration_id.', ['@migration_id' => $migration_id]));
} }
/**
* Enable all available modules.
*/
protected function enableAllModules() {
// Install all available modules.
$module_handler = $this->container->get('module_handler');
$modules = $this->coreModuleListDataProvider();
$modules_enabled = $module_handler->getModuleList();
$modules_to_enable = array_keys(array_diff_key($modules, $modules_enabled));
$this->enableModules($modules_to_enable);
} }
/** /**
...@@ -150,16 +142,9 @@ public function testFieldProvidersExist() { ...@@ -150,16 +142,9 @@ public function testFieldProvidersExist() {
'destination_module' => 'core', 'destination_module' => 'core',
], ],
]; ];
// Install all available modules. $this->enableAllModules();
$module_handler = $this->container->get('module_handler');
$modules = $this->coreModuleListDataProvider();
$modules_enabled = $module_handler->getModuleList();
$modules_to_enable = array_keys(array_diff_key($modules, $modules_enabled));
$this->enableModules($modules_to_enable);
/** @var \Drupal\migrate_drupal\Plugin\MigrateFieldPluginManager $plugin_manager */ $definitions = $this->container->get('plugin.manager.migrate.field')->getDefinitions();
$plugin_manager = $this->container->get('plugin.manager.migrate.field');
$definitions = $plugin_manager->getDefinitions();
foreach ($definitions as $key => $definition) { foreach ($definitions as $key => $definition) {
$this->assertArrayHasKey($key, $expected_mappings); $this->assertArrayHasKey($key, $expected_mappings);
$this->assertEquals($expected_mappings[$key]['source_module'], $definition['source_module']); $this->assertEquals($expected_mappings[$key]['source_module'], $definition['source_module']);
...@@ -192,7 +177,7 @@ public function testFieldProviderMissingRequiredProperty(array $definitions, $mi ...@@ -192,7 +177,7 @@ public function testFieldProviderMissingRequiredProperty(array $definitions, $mi
$plugin_manager->method('getDiscovery') $plugin_manager->method('getDiscovery')
->willReturn($discovery); ->willReturn($discovery);
$this->setExpectedException(BadPluginDefinitionException::class, "The missing_{$missing_property} plugin should define the $missing_property property."); $this->setExpectedException(BadPluginDefinitionException::class, "The missing_{$missing_property} plugin must define the $missing_property property.");
$plugin_manager->getDefinitions(); $plugin_manager->getDefinitions();
} }
......
# Migrations with any of these tags will raise an exception if their source
# plugin is missing the source_module property in their annotation.
enforce_source_module_tags:
- Drupal 6
- Drupal 7
migrate_drupal.settings:
type: config_object
label: 'Migrate Drupal settings'
mapping:
enforce_source_module_tags:
type: sequence
label: 'source_module enforcement tags'
sequence:
type: string
label: 'Tag'
<?php
/**
* @file
* Contains install and update functions for Migrate Drupal
*/
/**
* Creates migrate_drupal.settings config object.
*/
function migrate_drupal_update_8501() {
\Drupal::configFactory()
->getEditable('migrate_drupal.settings')
->set('enforce_source_module_tags', ['Drupal 6', 'Drupal 7'])
->save();
}
<?php
namespace Drupal\migrate_drupal;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceProviderBase;
use Symfony\Component\DependencyInjection\Reference;
/**
* Alters container services.
*/
class MigrateDrupalServiceProvider extends ServiceProviderBase {
/**
* {@inheritdoc}
*/
public function alter(ContainerBuilder $container) {
parent::alter($container);
$container->getDefinition('plugin.manager.migration')
->setClass(MigrationPluginManager::class)
->addArgument(new Reference('plugin.manager.migrate.source'))
->addArgument(new Reference('config.factory'));
}
}
<?php
namespace Drupal\migrate_drupal;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\migrate\Plugin\Exception\BadPluginDefinitionException;
use Drupal\migrate\Plugin\MigrateSourcePluginManager;
use Drupal\migrate\Plugin\MigrationPluginManager as BaseMigrationPluginManager;
/**
* Manages migration plugins.
*
* Analyzes migration definitions to ensure that the source plugin of any
* migration tagged with particular tags ('Drupal 6' or 'Drupal 7' by default)
* defines a source_module property in its plugin annotation. This is done in
* order to support the Migrate Drupal UI, which needs to know which modules
* "own" the data being migrated into Drupal 8, on both the source and
* destination sides.
*
* @todo Enforce the destination_module property too, in
* https://www.drupal.org/project/drupal/issues/2923810.
*/
class MigrationPluginManager extends BaseMigrationPluginManager {
/**
* The Migrate source plugin manager service.
*
* @var \Drupal\migrate\Plugin\MigrateSourcePluginManager
*/
protected $sourceManager;
/**
* The config factory service.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* The migration tags which will trigger source_module enforcement.
*
* @var string[]
*/
protected $enforcedSourceModuleTags;
/**
* MigrationPluginManager constructor.
*
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler service.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
* The cache backend.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager service.
* @param \Drupal\migrate\Plugin\MigrateSourcePluginManager $source_manager
* The Migrate source plugin manager service.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory service.
*/
public function __construct(ModuleHandlerInterface $module_handler, CacheBackendInterface $cache_backend, LanguageManagerInterface $language_manager, MigrateSourcePluginManager $source_manager, ConfigFactoryInterface $config_factory) {
parent::__construct($module_handler, $cache_backend, $language_manager);
$this->sourceManager = $source_manager;
$this->configFactory = $config_factory;
}
/**
* Returns the migration tags that trigger source_module enforcement.
*
* @return string[]
*/
protected function getEnforcedSourceModuleTags() {
if ($this->enforcedSourceModuleTags === NULL) {
$this->enforcedSourceModuleTags = $this->configFactory
->get('migrate_drupal.settings')
->get('enforce_source_module_tags') ?: [];
}
return $this->enforcedSourceModuleTags;
}
/**
* {@inheritdoc}
*/
public function processDefinition(&$definition, $plugin_id) {
parent::processDefinition($definition, $plugin_id);
// If the migration has no tags, we don't need to enforce the source_module
// annotation property.
if (empty($definition['migration_tags'])) {
return;
}
// Check if the migration has any of the tags that trigger source_module
// enforcement.
$applied_tags = array_intersect($this->getEnforcedSourceModuleTags(), $definition['migration_tags']);
if ($applied_tags) {
// Throw an exception if the source plugin definition does not define a
// source_module.
$source_id = $definition['source']['plugin'];
$source_definition = $this->sourceManager->getDefinition($source_id);
if (empty($source_definition['source_module'])) {
throw new BadPluginDefinitionException($source_id, 'source_module');
}
}
}
}
...@@ -14,7 +14,8 @@ ...@@ -14,7 +14,8 @@
* Source returning an empty row with Drupal specific config dependencies. * Source returning an empty row with Drupal specific config dependencies.
* *
* @MigrateSource( * @MigrateSource(
* id = "md_empty" * id = "md_empty",
* source_module = "system",
* ) * )
*/ */
class EmptySource extends BaseEmptySource implements ContainerFactoryPluginInterface, DependentPluginInterface { class EmptySource extends BaseEmptySource implements ContainerFactoryPluginInterface, DependentPluginInterface {
......
...@@ -13,7 +13,8 @@ ...@@ -13,7 +13,8 @@
* example for any normal source class returning multiple rows. * example for any normal source class returning multiple rows.
* *
* @MigrateSource( * @MigrateSource(
* id = "variable" * id = "variable",
* source_module = "system",
* ) * )
*/ */
class Variable extends DrupalSqlBase { class Variable extends DrupalSqlBase {
......
...@@ -11,7 +11,8 @@ ...@@ -11,7 +11,8 @@
* variable. * variable.
* *
* @MigrateSource( * @MigrateSource(
* id = "variable_multirow" * id = "variable_multirow",
* source_module = "system",
* ) * )
*/ */
class VariableMultiRow extends DrupalSqlBase { class VariableMultiRow extends DrupalSqlBase {
......
...@@ -11,7 +11,8 @@ ...@@ -11,7 +11,8 @@
* Drupal i18n_variable source from database. * Drupal i18n_variable source from database.
* *
* @MigrateSource( * @MigrateSource(
* id = "variable_translation" * id = "variable_translation",
* source_module = "system",
* ) * )
*/ */
class VariableTranslation extends DrupalSqlBase { class VariableTranslation extends DrupalSqlBase {
......
...@@ -8,7 +8,8 @@ ...@@ -8,7 +8,8 @@
* Drupal i18n_variable source from database. * Drupal i18n_variable source from database.
* *
* @MigrateSource( * @MigrateSource(
* id = "i18n_variable" * id = "i18n_variable",
* source_module = "system",
* ) * )
* *
* @deprecated in Drupal 8.4.x and will be removed in Drupal 9.0.x. Use * @deprecated in Drupal 8.4.x and will be removed in Drupal 9.0.x. Use
......
...@@ -9,7 +9,8 @@ ...@@ -9,7 +9,8 @@
* Drupal config source from database. * Drupal config source from database.
* *
* @MigrateSource( * @MigrateSource(
* id = "d8_config" * id = "d8_config",
* source_module = "system",
* ) * )
*/ */
class Config extends DrupalSqlBase { class Config extends DrupalSqlBase {
......
id: migration_provider_test
label: Missing source and destination provider
migration_tags:
- Drupal 6
- Drupal 7
source:
plugin: variable
variables:
- site_offline_message
# Do not add a provider for the test.
process:
message: site_offline_message
destination:
plugin: config
# An empty config_name will not have a destination provider.
config_name:
\ No newline at end of file
...@@ -109,8 +109,8 @@ public function testMigrateUpgrade() { ...@@ -109,8 +109,8 @@ public function testMigrateUpgrade() {
$session->responseContains('Upgrade a site by importing its files and the data from its database into a clean and empty new install of Drupal 8.'); $session->responseContains('Upgrade a site by importing its files and the data from its database into a clean and empty new install of Drupal 8.');
$this->drupalPostForm(NULL, [], t('Continue')); $this->drupalPostForm(NULL, [], t('Continue'));
$this->assertText('Provide credentials for the database of the Drupal site you want to upgrade.'); $session->pageTextContains('Provide credentials for the database of the Drupal site you want to upgrade.');
$this->assertFieldByName('mysql[host]'); $session->fieldExists('mysql[host]');
$driver = $connection_options['driver']; $driver = $connection_options['driver'];
$connection_options['prefix'] = $connection_options['prefix']['default']; $connection_options['prefix'] = $connection_options['prefix']['default'];
...@@ -140,29 +140,12 @@ public function testMigrateUpgrade() { ...@@ -140,29 +140,12 @@ public function testMigrateUpgrade() {
// Ensure submitting the form with invalid database credentials gives us a // Ensure submitting the form with invalid database credentials gives us a
// nice warning. // nice warning.
$this->drupalPostForm(NULL, [$driver . '[database]' => 'wrong'] + $edits, t('Review upgrade')); $this->drupalPostForm(NULL, [$driver . '[database]' => 'wrong'] + $edits, t('Review upgrade'));
$this->assertText('Resolve the issue below to continue the upgrade.'); $session->pageTextContains('Resolve the issue below to continue the upgrade.');
$this->drupalPostForm(NULL, $edits, t('Review upgrade')); $this->drupalPostForm(NULL, $edits, t('Review upgrade'));
$session->pageTextContains('WARNING: Content may be overwritten on your new site.');
$session->pageTextContains('There is conflicting content of these types:');
$session->pageTextContains('aggregator feed entities');
$session->pageTextContains('aggregator feed item entities');
$session->pageTextContains('custom block entities');
$session->pageTextContains('custom menu link entities');
$session->pageTextContains('file entities');
$session->pageTextContains('taxonomy term entities');
$session->pageTextContains('user entities');
$session->pageTextContains('comments');
$session->pageTextContains('content item revisions');
$session->pageTextContains('content items');
$session->pageTextContains('There is translated content of these types:');
$this->drupalPostForm(NULL, [], t('I acknowledge I may lose data. Continue anyway.'));
$this->assertResponse(200);
$this->assertText('Upgrade analysis report');
// Ensure we get errors about missing modules. // Ensure we get errors about missing modules.
$session->pageTextContains(t('Source module not found for migration_provider_no_annotation.')); $session->pageTextContains(t('Resolve the issue below to continue the upgrade'));
$session->pageTextContains(t('Source module not found for migration_provider_test.')); $session->pageTextContains(t('The no_source_module plugin must define the source_module property.'));
$session->pageTextContains(t('Destination module not found for migration_provider_test'));
// Uninstall the module causing the missing module error messages. // Uninstall the module causing the missing module error messages.
$this->container->get('module_installer')->uninstall(['migration_provider_test'], TRUE); $this->container->get('module_installer')->uninstall(['migration_provider_test'], TRUE);
...@@ -177,13 +160,24 @@ public function testMigrateUpgrade() { ...@@ -177,13 +160,24 @@ public function testMigrateUpgrade() {
$this->drupalPostForm(NULL, $edits, t('Review upgrade')); $this->drupalPostForm(NULL, $edits, t('Review upgrade'));
$session->pageTextContains('WARNING: Content may be overwritten on your new site.'); $session->pageTextContains('WARNING: Content may be overwritten on your new site.');
$session->pageTextContains('There is conflicting content of these types:');
$session->pageTextContains('aggregator feed entities');
$session->pageTextContains('aggregator feed item entities');
$session->pageTextContains('custom block entities');
$session->pageTextContains('custom menu link entities');
$session->pageTextContains('file entities');
$session->pageTextContains('taxonomy term entities');
$session->pageTextContains('user entities');
$session->pageTextContains('comments');
$session->pageTextContains('content item revisions');
$session->pageTextContains('content items');
$session->pageTextContains('There is translated content of these types:');
$this->drupalPostForm(NULL, [], t('I acknowledge I may lose data. Continue anyway.')); $this->drupalPostForm(NULL, [], t('I acknowledge I may lose data. Continue anyway.'));
$session->statusCodeEquals(200); $session->statusCodeEquals(200);
$session->pageTextContains('Upgrade analysis report'); $session->pageTextContains('Upgrade analysis report');
// Ensure there are no errors about the missing modules from the test module. // Ensure there are no errors about the missing modules from the test module.
$session->pageTextNotContains(t('Source module not found for migration_provider_no_annotation.')); $session->pageTextNotContains(t('Source module not found for migration_provider_no_annotation.'));
$session->pageTextNotContains(t('Source module not found for migration_provider_test.')); $session->pageTextNotContains(t('Source module not found for migration_provider_test.'));
$session->pageTextNotContains(t('Destination module not found for migration_provider_test'));
// Ensure there are no errors about any other missing migration providers. // Ensure there are no errors about any other missing migration providers.
$session->pageTextNotContains(t('module not found')); $session->pageTextNotContains(t('module not found'));
......
...@@ -110,7 +110,6 @@ protected function getAvailablePaths() { ...@@ -110,7 +110,6 @@ protected function getAvailablePaths() {
'email', 'email',
'entityreference', 'entityreference',
'field', 'field',
'field_sql_storage',
'file', 'file',
'filefield', 'filefield',
'filter', 'filter',
......
...@@ -42,9 +42,7 @@ public function testTranslations() { ...@@ -42,9 +42,7 @@ public function testTranslations() {
// With content_translation, there should be translation migrations for // With content_translation, there should be translation migrations for
// each content type. // each content type.
$this->enableModules(['language', 'content_translation']); $this->enableModules(['language', 'content_translation']);
$migrations = $this->pluginManager->createInstances('d6_node_translation'); $this->assertTrue($this->container->get('plugin.manager.migration')->hasDefinition('d6_node_translation:story'), "Node translation migrations exist after content_translation installed");
$this->assertArrayHasKey('d6_node_translation:story', $migrations,
"Node translation migrations exist after content_translation installed");
} }
} }
...@@ -11,38 +11,17 @@ ...@@ -11,38 +11,17 @@
*/ */
class MigrateNodeDeriverTest extends MigrateDrupal7TestBase { class MigrateNodeDeriverTest extends MigrateDrupal7TestBase {
/**
* The migration plugin manager.
*
* @var \Drupal\migrate\Plugin\MigrationPluginManagerInterface
*/
protected $pluginManager;
/**
* The module handler service.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function setUp() { public static $modules = ['node'];
parent::setUp();
$this->pluginManager = $this->container->get('plugin.manager.migration');
$this->moduleHandler = $this->container->get('module_handler');
}
/** /**
* Test node translation migrations with translation disabled. * Test node translation migrations with translation disabled.
*/ */
public function testNoTranslations() { public function testNoTranslations() {
// Enabling node module for this test.
$this->enableModules(['node']);
// Without content_translation, there should be no translation migrations. // Without content_translation, there should be no translation migrations.
$migrations = $this->pluginManager->createInstances('d7_node_translation'); $migrations = $this->container->get('plugin.manager.migration')->createInstances('d7_node_translation');
$this->assertTrue($this->moduleHandler->moduleExists('node'));
$this->assertEmpty($migrations); $this->assertEmpty($migrations);
} }
...@@ -52,10 +31,8 @@ public function testNoTranslations() { ...@@ -52,10 +31,8 @@ public function testNoTranslations() {
public function testTranslations() { public function testTranslations() {
// With content_translation, there should be translation migrations for // With content_translation, there should be translation migrations for
// each content type. // each content type.
$this->enableModules(['language', 'content_translation', 'node', 'filter']); $this->enableModules(['language', 'content_translation', 'filter']);
$migrations = $this->pluginManager->createInstances('d7_node_translation'); $this->assertTrue($this->container->get('plugin.manager.migration')->hasDefinition('d7_node_translation:article'), "Node translation migrations exist after content_translation installed");
$this->assertArrayHasKey('d7_node_translation:article', $migrations,
"Node translation migrations exist after content_translation installed");
} }
} }
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