Skip to content
Snippets Groups Projects
Commit da3e6e61 authored by codebymikey's avatar codebymikey
Browse files

Issue #3136087 by codebymikey: Provide PRE- event hooks for import, revert and delete commands

parent 960e3cf0
No related branches found
No related tags found
1 merge request!9Issue #3136087 by codebymikey: Provide PRE- event hooks for import, revert and delete commands
......@@ -7,6 +7,14 @@ namespace Drupal\config_update;
*/
interface ConfigDeleteInterface {
/**
* Name of the event triggered before configuration delete.
*
* @see \Drupal\config_update\ConfigRevertEvent
* @see \Drupal\config_update\ConfigDeleteInterface::delete()
*/
const PRE_DELETE = 'config_update.pre_delete';
/**
* Name of the event triggered on configuration delete.
*
......@@ -18,7 +26,8 @@ interface ConfigDeleteInterface {
/**
* Deletes a configuration item.
*
* This action triggers a ConfigDeleteInterface::DELETE event.
* This action triggers the ConfigDeleteInterface::PRE_DELETE and
* ConfigDeleteInterface::DELETE events if the configuration could be deleted.
*
* @param string $type
* The type of configuration.
......@@ -30,6 +39,7 @@ interface ConfigDeleteInterface {
* not be found to delete. May also throw exceptions if there is a
* problem during deleting the configuration.
*
* @see \Drupal\config_update\ConfigDeleteInterface::PRE_DELETE
* @see \Drupal\config_update\ConfigDeleteInterface::DELETE
*/
public function delete($type, $name);
......
<?php
namespace Drupal\config_update;
use Drupal\Component\EventDispatcher\Event;
/**
* Event context class for configuration pre-revert/pre-import events.
*
* This class is passed in as the event when the
* \Drupal\config_update\ConfigRevertInterface::PRE_IMPORT and
* \Drupal\config_update\ConfigRevertInterface::PRE_REVERT events are triggered.
*/
class ConfigPreRevertEvent extends Event {
/**
* The type of configuration that is being imported or reverted.
*
* @var string
*/
protected $type;
/**
* The name of the config item being imported or reverted, without prefix.
*
* @var string
*/
protected $name;
/**
* The current configuration to be applied.
*
* @var array
*/
protected $value;
/**
* The active configuration.
*
* Only available on \Drupal\config_update\ConfigRevertInterface::PRE_REVERT.
*
* @var array|null
*/
protected $active;
/**
* Constructs a new ConfigPreRevertEvent.
*
* @param string $type
* The type of configuration being imported or reverted.
* @param string $name
* The name of the config item being imported/reverted, without prefix.
* @param array $value
* The current configuration.
* @param array|null $active
* The active configuration.
*/
public function __construct($type, $name, array $value, $active) {
$this->type = $type;
$this->name = $name;
$this->active = $active;
$this->value = $value;
}
/**
* Returns the type of configuration being imported or reverted.
*
* @return string
* The type of configuration, either 'system.simple' or a config entity
* type machine name.
*/
public function getType() {
return $this->type;
}
/**
* Returns the name of the config item, without prefix.
*
* @return string
* The name of the config item being imported/reverted/deleted, with the
* prefix.
*/
public function getName() {
return $this->name;
}
/**
* Returns the current configuration value to be applied.
*
* @return array
* The configuration value.
*/
public function getValue() {
return $this->value;
}
/**
* Set the configuration value to import or revert to after the event.
*
* This new value will be imported in place of the configuration value
* originally read from the filesystem.
*
* @param array $value
* The configuration value.
*/
public function setValue(array $value) {
$this->value = $value;
}
/**
* Returns the active configuration.
*
* Only available on \Drupal\config_update\ConfigRevertInterface::PRE_REVERT.
*
* @return array|null
* The active configuration.
*/
public function getActive() {
return $this->active;
}
}
......@@ -9,6 +9,7 @@ use Drupal\Component\EventDispatcher\Event;
*
* This class is passed in as the event when the
* \Drupal\config_update\ConfigRevertInterface::IMPORT,
* \Drupal\config_update\ConfigDeleteInterface::PRE_DELETE,
* \Drupal\config_update\ConfigDeleteInterface::DELETE, and
* \Drupal\config_update\ConfigRevertInterface::REVERT events are triggered.
*/
......
......@@ -7,6 +7,14 @@ namespace Drupal\config_update;
*/
interface ConfigRevertInterface {
/**
* Name of the event triggered before configuration import.
*
* @see \Drupal\config_update\ConfigPreRevertEvent
* @see \Drupal\config_update\ConfigRevertInterface::import()
*/
const PRE_IMPORT = 'config_update.pre_import';
/**
* Name of the event triggered on configuration import.
*
......@@ -15,6 +23,14 @@ interface ConfigRevertInterface {
*/
const IMPORT = 'config_update.import';
/**
* Name of the event triggered before configuration revert.
*
* @see \Drupal\config_update\ConfigPreRevertEvent
* @see \Drupal\config_update\ConfigRevertInterface::revert()
*/
const PRE_REVERT = 'config_update.pre_revert';
/**
* Name of the event triggered on configuration revert.
*
......@@ -26,8 +42,9 @@ interface ConfigRevertInterface {
/**
* Imports configuration from extension storage to active storage.
*
* This action triggers a ConfigRevertInterface::IMPORT event if the
* configuration could be imported.
* This action triggers the ConfigRevertInterface::PRE_IMPORT and
* ConfigRevertInterface::IMPORT events if the configuration could be
* imported.
*
* @param string $type
* The type of configuration.
......@@ -39,6 +56,7 @@ interface ConfigRevertInterface {
* be found to import. May also throw exceptions if there is a problem
* during saving the configuration.
*
* @see \Drupal\config_update\ConfigRevertInterface::PRE_IMPORT
* @see \Drupal\config_update\ConfigRevertInterface::IMPORT
*/
public function import($type, $name);
......@@ -46,7 +64,9 @@ interface ConfigRevertInterface {
/**
* Reverts configuration to the value from extension storage.
*
* This action triggers a ConfigRevertInterface::REVERT event.
* This action triggers the ConfigRevertInterface::PRE_REVERT and
* ConfigRevertInterface::REVERT events if the configuration could be
* reverted.
*
* @param string $type
* The type of configuration.
......@@ -58,6 +78,7 @@ interface ConfigRevertInterface {
* not be found to revert to. May also throw exceptions if there is a
* problem during saving the configuration.
*
* @see \Drupal\config_update\ConfigRevertInterface::PRE_REVERT
* @see \Drupal\config_update\ConfigRevertInterface::REVERT
*/
public function revert($type, $name);
......
......@@ -101,8 +101,13 @@ class ConfigReverter implements ConfigRevertInterface, ConfigDeleteInterface {
return FALSE;
}
// Trigger an event to modify the configuration value.
$event = new ConfigPreRevertEvent($type, $name, $value, NULL);
$this->dispatcher->dispatch($event, ConfigRevertInterface::PRE_IMPORT);
$value = $event->getValue();
// Save it as a new config entity or simple config.
if ($type == 'system.simple') {
if ($type === 'system.simple') {
$this->configFactory->getEditable($full_name)->setData($value)->save();
}
else {
......@@ -137,13 +142,19 @@ class ConfigReverter implements ConfigRevertInterface, ConfigDeleteInterface {
}
// Make sure the configuration exists currently in active storage.
if (!$this->activeConfigStorage->read($full_name)) {
$active_value = $this->activeConfigStorage->read($full_name);
if (!$active_value) {
return FALSE;
}
// Trigger an event to modify the active configuration value.
$event = new ConfigPreRevertEvent($type, $name, $value, $active_value);
$this->dispatcher->dispatch($event, ConfigRevertInterface::PRE_REVERT);
$value = $event->getValue();
// Load the current config and replace the value, retaining the config
// hash (which is part of the _core config key's value).
if ($type == 'system.simple') {
if ($type === 'system.simple') {
$config = $this->configFactory->getEditable($full_name);
$core = $config->get('_core');
$config
......@@ -187,6 +198,10 @@ class ConfigReverter implements ConfigRevertInterface, ConfigDeleteInterface {
return FALSE;
}
// Trigger an event notifying of this change.
$event = new ConfigRevertEvent($type, $name);
$this->dispatcher->dispatch($event, ConfigDeleteInterface::PRE_DELETE);
if ($type === 'system.simple') {
$config->delete();
}
......@@ -246,7 +261,7 @@ class ConfigReverter implements ConfigRevertInterface, ConfigDeleteInterface {
* The config item's full name, or FALSE if there is an error.
*/
protected function getFullName($type, $name) {
if ($type == 'system.simple' || !$type) {
if ($type === 'system.simple' || !$type) {
return $name;
}
......
......@@ -115,18 +115,19 @@ class ConfigReverterTest extends ConfigUpdateUnitTestBase {
// Call the importer and test the Boolean result.
$result = $this->configReverter->import($type, $name);
$this->assertEquals($result, $expected);
$this->assertEquals($expected, $result);
if ($result) {
// Verify that the config is correct after import, and logging worked.
$this->assertEquals($this->configStorage[$config_name], $config_after);
$this->assertEquals(count($this->dispatchedEvents), 1);
$this->assertEquals($this->dispatchedEvents[0][0], ConfigRevertInterface::IMPORT);
$this->assertEquals($config_after, $this->configStorage[$config_name]);
$this->assertCount(2, $this->dispatchedEvents);
$this->assertEquals(ConfigRevertInterface::PRE_IMPORT, $this->dispatchedEvents[0][0]);
$this->assertEquals(ConfigRevertInterface::IMPORT, $this->dispatchedEvents[1][0]);
}
else {
// Verify that the config didn't change and no events were logged.
$this->assertEquals($this->configStorage, $save_config);
$this->assertEquals(count($this->dispatchedEvents), 0);
$this->assertEquals($save_config, $this->configStorage);
$this->assertCount(0, $this->dispatchedEvents);
}
}
......@@ -174,6 +175,28 @@ class ConfigReverterTest extends ConfigUpdateUnitTestBase {
['in.optional' => 'before'],
['in.optional' => 'optional', '_core' => 'core_for_in.optional'],
],
// Will be altered if the extension config exists.
[
'system.simple',
'in.extension.pre_import',
'in.extension.pre_import',
TRUE,
['prop' => 'unaltered_value'],
[
'prop' => 'altered_value',
'new_prop' => 'new_value',
'_core' => 'core_for_in.extension.pre_import',
],
],
// Will not be altered if the extension config doesn't exist.
[
'system.simple',
'missing2,pre_import',
'missing2.pre_import',
FALSE,
FALSE,
FALSE,
],
[
'unknown',
'in.extension',
......@@ -207,18 +230,19 @@ class ConfigReverterTest extends ConfigUpdateUnitTestBase {
// Call the reverter and test the Boolean result.
$result = $this->configReverter->revert($type, $name);
$this->assertEquals($result, $expected);
$this->assertEquals($expected, $result);
if ($result) {
// Verify that the config is correct after revert, and logging worked.
$this->assertEquals($this->configStorage[$config_name], $config_after);
$this->assertEquals(count($this->dispatchedEvents), 1);
$this->assertEquals($this->dispatchedEvents[0][0], ConfigRevertInterface::REVERT);
$this->assertEquals($config_after, $this->configStorage[$config_name]);
$this->assertCount(2, $this->dispatchedEvents);
$this->assertEquals(ConfigRevertInterface::PRE_REVERT, $this->dispatchedEvents[0][0]);
$this->assertEquals(ConfigRevertInterface::REVERT, $this->dispatchedEvents[1][0]);
}
else {
// Verify that the config didn't change and no events were logged.
$this->assertEquals($this->configStorage, $save_config);
$this->assertEquals(count($this->dispatchedEvents), 0);
$this->assertEquals($save_config, $this->configStorage);
$this->assertCount(0, $this->dispatchedEvents);
}
}
......@@ -230,6 +254,34 @@ class ConfigReverterTest extends ConfigUpdateUnitTestBase {
// Elements: type, name, config name, return value,
// config to set up before, config expected after. See also
// getFromExtensionProvider().
// The active config's 'prop' property will not be reverted.
[
'system.simple',
'in.extension.pre_revert',
'in.extension.pre_revert',
TRUE,
['prop' => 'unaltered_value'],
[
'prop' => 'active.pre_revert_value',
'new_prop' => 'new_value',
'_core' => 'core_for_in.extension.pre_revert',
],
],
// The active config's 'prop' property will not be reverted.
[
'foo',
'pre_revert',
'foo.bar.pre_revert',
TRUE,
['foo.bar.pre_revert' => 'active', 'id' => 'one'],
[
'foo.bar.pre_revert' => 'extension',
'id' => 'pre_revert',
'prop' => 'active.pre_revert_value',
'new_prop' => 'new_value',
'_core' => 'core_for_foo.bar.pre_revert',
],
],
[
'system.simple',
'in.extension',
......@@ -309,18 +361,19 @@ class ConfigReverterTest extends ConfigUpdateUnitTestBase {
// Call the deleter and test the Boolean result.
$result = $this->configReverter->delete($type, $name);
$this->assertEquals($result, $expected);
$this->assertEquals($expected, $result);
if ($result) {
// Verify that the config is missing after delete, and logging worked.
$this->assertTrue(!isset($this->configStorage[$config_name]));
$this->assertEquals(count($this->dispatchedEvents), 1);
$this->assertEquals($this->dispatchedEvents[0][0], ConfigDeleteInterface::DELETE);
$this->assertNotTrue(isset($this->configStorage[$config_name]));
$this->assertCount(2, $this->dispatchedEvents);
$this->assertEquals(ConfigDeleteInterface::PRE_DELETE, $this->dispatchedEvents[0][0]);
$this->assertEquals(ConfigDeleteInterface::DELETE, $this->dispatchedEvents[1][0]);
}
else {
// Verify that the config didn't change and no events were logged.
$this->assertEquals($this->configStorage, $save_config);
$this->assertEquals(count($this->dispatchedEvents), 0);
$this->assertEquals($save_config, $this->configStorage);
$this->assertCount(0, $this->dispatchedEvents);
}
}
......
......@@ -2,6 +2,7 @@
namespace Drupal\Tests\config_update\Unit;
use Drupal\config_update\ConfigPreRevertEvent;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Tests\UnitTestCase;
use Drupal\Component\EventDispatcher\Event;
......@@ -203,6 +204,10 @@ abstract class ConfigUpdateUnitTestBase extends UnitTestCase {
],
['in.both', ['in.both' => 'active']],
['in.optional', ['in.optional' => 'active']],
['in.extension.pre_revert',
['prop' => 'active.pre_revert_value', '_core' => 'core_for_in.extension'],
],
['foo.bar.pre_revert', ['foo.bar.pre_revert' => 'active', 'id' => 'pre_revert', 'prop' => 'active.pre_revert_value', ]],
];
$storage
->method('read')
......@@ -232,7 +237,11 @@ abstract class ConfigUpdateUnitTestBase extends UnitTestCase {
['in.optional', FALSE],
['foo.bar.one', ['foo.bar.one' => 'extension', 'id' => 'one']],
['another', ['another' => 'extension', 'id' => 'one']],
['in.extension.pre_import', ['prop' => 'extension.pre_import_value']],
['in.extension.pre_revert', ['prop' => 'extension.pre_revert_value']],
['foo.bar.pre_revert', ['foo.bar.pre_revert' => 'extension', 'id' => 'pre_revert', 'prop' => 'extension.pre_revert_value']],
['missing2', FALSE],
['missing2.pre_import', FALSE],
];
$storage
->method('read')
......@@ -341,13 +350,58 @@ abstract class ConfigUpdateUnitTestBase extends UnitTestCase {
/**
* Mocks event dispatch.
*
* For \Symfony\Component\EventDispatcher\EventDispatchInterface::dispatch().
* @see \Symfony\Component\EventDispatcher\EventDispatcherInterface::dispatch()
*/
public function mockDispatch(Event $event, $name = NULL) {
$this->dispatchedEvents[] = [$name, $event];
if ($event instanceof ConfigPreRevertEvent) {
$this->handlePreRevertDispatch($event);
}
return $event;
}
/**
* Handle the pre-revert events.
*
* @param \Drupal\config_update\ConfigPreRevertEvent $event
* The dispatched event.
*/
protected function handlePreRevertDispatch(ConfigPreRevertEvent $event) {
$name = $event->getName();
// Only modify configurations with the pre_import or pre_revert names.
$import = strpos($name, 'pre_import') !== FALSE;
$revert = strpos($name, 'pre_revert') !== FALSE;
if (!$import && !$revert) {
return;
}
$active = $event->getActive();
$value = $event->getValue();
// Always add the new property.
$value['new_prop'] = 'new_value';
// Alter the original property.
if (isset($value['prop'])) {
if ($active) {
// A revert operation is being done, read properties from it.
if (isset($active['prop'])) {
// Don't override the original property.
$value['prop'] = $active['prop'];
}
else {
$value['prop'] = 'altered_active_value';
}
}
else {
$value['prop'] = 'altered_value';
}
}
// Store the modified value.
$event->setValue($value);
}
/**
* Mock config storage for the mock config factory.
*
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment