Loading core/lib/Drupal/Core/Recipe/RecipeRunner.php +13 −1 Original line number Diff line number Diff line Loading @@ -4,6 +4,8 @@ namespace Drupal\Core\Recipe; use Drupal\Core\Config\Action\ConfigActionException; use Drupal\Core\Config\ConfigManagerInterface; use Drupal\Core\Config\FileStorage; use Drupal\Core\Config\InstallStorage; use Drupal\Core\Config\StorageInterface; Loading Loading @@ -93,11 +95,14 @@ protected static function processInstall(InstallConfigurator $install, StorageIn * The recipe being applied. */ protected static function processConfiguration(Recipe $recipe): void { /** @var \Drupal\Core\Config\ConfigManagerInterface $config_manager */ $config_manager = \Drupal::service(ConfigManagerInterface::class); $config_installer = new RecipeConfigInstaller( \Drupal::service('config.factory'), \Drupal::service('config.storage'), \Drupal::service('config.typed'), \Drupal::service('config.manager'), $config_manager, \Drupal::service('event_dispatcher'), NULL, \Drupal::service('extension.path.resolver')); Loading @@ -118,6 +123,13 @@ protected static function processConfiguration(Recipe $recipe): void { /** @var \Drupal\Core\Config\Action\ConfigActionManager $config_action_manager */ $config_action_manager = \Drupal::service('plugin.manager.config_action'); foreach ($config->config['actions'] as $config_name => $actions) { // If this config name contains an input value, it must begin with the // config prefix of a known entity type. if (str_contains($config_name, '${') && empty($config_manager->getEntityTypeIdByName($config_name))) { throw new ConfigActionException("The entity type for the config name '$config_name' could not be identified."); } $config_name = str_replace($keys, $replace, $config_name); foreach ($actions as $action_id => $data) { $config_action_manager->applyAction($action_id, $config_name, static::replaceInputValues($data, $replace)); } Loading core/tests/Drupal/KernelTests/Core/Recipe/InputTest.php +43 −0 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ use Drupal\Component\Uuid\UuidInterface; use Drupal\contact\Entity\ContactForm; use Drupal\Core\Config\Action\ConfigActionException; use Drupal\Core\Recipe\ConsoleInputCollector; use Drupal\Core\Recipe\InputCollectorInterface; use Drupal\Core\Recipe\Recipe; Loading @@ -14,6 +15,7 @@ use Drupal\Core\TypedData\TypedDataInterface; use Drupal\FunctionalTests\Core\Recipe\RecipeTestTrait; use Drupal\KernelTests\KernelTestBase; use Drupal\node\Entity\NodeType; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Style\StyleInterface; use Symfony\Component\Validator\Exception\ValidationFailedException; Loading Loading @@ -229,4 +231,45 @@ public function testLiterals(): void { $this->assertSame('int is 1234, bool is and float is 3.141', $config->get('slogan')); } /** * Tests using input values in entity IDs for config actions. */ public function testInputInConfigEntityIds(): void { $this->assertFalse(\Drupal::moduleHandler()->moduleExists('node')); $collector = new class () implements InputCollectorInterface { /** * {@inheritdoc} */ public function collectValue(string $name, DataDefinitionInterface $definition, mixed $default_value): mixed { return $default_value; } }; $recipe = Recipe::createFromDirectory('core/tests/fixtures/recipes/input_test'); $recipe->input->collectAll($collector); RecipeRunner::processRecipe($recipe); $this->assertInstanceOf(NodeType::class, NodeType::load('test')); // Using an input placeholder in a non-identifying part of the config entity // ID should cause an exception. $recipe = $this->createRecipe([ 'name' => 'Invalid use of an input in config entity ID', 'config' => [ 'actions' => [ 'node.${anything}.test' => [ 'createIfNotExists' => [ 'id' => 'test', ], ], ], ], ]); $recipe->input->collectAll($collector); $this->expectException(ConfigActionException::class); $this->expectExceptionMessage("The entity type for the config name 'node.\${anything}.test' could not be identified."); RecipeRunner::processRecipe($recipe); } } core/tests/fixtures/recipes/input_test/recipe.yml +11 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,8 @@ recipes: # Depend on another recipe in order to prove that we can collect input # from recipes that depend on other recipes. - no_extensions install: - node input: owner: data_type: string Loading @@ -27,8 +29,17 @@ input: default: source: value value: false node_type: data_type: string description: 'The ID of a node type to create' default: source: value value: 'test' config: actions: node.type.${node_type}: createIfNotExists: name: Test Content Type system.site: simpleConfigUpdate: name: "${owner}'s Turf" Loading
core/lib/Drupal/Core/Recipe/RecipeRunner.php +13 −1 Original line number Diff line number Diff line Loading @@ -4,6 +4,8 @@ namespace Drupal\Core\Recipe; use Drupal\Core\Config\Action\ConfigActionException; use Drupal\Core\Config\ConfigManagerInterface; use Drupal\Core\Config\FileStorage; use Drupal\Core\Config\InstallStorage; use Drupal\Core\Config\StorageInterface; Loading Loading @@ -93,11 +95,14 @@ protected static function processInstall(InstallConfigurator $install, StorageIn * The recipe being applied. */ protected static function processConfiguration(Recipe $recipe): void { /** @var \Drupal\Core\Config\ConfigManagerInterface $config_manager */ $config_manager = \Drupal::service(ConfigManagerInterface::class); $config_installer = new RecipeConfigInstaller( \Drupal::service('config.factory'), \Drupal::service('config.storage'), \Drupal::service('config.typed'), \Drupal::service('config.manager'), $config_manager, \Drupal::service('event_dispatcher'), NULL, \Drupal::service('extension.path.resolver')); Loading @@ -118,6 +123,13 @@ protected static function processConfiguration(Recipe $recipe): void { /** @var \Drupal\Core\Config\Action\ConfigActionManager $config_action_manager */ $config_action_manager = \Drupal::service('plugin.manager.config_action'); foreach ($config->config['actions'] as $config_name => $actions) { // If this config name contains an input value, it must begin with the // config prefix of a known entity type. if (str_contains($config_name, '${') && empty($config_manager->getEntityTypeIdByName($config_name))) { throw new ConfigActionException("The entity type for the config name '$config_name' could not be identified."); } $config_name = str_replace($keys, $replace, $config_name); foreach ($actions as $action_id => $data) { $config_action_manager->applyAction($action_id, $config_name, static::replaceInputValues($data, $replace)); } Loading
core/tests/Drupal/KernelTests/Core/Recipe/InputTest.php +43 −0 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ use Drupal\Component\Uuid\UuidInterface; use Drupal\contact\Entity\ContactForm; use Drupal\Core\Config\Action\ConfigActionException; use Drupal\Core\Recipe\ConsoleInputCollector; use Drupal\Core\Recipe\InputCollectorInterface; use Drupal\Core\Recipe\Recipe; Loading @@ -14,6 +15,7 @@ use Drupal\Core\TypedData\TypedDataInterface; use Drupal\FunctionalTests\Core\Recipe\RecipeTestTrait; use Drupal\KernelTests\KernelTestBase; use Drupal\node\Entity\NodeType; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Style\StyleInterface; use Symfony\Component\Validator\Exception\ValidationFailedException; Loading Loading @@ -229,4 +231,45 @@ public function testLiterals(): void { $this->assertSame('int is 1234, bool is and float is 3.141', $config->get('slogan')); } /** * Tests using input values in entity IDs for config actions. */ public function testInputInConfigEntityIds(): void { $this->assertFalse(\Drupal::moduleHandler()->moduleExists('node')); $collector = new class () implements InputCollectorInterface { /** * {@inheritdoc} */ public function collectValue(string $name, DataDefinitionInterface $definition, mixed $default_value): mixed { return $default_value; } }; $recipe = Recipe::createFromDirectory('core/tests/fixtures/recipes/input_test'); $recipe->input->collectAll($collector); RecipeRunner::processRecipe($recipe); $this->assertInstanceOf(NodeType::class, NodeType::load('test')); // Using an input placeholder in a non-identifying part of the config entity // ID should cause an exception. $recipe = $this->createRecipe([ 'name' => 'Invalid use of an input in config entity ID', 'config' => [ 'actions' => [ 'node.${anything}.test' => [ 'createIfNotExists' => [ 'id' => 'test', ], ], ], ], ]); $recipe->input->collectAll($collector); $this->expectException(ConfigActionException::class); $this->expectExceptionMessage("The entity type for the config name 'node.\${anything}.test' could not be identified."); RecipeRunner::processRecipe($recipe); } }
core/tests/fixtures/recipes/input_test/recipe.yml +11 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,8 @@ recipes: # Depend on another recipe in order to prove that we can collect input # from recipes that depend on other recipes. - no_extensions install: - node input: owner: data_type: string Loading @@ -27,8 +29,17 @@ input: default: source: value value: false node_type: data_type: string description: 'The ID of a node type to create' default: source: value value: 'test' config: actions: node.type.${node_type}: createIfNotExists: name: Test Content Type system.site: simpleConfigUpdate: name: "${owner}'s Turf"