From 5d12af3d39bb6f6483d19cebbfea1085d2bac8e8 Mon Sep 17 00:00:00 2001
From: mikeryan <mikeryan@4420.no-reply.drupal.org>
Date: Tue, 9 May 2017 14:17:24 -0500
Subject: [PATCH] Issue #2752335 by mikeryan, dpalmer, joelpittet, ckaotik,
 heddn, Berdir, chx, Wim Leers: Properly integrate configuration-entity-based
 migrations with the core plugin manager

---
 README.txt                                    |  9 +---
 migrate_example/README.txt                    | 30 +++++++++---
 .../beer_comment.yml}                         |  0
 migrate_plus.info.yml                         |  2 +-
 migrate_plus.module                           | 13 +++++
 migrate_plus.services.yml                     |  3 --
 migrations/migration_config_deriver.yml       |  6 +++
 .../Discovery/ConfigEntityDiscovery.php       | 49 -------------------
 src/Plugin/MigrationConfigDeriver.php         | 28 +++++++++++
 .../MigrationConfigEntityPluginManager.php    | 25 ----------
 .../src/Kernel/MigrationConfigEntityTest.php  | 18 +++----
 tests/src/Kernel/MigrationGroupTest.php       |  2 +-
 12 files changed, 82 insertions(+), 103 deletions(-)
 rename migrate_example/{config/install/migrate_plus.migration.beer_comment.yml => migrations/beer_comment.yml} (100%)
 create mode 100644 migrations/migration_config_deriver.yml
 delete mode 100644 src/Plugin/Discovery/ConfigEntityDiscovery.php
 create mode 100644 src/Plugin/MigrationConfigDeriver.php
 delete mode 100644 src/Plugin/MigrationConfigEntityPluginManager.php

diff --git a/README.txt b/README.txt
index af3cafbc..797082e3 100644
--- a/README.txt
+++ b/README.txt
@@ -5,13 +5,6 @@ Extensions to base API
 ======================
 * A Migration configuration entity is provided, enabling persistance of dynamic
   migration configuration.
-* A ConfigEntityDiscovery class is implemented which enables plugin
-  configuration to be based on configuration entities. This is fully general -
-  it can be used for any configuration entity type, not just migrations.
-* A MigrationConfigEntityPluginManager class and corresponding
-  plugin.manager.config_entity_migration service is provided, to enable
-  discovery and instantiation of migration plugins based on the Migration
-  configuration entity.
 * A MigrationGroup configuration entity is provided, which enables migrations to
   be organized in groups, and to maintain shared configuration in one place.
 * A MigrateEvents::PREPARE_ROW event is provided to dispatch hook_prepare_row()
@@ -73,7 +66,7 @@ Authentication
 --------------
 * The basic authentication plugin provides HTTP Basic authentication.
 * The digest authentication plugin provides HTTP Digest authentication.
-* The oauth2 authencitation plugin provides OAuth2 authentication over HTTP.
+* The oauth2 authentication plugin provides OAuth2 authentication over HTTP.
 
 Examples
 ========
diff --git a/migrate_example/README.txt b/migrate_example/README.txt
index 125e3b0f..32d77da9 100644
--- a/migrate_example/README.txt
+++ b/migrate_example/README.txt
@@ -22,12 +22,30 @@ STRUCTURE
 ---------
 There are two primary components to this example:
 
-1. Migration configuration, in the config/install directory. These YAML files
-   describe the migration process and provide the mappings from the source data
-   to Drupal's destination entities. The YAML file names are prefixed with
-   'migrate_plus.migration.' (because, reading from right to left, they define
-   "migration" configuration entities, and the configuration entity type is
-   defined by the "migrate_plus" module).
+1. Migration configuration, in the migrations and config/install directories.
+   These YAML files describe the migration process and provide the mappings from
+   the source data to Drupal's destination entities. The difference between the
+   two possible directories:
+
+   a. Files in the migrations directory provide configuration directly for the
+   migration plugins. The filenames are of the form <migration ID>.yml. This
+   approach is recommended when your migration configuration is fully hardcoded
+   and does not need to be overridden (e.g., you don't need to change the URL to
+   a source web service through an admin UI). While developing migrations,
+   changes to these files require at most a 'drush cr' to load your changes.
+
+   b. Files in the config/install directory provide migration configuration as
+   configuration entities, and have names of the form
+   migrate_plus.migration.<migration ID>.yml ("migration" because they define
+   entities of the "migration" type, and "migrate_plus" because that is the
+   module which implements the "migration" type). Migrations defined in this way
+   may have their configuration modified (in particular, through a web UI) by
+   loading the configuration entity, modifying its configuration, and saving the
+   entity. When developing, to get edits to the .yml files in config/install to
+   take effect in active configuration, use the config_devel module.
+
+   Configuration in either type of file is identical - the only differences are
+   the directories and filenames.
 
 2. Source plugins, in src/Plugin/migrate/source. These are referenced from the
    configuration files, and provide the source data to the migration processing
