Commit b55ed6bd authored by catch's avatar catch

Issue #2666392 by alexpott, webflo, xaqrox, Boobaa, dawehner, Berdir: Unable...

Issue #2666392 by alexpott, webflo, xaqrox, Boobaa, dawehner, Berdir: Unable to revert third party settings via config import
parent 099f4146
......@@ -4,7 +4,6 @@
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Config\Schema\SchemaIncompleteException;
use Drupal\Core\Entity\Entity;
use Drupal\Core\Config\ConfigDuplicateUUIDException;
use Drupal\Core\Entity\EntityStorageInterface;
......@@ -267,18 +266,8 @@ public function toArray() {
/** @var \Drupal\Core\Config\Entity\ConfigEntityTypeInterface $entity_type */
$entity_type = $this->getEntityType();
$properties_to_export = $entity_type->getPropertiesToExport();
if (empty($properties_to_export)) {
$config_name = $entity_type->getConfigPrefix() . '.' . $this->id();
$definition = $this->getTypedConfig()->getDefinition($config_name);
if (!isset($definition['mapping'])) {
throw new SchemaIncompleteException("Incomplete or missing schema for $config_name");
}
$properties_to_export = array_combine(array_keys($definition['mapping']), array_keys($definition['mapping']));
}
$id_key = $entity_type->getKey('id');
foreach ($properties_to_export as $property_name => $export_name) {
foreach ($entity_type->getPropertiesToExport($this->id()) as $property_name => $export_name) {
// Special handling for IDs so that computed compound IDs work.
// @see \Drupal\Core\Entity\EntityDisplayBase::id()
if ($property_name == $id_key) {
......
......@@ -450,9 +450,19 @@ public function updateFromStorageRecord(ConfigEntityInterface $entity, array $va
$data = $this->mapFromStorageRecords([$values]);
$updated_entity = current($data);
foreach (array_keys($values) as $property) {
$value = $updated_entity->get($property);
$entity->set($property, $value);
/** @var \Drupal\Core\Config\Entity\ConfigEntityTypeInterface $entity_type */
$entity_type = $this->getEntityType();
$id_key = $entity_type->getKey('id');
foreach ($entity_type->getPropertiesToExport($updated_entity->get($id_key)) as $property) {
if ($property === $this->uuidKey) {
// During an update the UUID field should not be copied. Under regular
// circumstances the values will be equal. If configuration is written
// twice during configuration install the updated entity will not have a
// UUID.
// @see \Drupal\Core\Config\ConfigInstaller::createConfiguration()
continue;
}
$entity->set($property, $updated_entity->get($property));
}
return $entity;
......
......@@ -3,6 +3,7 @@
namespace Drupal\Core\Config\Entity;
use Drupal\Core\Config\Entity\Exception\ConfigEntityStorageClassException;
use Drupal\Core\Config\Schema\SchemaIncompleteException;
use Drupal\Core\Entity\EntityType;
use Drupal\Core\Config\ConfigPrefixLengthException;
......@@ -142,30 +143,40 @@ protected function checkStorageClass($class) {
/**
* {@inheritdoc}
*/
public function getPropertiesToExport() {
public function getPropertiesToExport($id = NULL) {
if (!empty($this->mergedConfigExport)) {
return $this->mergedConfigExport;
}
if (!empty($this->config_export)) {
if (empty($this->mergedConfigExport)) {
// Always add default properties to be exported.
$this->mergedConfigExport = [
'uuid' => 'uuid',
'langcode' => 'langcode',
'status' => 'status',
'dependencies' => 'dependencies',
'third_party_settings' => 'third_party_settings',
'_core' => '_core',
];
foreach ($this->config_export as $property => $name) {
if (is_numeric($property)) {
$this->mergedConfigExport[$name] = $name;
}
else {
$this->mergedConfigExport[$property] = $name;
}
// Always add default properties to be exported.
$this->mergedConfigExport = [
'uuid' => 'uuid',
'langcode' => 'langcode',
'status' => 'status',
'dependencies' => 'dependencies',
'third_party_settings' => 'third_party_settings',
'_core' => '_core',
];
foreach ($this->config_export as $property => $name) {
if (is_numeric($property)) {
$this->mergedConfigExport[$name] = $name;
}
else {
$this->mergedConfigExport[$property] = $name;
}
}
return $this->mergedConfigExport;
}
return NULL;
else {
// @todo https://www.drupal.org/project/drupal/issues/2949021 Deprecate
// fallback to schema.
$config_name = $this->getConfigPrefix() . '.' . $id;
$definition = \Drupal::service('config.typed')->getDefinition($config_name);
if (!isset($definition['mapping'])) {
throw new SchemaIncompleteException("Incomplete or missing schema for $config_name");
}
$this->mergedConfigExport = array_combine(array_keys($definition['mapping']), array_keys($definition['mapping']));
}
return $this->mergedConfigExport;
}
/**
......
......@@ -65,11 +65,22 @@ public function getConfigPrefix();
/**
* Gets the config entity properties to export if declared on the annotation.
*
* Falls back to determining the properties using configuration schema, if the
* config entity properties are not declared.
*
* param string $id
* The ID of the configuration entity. Used when checking schema instead of
* the annotation.
*
* @return array|null
* The properties to export or NULL if they can not be determine from the
* config entity type annotation.
*
* @throws \Drupal\Core\Config\Schema\SchemaIncompleteException
* Thrown when the configuration entity type does not have the annotation or
* a configuration schema.
*/
public function getPropertiesToExport();
public function getPropertiesToExport($id = NULL);
/**
* Gets the keys that are available for fast lookup.
......
......@@ -158,6 +158,14 @@ config_test.foo:
config_test.bar:
type: config_test.foo
system.action.*.third_party.config_test:
type: mapping
label: 'Third party setting for action entity'
mapping:
integer:
type: integer
label: 'Integer'
config_test.validation:
type: config_object
label: 'Configuration type'
......
......@@ -71,13 +71,6 @@ class FieldConfigEntityUnitTest extends UnitTestCase {
*/
protected $fieldStorage;
/**
* The typed configuration manager used for testing.
*
* @var \Drupal\Core\Config\TypedConfigManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $typedConfigManager;
/**
* The mock field type plugin manager;
*
......@@ -98,8 +91,6 @@ protected function setUp() {
$this->uuid = $this->getMock('\Drupal\Component\Uuid\UuidInterface');
$this->typedConfigManager = $this->getMock('Drupal\Core\Config\TypedConfigManagerInterface');
$this->fieldTypePluginManager = $this->getMock('Drupal\Core\Field\FieldTypePluginManagerInterface');
$container = new ContainerBuilder();
......@@ -107,7 +98,6 @@ protected function setUp() {
$container->set('entity_field.manager', $this->entityFieldManager);
$container->set('entity_type.manager', $this->entityTypeManager);
$container->set('uuid', $this->uuid);
$container->set('config.typed', $this->typedConfigManager);
$container->set('plugin.manager.field.field_type', $this->fieldTypePluginManager);
// Inject the container into entity.manager so it can defer to
// entity_type.manager, etc.
......@@ -299,9 +289,10 @@ public function testToArray() {
->method('getKey')
->with('id')
->will($this->returnValue('id'));
$this->typedConfigManager->expects($this->once())
->method('getDefinition')
->will($this->returnValue(['mapping' => array_fill_keys(array_keys($expected), '')]));
$this->entityType->expects($this->once())
->method('getPropertiesToExport')
->with('test_entity_type.test_bundle.field_test')
->will($this->returnValue(array_combine(array_keys($expected), array_keys($expected))));
$export = $field->toArray();
$this->assertEquals($expected, $export);
......
......@@ -21,7 +21,7 @@ class ConfigEntityImportTest extends BrowserTestBase {
*
* @var array
*/
public static $modules = ['action', 'block', 'filter', 'image', 'search', 'search_extra_type'];
public static $modules = ['action', 'block', 'filter', 'image', 'search', 'search_extra_type', 'config_test'];
/**
* {@inheritdoc}
......@@ -41,6 +41,7 @@ public function testConfigUpdateImport() {
$this->doFilterFormatUpdate();
$this->doImageStyleUpdate();
$this->doSearchPageUpdate();
$this->doThirdPartySettingsUpdate();
}
/**
......@@ -172,6 +173,34 @@ protected function doSearchPageUpdate() {
$this->assertConfigUpdateImport($name, $original_data, $custom_data);
}
/**
* Tests updating of third party settings.
*/
protected function doThirdPartySettingsUpdate() {
// Create a test action with a known label.
$name = 'system.action.third_party_settings_test';
/** @var \Drupal\config_test\Entity\ConfigTest $entity */
$entity = Action::create([
'id' => 'third_party_settings_test',
'plugin' => 'action_message_action',
]);
$entity->save();
$this->assertIdentical([], $entity->getThirdPartyProviders());
// Get a copy of the configuration before the third party setting is added.
$no_third_part_setting_config = $this->container->get('config.storage')->read($name);
// Add a third party setting.
$entity->setThirdPartySetting('config_test', 'integer', 1);
$entity->save();
$this->assertIdentical(1, $entity->getThirdPartySetting('config_test', 'integer'));
$has_third_part_setting_config = $this->container->get('config.storage')->read($name);
// Ensure configuration imports can completely remove third party settings.
$this->assertConfigUpdateImport($name, $has_third_part_setting_config, $no_third_part_setting_config);
}
/**
* Tests that a single set of plugin config stays in sync.
*
......
......@@ -8,7 +8,6 @@
namespace Drupal\Tests\Core\Config\Entity;
use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Core\Config\Schema\SchemaIncompleteException;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Language\Language;
......@@ -552,32 +551,6 @@ public function testToArrayIdKey() {
$this->assertEquals(['configId' => $entity->id(), 'dependencies' => []], $properties);
}
/**
* @covers ::toArray
*/
public function testToArraySchemaFallback() {
$this->typedConfigManager->expects($this->once())
->method('getDefinition')
->will($this->returnValue(['mapping' => ['id' => '', 'dependencies' => '']]));
$this->entityType->expects($this->any())
->method('getPropertiesToExport')
->willReturn([]);
$properties = $this->entity->toArray();
$this->assertInternalType('array', $properties);
$this->assertEquals(['id' => $this->entity->id(), 'dependencies' => []], $properties);
}
/**
* @covers ::toArray
*/
public function testToArrayFallback() {
$this->entityType->expects($this->any())
->method('getPropertiesToExport')
->willReturn([]);
$this->setExpectedException(SchemaIncompleteException::class);
$this->entity->toArray();
}
/**
* @covers ::getThirdPartySetting
* @covers ::setThirdPartySetting
......
......@@ -2,6 +2,9 @@
namespace Drupal\Tests\Core\Config\Entity;
use Drupal\Core\Config\Schema\SchemaIncompleteException;
use Drupal\Core\Config\TypedConfigManagerInterface;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Tests\UnitTestCase;
use Drupal\Core\Config\Entity\ConfigEntityType;
use Drupal\Core\Config\Entity\Exception\ConfigEntityStorageClassException;
......@@ -12,6 +15,23 @@
*/
class ConfigEntityTypeTest extends UnitTestCase {
/**
* The mocked typed config manager.
*
* @var \Drupal\Core\Config\TypedConfigManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $typedConfigManager;
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->typedConfigManager = $this->getMock(TypedConfigManagerInterface::class);
$container = new ContainerBuilder();
$container->set('config.typed', $this->typedConfigManager);
\Drupal::setContainer($container);
}
/**
* Sets up a ConfigEntityType object for a given set of values.
*
......@@ -137,11 +157,6 @@ public function testGetPropertiesToExport($definition, $expected) {
public function providerGetPropertiesToExport() {
$data = [];
$data[] = [
[],
NULL,
];
$data[] = [
[
'config_export' => [
......@@ -177,4 +192,28 @@ public function providerGetPropertiesToExport() {
return $data;
}
/**
* @covers ::getPropertiesToExport
*/
public function testGetPropertiesToExportSchemaFallback() {
$this->typedConfigManager->expects($this->once())
->method('getDefinition')
->will($this->returnValue(['mapping' => ['id' => '', 'dependencies' => '']]));
$config_entity_type = new ConfigEntityType([
'id' => 'example_config_entity_type',
]);
$this->assertEquals(['id' => 'id', 'dependencies' => 'dependencies'], $config_entity_type->getPropertiesToExport('test'));
}
/**
* @covers ::getPropertiesToExport
*/
public function testGetPropertiesToExportException() {
$config_entity_type = new ConfigEntityType([
'id' => 'example_config_entity_type',
]);
$this->setExpectedException(SchemaIncompleteException::class);
$config_entity_type->getPropertiesToExport();
}
}
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