Commit 1076c7ae authored by catch's avatar catch
Browse files

Issue #2991683 by bircher, Krzysztof Domański, alexpott, Tim Bozeman,...

Issue #2991683 by bircher, Krzysztof Domański, alexpott, Tim Bozeman, ricardoamaro, mpotter, tstoeckler, larowlan: Move configuration transformation API in \Drupal\Core\Config namespace
parent 6dc692be
......@@ -315,9 +315,16 @@ services:
public: false
tags:
- { name: backend_overridable }
config.import_transformer:
class: Drupal\Core\Config\ImportStorageTransformer
arguments: ['@event_dispatcher', '@database', '@lock', '@lock.persistent']
config.storage.export:
class: Drupal\Core\Config\ReadOnlyStorage
arguments: ['@config.storage']
class: Drupal\Core\Config\ManagedStorage
arguments: ['@config.storage.export.manager']
config.storage.export.manager:
class: Drupal\Core\Config\ExportStorageManager
arguments: ['@config.storage', '@database', '@event_dispatcher', '@lock']
public: false
# @deprecated in Drupal 8.0.x and will be removed before 9.0.0. Use
# config.storage.sync instead.
# @see https://www.drupal.org/node/2574957
......
......@@ -143,4 +143,64 @@ final class ConfigEvents {
*/
const COLLECTION_INFO = 'config.collection_info';
/**
* Name of the event fired just before importing configuration.
*
* This event allows subscribers to modify the configuration which is about to
* be imported. The event listener method receives a
* \Drupal\Core\Config\StorageTransformEvent instance. This event contains a
* config storage which subscribers can interact with and which will finally
* be used to import the configuration from.
* Together with \Drupal\Core\Config\ConfigEvents::STORAGE_TRANSFORM_EXPORT
* subscribers can alter the active configuration in a config sync workflow
* instead of just overriding at runtime via the config-override system.
* This allows a complete customisation of the workflow including additional
* modules and editable configuration in different environments.
*
* @code
* $storage = $event->getStorage();
* @endcode
*
* This event is also fired when just viewing the difference of configuration
* to be imported independently of whether the import takes place or not.
* Use the \Drupal\Core\Config\ConfigEvents::IMPORT event to subscribe to the
* import having taken place.
*
* @Event
*
* @see \Drupal\Core\Config\StorageTransformEvent
* @see \Drupal\Core\Config\ConfigEvents::STORAGE_TRANSFORM_EXPORT
* @see \Drupal\Core\Config\ImportStorageTransformer::transform
*
* @var string
*/
const STORAGE_TRANSFORM_IMPORT = 'config.transform.import';
/**
* Name of the event fired when the export storage is used.
*
* This event allows subscribers to modify the configuration which is about to
* be exported. The event listener method receives a
* \Drupal\Core\Config\StorageTransformEvent instance. This event contains a
* config storage which subscribers can interact with and which will finally
* be used to export the configuration from.
*
* @code
* $storage = $event->getStorage();
* @endcode
*
* Typically subscribers will want to perform the reverse operation on the
* storage than for \Drupal\Core\Config\ConfigEvents::STORAGE_TRANSFORM_IMPORT
* to make sure successive exports and imports yield no difference.
*
* @Event
*
* @see \Drupal\Core\Config\StorageTransformEvent
* @see \Drupal\Core\Config\ConfigEvents::STORAGE_TRANSFORM_IMPORT
* @see \Drupal\Core\Config\ExportStorageManager::getStorage
*
* @var string
*/
const STORAGE_TRANSFORM_EXPORT = 'config.transform.export';
}
<?php
// @codingStandardsIgnoreStart
// @todo: Move this back to \Drupal\Core\Config in #2991683.
// Use this class with its class alias Drupal\Core\Config\ExportStorageManager
// @codingStandardsIgnoreEnd
namespace Drupal\config_environment\Core\Config;
namespace Drupal\Core\Config;
use Drupal\Core\Config\DatabaseStorage;
use Drupal\Core\Config\ReadOnlyStorage;
use Drupal\Core\Config\StorageCopyTrait;
use Drupal\Core\Config\StorageInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Lock\LockBackendInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
......@@ -17,9 +9,11 @@
/**
* The export storage manager dispatches an event for the export storage.
*
* @internal
* This class is not meant to be extended and is final to make sure the
* constructor and the getStorage method are both changed when this pattern is
* used in other circumstances.
*/
class ExportStorageManager implements StorageManagerInterface {
final class ExportStorageManager implements StorageManagerInterface {
use StorageCopyTrait;
......@@ -91,8 +85,7 @@ public function getStorage() {
}
self::replaceStorageContents($this->active, $this->storage);
// @todo: Use ConfigEvents::STORAGE_TRANSFORM_EXPORT in #2991683
$this->eventDispatcher->dispatch('config.transform.export', new StorageTransformEvent($this->storage));
$this->eventDispatcher->dispatch(ConfigEvents::STORAGE_TRANSFORM_EXPORT, new StorageTransformEvent($this->storage));
return new ReadOnlyStorage($this->storage);
}
......
<?php
// @codingStandardsIgnoreStart
// @todo: Move this back to \Drupal\Core\Config in #2991683.
// Use this class with its class alias Drupal\Core\Config\ImportStorageTransformer
// @codingStandardsIgnoreEnd
namespace Drupal\config_environment\Core\Config;
namespace Drupal\Core\Config;
use Drupal\Core\Config\ConfigImporter;
use Drupal\Core\Database\Connection;
use Drupal\Core\Lock\LockBackendInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Drupal\Core\Config\DatabaseStorage;
use Drupal\Core\Config\StorageCopyTrait;
use Drupal\Core\Config\StorageInterface;
/**
* Class ImportStorageTransformer.
* The import storage transformer helps to use the configuration management api.
*
* @internal
* This service does not implement an interface and is final because it is not
* meant to be replaced, extended or used in a different context.
* Its single purpose is to transform a storage for the import step of a
* configuration synchronisation by dispatching the import transformation event.
*/
class ImportStorageTransformer {
final class ImportStorageTransformer {
use StorageCopyTrait;
......@@ -122,8 +117,7 @@ public function transform(StorageInterface $storage) {
self::replaceStorageContents($storage, $mutable);
// Dispatch the event so that event listeners can alter the configuration.
// @todo: Use ConfigEvents::STORAGE_TRANSFORM_IMPORT in #2991683
$this->eventDispatcher->dispatch('config.transform.import', new StorageTransformEvent($mutable));
$this->eventDispatcher->dispatch(ConfigEvents::STORAGE_TRANSFORM_IMPORT, new StorageTransformEvent($mutable));
// Return the storage with the altered configuration.
return $mutable;
......
<?php
// @codingStandardsIgnoreStart
// @todo: Move this back to \Drupal\Core\Config in #2991683.
// Use this class with its class alias Drupal\Core\Config\ManagedStorage
// @codingStandardsIgnoreEnd
namespace Drupal\config_environment\Core\Config;
use Drupal\Core\Config\StorageInterface;
namespace Drupal\Core\Config;
/**
* The managed storage defers all the storage method calls to the manager.
......@@ -16,9 +10,10 @@
* the storage can be used so we can't do it in the constructor but we also
* don't know which method is called first.
*
* @internal
* This class is not meant to be extended and is final to make sure the
* assumptions that the storage is retrieved only once are upheld.
*/
class ManagedStorage implements StorageInterface {
final class ManagedStorage implements StorageInterface {
/**
* The decorated storage.
......
<?php
// @codingStandardsIgnoreStart
// @todo: Move this back to \Drupal\Core\Config in #2991683.
// Use this class with its class alias Drupal\Core\Config\StorageManagerInterface
// @codingStandardsIgnoreEnd
namespace Drupal\config_environment\Core\Config;
namespace Drupal\Core\Config;
/**
* Interface for a storage manager.
*
* @internal
*/
interface StorageManagerInterface {
......
<?php
// @codingStandardsIgnoreStart
// @todo: Move this back to \Drupal\Core\Config in #2991683.
// Use this class with its class alias Drupal\Core\Config\StorageTransformEvent
// @codingStandardsIgnoreEnd
namespace Drupal\config_environment\Core\Config;
namespace Drupal\Core\Config;
use Symfony\Component\EventDispatcher\Event;
// @todo: below removed when namespace is \Drupal\Core\Config in 2991683.
use Drupal\Core\Config\StorageInterface;
/**
* Class StorageTransformEvent.
......
<?php
namespace Drupal\config_environment\Core\Config;
namespace Drupal\Core\Config;
/**
* Thrown by config storage transformers if they cannot acquire a lock.
......
......@@ -4,6 +4,7 @@
use Drupal\Core\Archiver\ArchiveTar;
use Drupal\Core\Config\ConfigManagerInterface;
use Drupal\Core\Config\ImportStorageTransformer;
use Drupal\Core\Config\StorageInterface;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Diff\DiffFormatter;
......@@ -28,11 +29,18 @@ class ConfigController implements ContainerInjectionInterface {
protected $targetStorage;
/**
* The source storage.
* The sync storage.
*
* @var \Drupal\Core\Config\StorageInterface
*/
protected $sourceStorage;
protected $syncStorage;
/**
* The import transformer service.
*
* @var \Drupal\Core\Config\ImportStorageTransformer
*/
protected $importTransformer;
/**
* The configuration manager.
......@@ -80,7 +88,8 @@ public static function create(ContainerInterface $container) {
FileDownloadController::create($container),
$container->get('diff.formatter'),
$container->get('file_system'),
$container->get('config.storage.export')
$container->get('config.storage.export'),
$container->get('config.import_transformer')
);
}
......@@ -89,8 +98,8 @@ public static function create(ContainerInterface $container) {
*
* @param \Drupal\Core\Config\StorageInterface $target_storage
* The target storage.
* @param \Drupal\Core\Config\StorageInterface $source_storage
* The source storage.
* @param \Drupal\Core\Config\StorageInterface $sync_storage
* The sync storage.
* @param \Drupal\Core\Config\ConfigManagerInterface $config_manager
* The config manager.
* @param \Drupal\system\FileDownloadController $file_download_controller
......@@ -101,10 +110,12 @@ public static function create(ContainerInterface $container) {
* The file system.
* @param \Drupal\Core\Config\StorageInterface $export_storage
* The export storage.
* @param \Drupal\Core\Config\ImportStorageTransformer $import_transformer
* The import transformer service.
*/
public function __construct(StorageInterface $target_storage, StorageInterface $source_storage, ConfigManagerInterface $config_manager, FileDownloadController $file_download_controller, DiffFormatter $diff_formatter, FileSystemInterface $file_system, StorageInterface $export_storage = NULL) {
public function __construct(StorageInterface $target_storage, StorageInterface $sync_storage, ConfigManagerInterface $config_manager, FileDownloadController $file_download_controller, DiffFormatter $diff_formatter, FileSystemInterface $file_system, StorageInterface $export_storage = NULL, ImportStorageTransformer $import_transformer = NULL) {
$this->targetStorage = $target_storage;
$this->sourceStorage = $source_storage;
$this->syncStorage = $sync_storage;
$this->configManager = $config_manager;
$this->fileDownloadController = $file_download_controller;
$this->diffFormatter = $diff_formatter;
......@@ -114,6 +125,11 @@ public function __construct(StorageInterface $target_storage, StorageInterface $
$export_storage = \Drupal::service('config.storage.export');
}
$this->exportStorage = $export_storage;
if (is_null($import_transformer)) {
@trigger_error('The config.import_transformer service must be passed to ConfigController::__construct(), it is required before Drupal 9.0.0. See https://www.drupal.org/node/3066005.', E_USER_DEPRECATED);
$import_transformer = \Drupal::service('config.import_transformer');
}
$this->importTransformer = $import_transformer;
}
/**
......@@ -163,7 +179,8 @@ public function diff($source_name, $target_name = NULL, $collection = NULL) {
if (!isset($collection)) {
$collection = StorageInterface::DEFAULT_COLLECTION;
}
$diff = $this->configManager->diff($this->targetStorage, $this->sourceStorage, $source_name, $target_name, $collection);
$syncStorage = $this->importTransformer->transform($this->syncStorage);
$diff = $this->configManager->diff($this->targetStorage, $syncStorage, $source_name, $target_name, $collection);
$this->diffFormatter->show_header = FALSE;
$build = [];
......
......@@ -5,6 +5,7 @@
use Drupal\Core\Config\ConfigImporterException;
use Drupal\Core\Config\ConfigImporter;
use Drupal\Core\Config\Importer\ConfigImporterBatch;
use Drupal\Core\Config\ImportStorageTransformer;
use Drupal\Core\Config\TypedConfigManagerInterface;
use Drupal\Core\Extension\ModuleExtensionList;
use Drupal\Core\Extension\ModuleHandlerInterface;
......@@ -112,6 +113,13 @@ class ConfigSync extends FormBase {
*/
protected $moduleExtensionList;
/**
* The import transformer service.
*
* @var \Drupal\Core\Config\ImportStorageTransformer
*/
protected $importTransformer;
/**
* Constructs the object.
*
......@@ -139,8 +147,10 @@ class ConfigSync extends FormBase {
* The renderer.
* @param \Drupal\Core\Extension\ModuleExtensionList $extension_list_module
* The module extension list
* @param \Drupal\Core\Config\ImportStorageTransformer $import_transformer
* The import transformer service.
*/
public function __construct(StorageInterface $sync_storage, StorageInterface $active_storage, StorageInterface $snapshot_storage, LockBackendInterface $lock, EventDispatcherInterface $event_dispatcher, ConfigManagerInterface $config_manager, TypedConfigManagerInterface $typed_config, ModuleHandlerInterface $module_handler, ModuleInstallerInterface $module_installer, ThemeHandlerInterface $theme_handler, RendererInterface $renderer, ModuleExtensionList $extension_list_module) {
public function __construct(StorageInterface $sync_storage, StorageInterface $active_storage, StorageInterface $snapshot_storage, LockBackendInterface $lock, EventDispatcherInterface $event_dispatcher, ConfigManagerInterface $config_manager, TypedConfigManagerInterface $typed_config, ModuleHandlerInterface $module_handler, ModuleInstallerInterface $module_installer, ThemeHandlerInterface $theme_handler, RendererInterface $renderer, ModuleExtensionList $extension_list_module, ImportStorageTransformer $import_transformer = NULL) {
$this->syncStorage = $sync_storage;
$this->activeStorage = $active_storage;
$this->snapshotStorage = $snapshot_storage;
......@@ -153,6 +163,11 @@ public function __construct(StorageInterface $sync_storage, StorageInterface $ac
$this->themeHandler = $theme_handler;
$this->renderer = $renderer;
$this->moduleExtensionList = $extension_list_module;
if (is_null($import_transformer)) {
@trigger_error('The config.import_transformer service must be passed to ConfigSync::__construct(), it is required before Drupal 9.0.0. See https://www.drupal.org/node/3066005.', E_USER_DEPRECATED);
$import_transformer = \Drupal::service('config.import_transformer');
}
$this->importTransformer = $import_transformer;
}
/**
......@@ -171,7 +186,8 @@ public static function create(ContainerInterface $container) {
$container->get('module_installer'),
$container->get('theme_handler'),
$container->get('renderer'),
$container->get('extension.list.module')
$container->get('extension.list.module'),
$container->get('config.import_transformer')
);
}
......@@ -191,8 +207,9 @@ public function buildForm(array $form, FormStateInterface $form_state) {
'#type' => 'submit',
'#value' => $this->t('Import all'),
];
$source_list = $this->syncStorage->listAll();
$storage_comparer = new StorageComparer($this->syncStorage, $this->activeStorage);
$syncStorage = $this->importTransformer->transform($this->syncStorage);
$source_list = $syncStorage->listAll();
$storage_comparer = new StorageComparer($syncStorage, $this->activeStorage);
if (empty($source_list) || !$storage_comparer->createChangelist()->hasChanges()) {
$form['no_changes'] = [
'#type' => 'table',
......
# @todo: Move this test module under the config module in #2991683.
name: 'Configuration Storage Transformer Test'
type: module
package: Testing
......@@ -6,5 +5,3 @@ version: VERSION
core: 8.x
dependencies:
- drupal:config
# @todo: remove dependency on config_environment in #2991683.
- drupal:config_environment
......@@ -2,6 +2,7 @@
namespace Drupal\config_transformer_test;
use Drupal\Core\Config\ConfigEvents;
use Drupal\Core\Config\StorageInterface;
use Drupal\Core\Config\StorageTransformEvent;
use Drupal\Core\State\StateInterface;
......@@ -105,9 +106,8 @@ public function onExportTransform(StorageTransformEvent $event) {
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
// @todo: use class constants when they get added in #2991683
$events['config.transform.import'][] = ['onImportTransform'];
$events['config.transform.export'][] = ['onExportTransform'];
$events[ConfigEvents::STORAGE_TRANSFORM_IMPORT][] = ['onImportTransform'];
$events[ConfigEvents::STORAGE_TRANSFORM_EXPORT][] = ['onExportTransform'];
return $events;
}
......
<?php
// @codingStandardsIgnoreStart
// @todo: Move this back to \Drupal\Tests\config\Functional in #2991683.
// @codingStandardsIgnoreEnd
namespace Drupal\Tests\config_environment\Functional;
namespace Drupal\Tests\config\Functional;
use Drupal\Component\Utility\Html;
use Drupal\Tests\BrowserTestBase;
......@@ -21,7 +18,6 @@ class TransformedConfigExportImportUITest extends BrowserTestBase {
public static $modules = [
'config',
'config_transformer_test',
'config_environment',
];
/**
......
......@@ -5,16 +5,6 @@
* Allows site administrators to modify environment configuration.
*/
// Set class aliases for the classes that will go into core when we are in beta.
// See the experimental modules policy https://www.drupal.org/core/experimental
// @todo: remove class aliases in #2991683
@class_alias('Drupal\config_environment\Core\Config\StorageTransformEvent', 'Drupal\Core\Config\StorageTransformEvent');
@class_alias('Drupal\config_environment\Core\Config\ManagedStorage', 'Drupal\Core\Config\ManagedStorage');
@class_alias('Drupal\config_environment\Core\Config\StorageManagerInterface', 'Drupal\Core\Config\StorageManagerInterface');
@class_alias('Drupal\config_environment\Core\Config\ExportStorageManager', 'Drupal\Core\Config\ExportStorageManager');
@class_alias('Drupal\config_environment\Core\Config\ImportStorageTransformer', 'Drupal\Core\Config\ImportStorageTransformer');
@class_alias('Drupal\config_environment\Core\Config\StorageTransformerException', 'Drupal\Core\Config\StorageTransformerException');
use Drupal\Core\Routing\RouteMatchInterface;
/**
......
# @todo: Stop taking over config module routes in #2991683
config.sync:
path: '/admin/config/development/configuration'
defaults:
_form: '\Drupal\config_environment\Form\ConfigSync'
_title: 'Synchronize'
requirements:
_permission: 'synchronize configuration'
config.diff:
path: '/admin/config/development/configuration/sync/diff/{source_name}/{target_name}'
defaults:
_controller: '\Drupal\config_environment\Controller\ConfigController::diff'
target_name: NULL
requirements:
_permission: 'synchronize configuration'
config.diff_collection:
path: '/admin/config/development/configuration/sync/diff_collection/{collection}/{source_name}/{target_name}'
defaults:
_controller: '\Drupal\config_environment\Controller\ConfigController::diff'
target_name: NULL
requirements:
_permission: 'synchronize configuration'
config.export_download:
path: '/admin/config/development/configuration/full/export-download'
defaults:
_controller: '\Drupal\config_environment\Controller\ConfigController::downloadExport'
requirements:
_permission: 'export configuration'
services:
# @todo: Move this back to core services in #2991683
config.import_transformer:
class: Drupal\config_environment\Core\Config\ImportStorageTransformer
arguments: ['@event_dispatcher', '@database', '@lock', '@lock.persistent']
config.storage.export:
class: Drupal\config_environment\Core\Config\ManagedStorage
arguments: ['@config.storage.export.manager']
config.storage.export.manager:
class: Drupal\config_environment\Core\Config\ExportStorageManager
arguments: ['@config.storage', '@database', '@event_dispatcher', '@lock']
# config_environment services.
config_environment.excluded_modules.event_subscriber:
class: Drupal\config_environment\EventSubscriber\ExcludedModulesEventSubscriber
arguments: ['@config.storage', '@settings', '@config.manager']
......
<?php
namespace Drupal\config_environment\Controller;
use Drupal\config\Controller\ConfigController as OriginalConfigController;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Returns responses for config module routes.
*/
class ConfigController extends OriginalConfigController {
/**
* The import transformer service.
*
* @var \Drupal\Core\Config\ImportStorageTransformer
*/
protected $importTransformer;
/**
* The sync storage.
*
* @var \Drupal\Core\Config\StorageInterface
*/
protected $syncStorage;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
$controller = parent::create($container);
$controller->importTransformer = $container->get('config.import_transformer');
$controller->syncStorage = $container->get('config.storage.sync');
return $controller;
}
/**
* {@inheritdoc}
*/
public function diff($source_name, $target_name = NULL, $collection = NULL) {
$this->sourceStorage = $this->importTransformer->transform($this->syncStorage);
return parent::diff($source_name, $target_name, $collection);
}
}
<?php
// @codingStandardsIgnoreStart
// @todo: Move this back to \Drupal\Core\Config\ConfigEvents in #2991683.
// @codingStandardsIgnoreEnd
namespace Drupal\config_environment\Core\Config;
/**
* Defines events for the configuration transform system.
*
* The constants in this class will be moved back into ConfigEvents.
* But due to the fact that the config_environment is not in beta we save their
* definitions here and use the literal strings in the mean time.
*
* @internal
*
* @deprecated The class will be merged with Drupal\Core\Config\ConfigEvents.
*/
final class ConfigEvents {
/**
* Name of the event fired just before importing configuration.
*
* This event allows subscribers to modify the configuration which is about to
* be imported. The event listener method receives a
* \Drupal\Core\Config\StorageTransformEvent instance. This event contains a
* config storage which subscribers can interact with and which will finally
* be used to import the configuration from.
* Together with \Drupal\Core\Config\ConfigEvents::STORAGE_TRANSFORM_EXPORT
* subscribers can alter the active configuration in a config sync workflow
* instead of just overriding at runtime via the config-override system.
* This allows a complete customisation of the workflow including additional
* modules and editable configuration in different environments.
*
* @code
* $storage = $event->getStorage();
* @endcode
*
* This event is also fired when just viewing the difference of configuration
* to be imported independently of whether the import takes place or not.
* Use the \Drupal\Core\Config\ConfigEvents::IMPORT event to subscribe to the
* import having taken place.
*
* @Event