Skip to content
Snippets Groups Projects
Verified Commit 28cf67bd authored by Alex Pott's avatar Alex Pott
Browse files

Issue #3441557 by phenaproxima, thejimbirch: Recipes with default content...

Issue #3441557 by phenaproxima, thejimbirch: Recipes with default content cannot be applied twice due to UUID duplication

(cherry picked from commit fa163524cd403110d91f7d14ba44b0a6bd4f9eae)
parent 15b03f98
No related branches found
No related tags found
1 merge request!7908Recipes API on 10.3.x
<?php
namespace Drupal\Core\DefaultContent;
/**
* Defines what to do if importing an entity that already exists (by UUID).
*
* @internal
* This API is experimental.
*/
enum Existing {
case Error;
case Skip;
}
...@@ -54,8 +54,19 @@ public function __construct( ...@@ -54,8 +54,19 @@ public function __construct(
* @param \Drupal\Core\DefaultContent\Finder $content * @param \Drupal\Core\DefaultContent\Finder $content
* The content finder, which has information on the entities to create * The content finder, which has information on the entities to create
* in the necessary dependency order. * in the necessary dependency order.
* @param \Drupal\Core\DefaultContent\Existing $existing
* (optional) What to do if one of the entities being imported already
* exists, by UUID:
* - \Drupal\Core\DefaultContent\Existing::Error: Throw an exception.
* - \Drupal\Core\DefaultContent\Existing::Skip: Leave the existing entity
* as-is.
*
* @throws \Drupal\Core\DefaultContent\ImportException
* - If any of the entities being imported are not content entities.
* - If any of the entities being imported already exists, by UUID, and
* $existing is \Drupal\Core\DefaultContent\Existing::Error.
*/ */
public function importContent(Finder $content): void { public function importContent(Finder $content, Existing $existing = Existing::Error): void {
if (count($content->data) === 0) { if (count($content->data) === 0) {
return; return;
} }
...@@ -77,6 +88,16 @@ public function importContent(Finder $content): void { ...@@ -77,6 +88,16 @@ public function importContent(Finder $content): void {
throw new ImportException("Content entity $uuid is a '$entity_type_id', which is not a content entity type."); throw new ImportException("Content entity $uuid is a '$entity_type_id', which is not a content entity type.");
} }
$entity = $this->entityRepository->loadEntityByUuid($entity_type_id, $uuid);
if ($entity) {
if ($existing === Existing::Skip) {
continue;
}
else {
throw new ImportException("$entity_type_id $uuid already exists.");
}
}
$entity = $this->toEntity($decoded)->enforceIsNew(); $entity = $this->toEntity($decoded)->enforceIsNew();
// Ensure that the entity is not owned by the anonymous user. // Ensure that the entity is not owned by the anonymous user.
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
use Drupal\Core\Config\FileStorage; use Drupal\Core\Config\FileStorage;
use Drupal\Core\Config\InstallStorage; use Drupal\Core\Config\InstallStorage;
use Drupal\Core\Config\StorageInterface; use Drupal\Core\Config\StorageInterface;
use Drupal\Core\DefaultContent\Existing;
use Drupal\Core\DefaultContent\Importer; use Drupal\Core\DefaultContent\Importer;
use Drupal\Core\DefaultContent\Finder; use Drupal\Core\DefaultContent\Finder;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
...@@ -129,7 +130,7 @@ protected static function processContent(Finder $content): void { ...@@ -129,7 +130,7 @@ protected static function processContent(Finder $content): void {
/** @var \Drupal\Core\DefaultContent\Importer $importer */ /** @var \Drupal\Core\DefaultContent\Importer $importer */
$importer = \Drupal::service(Importer::class); $importer = \Drupal::service(Importer::class);
$importer->setLogger(\Drupal::logger('recipe')); $importer->setLogger(\Drupal::logger('recipe'));
$importer->importContent($content); $importer->importContent($content, Existing::Skip);
} }
} }
...@@ -7,8 +7,10 @@ ...@@ -7,8 +7,10 @@
use ColinODell\PsrTestLogger\TestLogger; use ColinODell\PsrTestLogger\TestLogger;
use Drupal\block_content\BlockContentInterface; use Drupal\block_content\BlockContentInterface;
use Drupal\block_content\Entity\BlockContentType; use Drupal\block_content\Entity\BlockContentType;
use Drupal\Core\DefaultContent\Existing;
use Drupal\Core\DefaultContent\Finder; use Drupal\Core\DefaultContent\Finder;
use Drupal\Core\DefaultContent\Importer; use Drupal\Core\DefaultContent\Importer;
use Drupal\Core\DefaultContent\ImportException;
use Drupal\Core\Entity\EntityRepositoryInterface; use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Url; use Drupal\Core\Url;
use Drupal\field\Entity\FieldConfig; use Drupal\field\Entity\FieldConfig;
...@@ -102,6 +104,31 @@ protected function setUp(): void { ...@@ -102,6 +104,31 @@ protected function setUp(): void {
$this->contentDir = $this->getDrupalRoot() . '/core/tests/fixtures/default_content'; $this->contentDir = $this->getDrupalRoot() . '/core/tests/fixtures/default_content';
} }
/**
* @return array<array<mixed>>
*/
public function providerImportEntityThatAlreadyExists(): array {
return [
[Existing::Error],
[Existing::Skip],
];
}
/**
* @dataProvider providerImportEntityThatAlreadyExists
*/
public function testImportEntityThatAlreadyExists(Existing $existing): void {
$this->drupalCreateUser(values: ['uuid' => '94503467-be7f-406c-9795-fc25baa22203']);
if ($existing === Existing::Error) {
$this->expectException(ImportException::class);
$this->expectExceptionMessage('user 94503467-be7f-406c-9795-fc25baa22203 already exists.');
}
$this->container->get(Importer::class)
->importContent(new Finder($this->contentDir), $existing);
}
/** /**
* Tests importing content directly, via the API. * Tests importing content directly, via the API.
*/ */
...@@ -146,9 +173,7 @@ private function assertContentWasImported(): void { ...@@ -146,9 +173,7 @@ private function assertContentWasImported(): void {
// The tag carries a field with serialized data, so ensure it came through // The tag carries a field with serialized data, so ensure it came through
// properly. // properly.
$this->assertSame('a:2:{i:0;s:2:"Hi";i:1;s:6:"there!";}', $tag->field_serialized_stuff->value); $this->assertSame('a:2:{i:0;s:2:"Hi";i:1;s:6:"there!";}', $tag->field_serialized_stuff->value);
$owner = $node->getOwner(); $this->assertSame('94503467-be7f-406c-9795-fc25baa22203', $node->getOwner()->uuid());
$this->assertSame('Naomi Malone', $owner->getAccountName());
$this->assertSame('94503467-be7f-406c-9795-fc25baa22203', $owner->uuid());
// The node's URL should use the path alias shipped with the recipe. // The node's URL should use the path alias shipped with the recipe.
$node_url = $node->toUrl()->toString(); $node_url = $node->toUrl()->toString();
$this->assertSame(Url::fromUserInput('/test-article')->toString(), $node_url); $this->assertSame(Url::fromUserInput('/test-article')->toString(), $node_url);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment