From a1917ee3268fcf5785d8af29bf7f59c38822444f Mon Sep 17 00:00:00 2001 From: Moshe Weitzman Date: Thu, 6 Jan 2022 15:01:27 -0500 Subject: [PATCH] Let config:set change multiple top-level keys. Add docs and tests. (#4983) * Let config:set change multiple top-level keys. Add docs and tests. * Also get rid of unused --value option in state:set --- src/Drupal/Commands/config/ConfigCommands.php | 43 +++++++++++-------- src/Drupal/Commands/core/StateCommands.php | 6 +-- tests/functional/ConfigTest.php | 29 ++++++++++++- 3 files changed, 53 insertions(+), 25 deletions(-) diff --git a/src/Drupal/Commands/config/ConfigCommands.php b/src/Drupal/Commands/config/ConfigCommands.php index 10e1342567..e8c85f86d6 100644 --- a/src/Drupal/Commands/config/ConfigCommands.php +++ b/src/Drupal/Commands/config/ConfigCommands.php @@ -126,59 +126,64 @@ public function get($config_name, $key = '', $options = ['format' => 'yaml', 'so } /** - * Set config value directly. Does not perform a config import. + * Save a config value directly. Does not perform a config import. * * @command config:set * @validate-config-name * @todo @interact-config-name deferred until we have interaction for key. * @param $config_name The config object name, for example system.site. - * @param $key The config key, for example page.front. + * @param $key The config key, for example page.front. Use ? if you are updating multiple keys. * @param $value The value to assign to the config key. Use - to read from STDIN. - * @option input-format Format to parse the object. Recognized values: string, yaml - * @option value The value to assign to the config key (if any). - * @hidden-options value + * @option input-format Format to parse the object. Recognized values: string, yaml. Since JSON is a subset of YAML, $value may be in JSON format. + * @usage drush config:set system.site name MySite + * Sets a value for the key name of system.site config object. * @usage drush config:set system.site page.front '/path/to/page' * Sets the given URL path as value for the config item with key page.front of system.site config object. * @usage drush config:set system.site '[]' * Sets the given key to an empty array. + * @usage drush config:set --input-format=yaml user.role.authenticated permissions [foo,bar] + * Use a sequence as value for the key permissions of user.role.authenticated config object. + * @usage drush config:set --input-format=yaml system.site page {403: '403', front: home} + * Use a mapping as value for the key page of system.site config object. + * @usage drush config:set --input-format=yaml user.role.authenticated ? "{label: 'Auth user', weight: 5}" + * Update two top level keys (label, weight) in the system.site config object. * @aliases cset,config-set */ - public function set($config_name, $key, $value = null, $options = ['input-format' => 'string', 'value' => self::REQ]) + public function set($config_name, $key, $value = null, $options = ['input-format' => 'string']) { - // This hidden option is a convenient way to pass a value without passing a key. - $data = $options['value'] ?: $value; + $data = $value; if (!isset($data)) { throw new \Exception(dt('No config value specified.')); } - $config = $this->getConfigFactory()->getEditable($config_name); - // Check to see if config key already exists. - $new_key = $config->get($key) === null; - // Special flag indicating that the value has been passed via STDIN. if ($data === '-') { $data = $this->stdin()->contents(); } - // Special handling for empty array. if ($data == '[]') { $data = []; } - // Now, we parse the value. + // Parse the value if needed. switch ($options['input-format']) { case 'yaml': $parser = new Parser(); $data = $parser->parse($data, true); } - if (is_array($data) && !empty($data) && $this->io()->confirm(dt('Do you want to update or set multiple keys on !name config.', ['!name' => $config_name]))) { - foreach ($data as $data_key => $value) { - $config->set("$key.$data_key", $value); + $config = $this->getConfigFactory()->getEditable($config_name); + // Check to see if config key already exists. + $new_key = $config->get($key) === null; + $simulate = $this->getConfig()->simulate(); + + if ($key == '?' && !empty($data) && $this->io()->confirm(dt('Do you want to update or set multiple keys on !name config.', ['!name' => $config_name]))) { + foreach ($data as $data_key => $val) { + $config->set($data_key, $val); } - return $config->save(); + return $simulate ? self::EXIT_SUCCESS : $config->save(); } else { $confirmed = false; if ($config->isNew() && $this->io()->confirm(dt('!name config does not exist. Do you want to create a new config object?', ['!name' => $config_name]))) { @@ -188,7 +193,7 @@ public function set($config_name, $key, $value = null, $options = ['input-format } elseif ($this->io()->confirm(dt('Do you want to update !key key in !name config?', ['!key' => $key, '!name' => $config_name]))) { $confirmed = true; } - if ($confirmed && !$this->getConfig()->simulate()) { + if ($confirmed && !$simulate) { return $config->set($key, $data)->save(); } } diff --git a/src/Drupal/Commands/core/StateCommands.php b/src/Drupal/Commands/core/StateCommands.php index a2f01b4943..5c3febc6f5 100644 --- a/src/Drupal/Commands/core/StateCommands.php +++ b/src/Drupal/Commands/core/StateCommands.php @@ -51,8 +51,6 @@ public function get(string $key, $options = ['format' => 'string']): PropertyLis * @param string $key The state key, for example: system.cron_last. * @param mixed $value The value to assign to the state key. Use - to read from STDIN. * @option input-format Type for the value. Other recognized values: string, integer, float, boolean, json, yaml. - * @option value For internal use only. - * @hidden-options value * @usage drush sset system.maintenance_mode 1 --input-format=integer * Put site into Maintenance mode. * @usage drush state:set system.cron_last 1406682882 --input-format=integer @@ -61,10 +59,8 @@ public function get(string $key, $options = ['format' => 'string']): PropertyLis * Set a key to a complex value (e.g. array) * @aliases sset,state-set */ - public function set(string $key, $value, $options = ['input-format' => 'auto', 'value' => self::REQ]): void + public function set(string $key, $value, $options = ['input-format' => 'auto']): void { - // A convenient way to pass a multiline value within a backend request. - $value = $options['value'] ?: $value; if (!isset($value)) { throw new \Exception(dt('No state value specified.')); diff --git a/tests/functional/ConfigTest.php b/tests/functional/ConfigTest.php index 0935d2a4e7..690a9d3dbe 100644 --- a/tests/functional/ConfigTest.php +++ b/tests/functional/ConfigTest.php @@ -25,11 +25,38 @@ public function setup(): void } } + /** + * @todo If this becomes an integration test, add test for stdin handling. + */ public function testConfigGetSet() { + // Simple value $this->drush('config:set', ['system.site', 'name', 'config_test']); $this->drush('config:get', ['system.site', 'name']); - $this->assertEquals("'system.site:name': config_test", $this->getOutput(), 'Config was successfully set and get.'); + $this->assertEquals("'system.site:name': config_test", $this->getOutput()); + + // Nested value + $this->drush('config:set', ['system.site', 'page.front', 'llama']); + $this->drush('config:get', ['system.site', 'page.front']); + $this->assertEquals("'system.site:page.front': llama", $this->getOutput()); + + // Simple sequence value + $this->drush('config:set', ['user.role.authenticated', 'permissions', '[foo,bar]'], ['input-format' => 'yaml']); + $this->drush('config:get', ['user.role.authenticated', 'permissions'], ['format' => 'json']); + $output = $this->getOutputFromJSON('user.role.authenticated:permissions'); + + // Mapping value + $this->drush('config:set', ['system.site', 'page', "{403: '403', front: home}"], ['input-format' => 'yaml']); + $this->drush('config:get', ['system.site', 'page'], ['format' => 'json']); + $output = $this->getOutputFromJSON('system.site:page'); + $this->assertSame(['403' => '403', 'front' => 'home'], $output); + + // Multiple top-level keys + $this->drush('config:set', ['user.role.authenticated', '?', "{label: 'Auth user', weight: 5}"], ['input-format' => 'yaml']); + $this->drush('config:get', ['user.role.authenticated'], ['format' => 'json']); + $output = $this->getOutputFromJSON(); + $this->assertSame('Auth user', $output['label']); + $this->assertSame(5, $output['weight']); } public function testConfigExportImportStatusExistingConfig()