diff --git a/migrate_example/config/install/migrate_plus.migration.beer_comment.yml b/migrate_example/migrations/beer_comment.yml
similarity index 100%
rename from migrate_example/config/install/migrate_plus.migration.beer_comment.yml
rename to migrate_example/migrations/beer_comment.yml
diff --git a/migrate_plus.info.yml b/migrate_plus.info.yml
index 2eb2a605..05b5477b 100644
--- a/migrate_plus.info.yml
+++ b/migrate_plus.info.yml
@@ -4,4 +4,4 @@ description: 'Enhancements to core migration support'
 package: Migration
 core: 8.x
 dependencies:
-  - drupal:migrate (>=8.2)
+  - drupal:migrate (>=8.3)
diff --git a/migrate_plus.module b/migrate_plus.module
index 67efba62..0dbddc47 100644
--- a/migrate_plus.module
+++ b/migrate_plus.module
@@ -23,6 +23,19 @@ function migrate_plus_migration_plugins_alter(array &$migrations) {
       $migrations[$id]['class'] = 'Drupal\migrate\Plugin\Migration';
     }
 
+    // For derived configuration entity-based migrations, strip the deriver
+    // prefix so we can reference migrations by the IDs they specify (i.e.,
+    // the migration that specifies "id: temp" can be referenced as "temp"
+    // rather than "migration_config_deriver:temp").
+    $prefix = 'migration_config_deriver:';
+    if (strpos($id, $prefix) === 0) {
+      $new_id = substr($id, strlen($prefix));
+      $migrations[$new_id] = $migrations[$id];
+      unset($migrations[$id]);
+      $id = $new_id;
+    }
+
+    // Integrate shared group configuration into the migration.
     if (empty($migration['migration_group'])) {
       $migration['migration_group'] = 'default';
     }
diff --git a/migrate_plus.services.yml b/migrate_plus.services.yml
index 4b334456..2ca3363c 100644
--- a/migrate_plus.services.yml
+++ b/migrate_plus.services.yml
@@ -8,6 +8,3 @@ services:
   plugin.manager.migrate_plus.data_parser:
     class: Drupal\migrate_plus\DataParserPluginManager
     parent: default_plugin_manager
-  plugin.manager.config_entity_migration:
-    class: Drupal\migrate_plus\Plugin\MigrationConfigEntityPluginManager
-    parent: plugin.manager.migration
diff --git a/migrations/migration_config_deriver.yml b/migrations/migration_config_deriver.yml
new file mode 100644
index 00000000..9fc597e7
--- /dev/null
+++ b/migrations/migration_config_deriver.yml
@@ -0,0 +1,6 @@
+id: migration_config_deriver
+deriver: Drupal\migrate_plus\Plugin\MigrationConfigDeriver
+# @todo: Remove if/when https://www.drupal.org/node/2797421 is fixed.
+# Unused source configuration must be added to prevent errors.
+source:
+  plugin: embedded_data
diff --git a/src/Plugin/Discovery/ConfigEntityDiscovery.php b/src/Plugin/Discovery/ConfigEntityDiscovery.php
deleted file mode 100644
index 551c8a7a..00000000
--- a/src/Plugin/Discovery/ConfigEntityDiscovery.php
+++ /dev/null
@@ -1,49 +0,0 @@
-<?php
-
-namespace Drupal\migrate_plus\Plugin\Discovery;
-
-use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
-use Drupal\Component\Plugin\Discovery\DiscoveryTrait;
-
-/**
- * Allows configuration entities to define plugin definitions.
- */
-class ConfigEntityDiscovery implements DiscoveryInterface {
-
-  use DiscoveryTrait;
-
-  /**
-   * Entity type to query.
-   *
-   * @var string
-   */
-  protected $entityType;
-
-  /**
-   * Construct a YamlDiscovery object.
-   *
-   * @param string $entity_type
-   *   The entity type to query for.
-   */
-  function __construct($entity_type) {
-    $this->entityType = $entity_type;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getDefinitions() {
-    $definition = \Drupal::entityTypeManager()->getDefinition($this->entityType);
-    $prefix = $definition->getConfigPrefix() . '.';
-    $storage = \Drupal::service('config.storage');
-    $query = \Drupal::entityQuery($this->entityType);
-    $ids = $query->execute();
-    $definitions = [];
-    foreach ($ids as $id) {
-      $definitions[$id] = $storage->read($prefix . $id);
-    }
-
-    return $definitions;
-  }
-
-}
diff --git a/src/Plugin/MigrationConfigDeriver.php b/src/Plugin/MigrationConfigDeriver.php
new file mode 100644
index 00000000..01354449
--- /dev/null
+++ b/src/Plugin/MigrationConfigDeriver.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace Drupal\migrate_plus\Plugin;
+
+use Drupal\Component\Plugin\Derivative\DeriverBase;
+use Drupal\migrate_plus\Entity\Migration;
+
+/**
+ * Expose migration entities in the active config store as derivative plugins.
+ */
+class MigrationConfigDeriver extends DeriverBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDerivativeDefinitions($base_plugin_definition) {
+    // Always rederive from scratch, because changes may have been made without
+    // clearing our internal cache.
+    $this->derivatives = [];
+    $migrations = Migration::loadMultiple();
+    /** @var \Drupal\migrate_plus\Entity\MigrationInterface $migration */
+    foreach ($migrations as $id => $migration) {
+      $this->derivatives[$id] = $migration->toArray();
+    }
+    return $this->derivatives;
+  }
+
+}
diff --git a/src/Plugin/MigrationConfigEntityPluginManager.php b/src/Plugin/MigrationConfigEntityPluginManager.php
deleted file mode 100644
index 25009758..00000000
--- a/src/Plugin/MigrationConfigEntityPluginManager.php
+++ /dev/null
@@ -1,25 +0,0 @@
-<?php
-
-namespace Drupal\migrate_plus\Plugin;
-
-use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator;
-use Drupal\migrate\Plugin\MigrationPluginManager;
-use Drupal\migrate_plus\Plugin\Discovery\ConfigEntityDiscovery;
-
-/**
- * Plugin manager for migration plugins.
- */
-class MigrationConfigEntityPluginManager extends MigrationPluginManager {
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function getDiscovery() {
-    if (!isset($this->discovery)) {
-      $discovery = new ConfigEntityDiscovery('migration');
-      $this->discovery = new ContainerDerivativeDiscoveryDecorator($discovery);
-    }
-    return $this->discovery;
-  }
-
-}
diff --git a/tests/src/Kernel/MigrationConfigEntityTest.php b/tests/src/Kernel/MigrationConfigEntityTest.php
index f309df4a..386363a3 100644
--- a/tests/src/Kernel/MigrationConfigEntityTest.php
+++ b/tests/src/Kernel/MigrationConfigEntityTest.php
@@ -4,7 +4,6 @@ namespace Drupal\Tests\migrate_plus\Kernel;
 
 use Drupal\KernelTests\KernelTestBase;
 use Drupal\migrate_plus\Entity\Migration;
-use Drupal\migrate_plus\Plugin\MigrationConfigEntityPluginManager;
 
 /**
  * Test migration config entity discovery.
@@ -16,13 +15,13 @@ class MigrationConfigEntityTest extends KernelTestBase {
   public static $modules = ['migrate', 'migrate_plus'];
 
   /**
-   * @var MigrationConfigEntityPluginManager
+   * @var \Drupal\migrate\Plugin\MigrationPluginManager
    */
-  protected $pluginMananger;
+  protected $pluginManager;
 
   protected function setUp() {
     parent::setUp();
-    $this->pluginMananger = \Drupal::service('plugin.manager.config_entity_migration');
+    $this->pluginManager = \Drupal::service('plugin.manager.migration');
   }
 
   public function testCacheInvalidation() {
@@ -36,19 +35,18 @@ class MigrationConfigEntityTest extends KernelTestBase {
     ]);
     $config->save();
 
-    $this->assertTrue($this->pluginMananger->getDefinition('test'));
-    $this->assertSame('Label A', $this->pluginMananger->getDefinition('test')['label']);
+    $this->assertTrue($this->pluginManager->getDefinition('test'));
+    $this->assertSame('Label A', $this->pluginManager->getDefinition('test')['label']);
 
     // Clear static cache in the plugin manager, the cache tag take care of the
     // persistent cache.
-    $this->pluginMananger->useCaches(FALSE);
-    $this->pluginMananger->useCaches(TRUE);
+    $this->pluginManager->useCaches(FALSE);
+    $this->pluginManager->useCaches(TRUE);
 
     $config->set('label', 'Label B');
     $config->save();
 
-    $this->assertSame('Label B', $this->pluginMananger->getDefinition('test')['label']);
-    $this->assertSame('Label B', \Drupal::service('plugin.manager.migration')->getDefinition('test')['label']);
+    $this->assertSame('Label B', $this->pluginManager->getDefinition('test')['label']);
   }
 
 }
diff --git a/tests/src/Kernel/MigrationGroupTest.php b/tests/src/Kernel/MigrationGroupTest.php
index 42c1f9f8..4cdd5525 100644
--- a/tests/src/Kernel/MigrationGroupTest.php
+++ b/tests/src/Kernel/MigrationGroupTest.php
@@ -76,7 +76,7 @@ class MigrationGroupTest extends KernelTestBase {
       'destination' => ['plugin' => 'field_storage_config'],
     ];
     /** @var \Drupal\migrate\Plugin\MigrationInterface $loaded_migration */
-    $loaded_migration = $this->container->get('plugin.manager.config_entity_migration')
+    $loaded_migration = $this->container->get('plugin.manager.migration')
       ->createInstance('specific_migration');
     foreach ($expected_config as $key => $expected_value) {
       $actual_value = $loaded_migration->get($key);
-- 
GitLab