From 9380375cc65aa90cc60e25e0bccfb2aedfbfb40e Mon Sep 17 00:00:00 2001 From: Maxence Lange Date: Wed, 23 Jul 2025 15:22:51 -0100 Subject: [PATCH] feat(lexicon): add note and key-details Signed-off-by: Maxence Lange --- core/Command/Base.php | 2 + core/Command/Config/App/GetConfig.php | 12 ++++++ core/Command/Config/App/SetConfig.php | 5 +++ lib/private/AppConfig.php | 59 +++++++++++++++++++++++---- lib/public/Config/Lexicon/Entry.php | 20 ++++++++- lib/public/IAppConfig.php | 28 +++++++++++++ 6 files changed, 117 insertions(+), 9 deletions(-) diff --git a/core/Command/Base.php b/core/Command/Base.php index c9b6337b64a..6ab2765b0f9 100644 --- a/core/Command/Base.php +++ b/core/Command/Base.php @@ -170,6 +170,8 @@ class Base extends Command implements CompletionAwareInterface { return 'true'; } elseif ($value === null) { return $returnNull ? null : 'null'; + } if ($value instanceof \UnitEnum) { + return $value->value; } else { return $value; } diff --git a/core/Command/Config/App/GetConfig.php b/core/Command/Config/App/GetConfig.php index b68476a2e91..af0c5648232 100644 --- a/core/Command/Config/App/GetConfig.php +++ b/core/Command/Config/App/GetConfig.php @@ -37,6 +37,12 @@ class GetConfig extends Base { InputOption::VALUE_NONE, 'returns complete details about the app config value' ) + ->addOption( + '--key-details', + null, + InputOption::VALUE_NONE, + 'returns complete details about the app config key' + ) ->addOption( 'default-value', null, @@ -66,6 +72,12 @@ class GetConfig extends Base { return 0; } + if ($input->getOption('key-details')) { + $details = $this->appConfig->getKeyDetails($appName, $configName); + $this->writeArrayInOutputFormat($input, $output, $details); + return 0; + } + try { $configValue = $this->appConfig->getDetails($appName, $configName)['value']; } catch (AppConfigUnknownKeyException $e) { diff --git a/core/Command/Config/App/SetConfig.php b/core/Command/Config/App/SetConfig.php index 1f4ab81bf05..c818404fc0e 100644 --- a/core/Command/Config/App/SetConfig.php +++ b/core/Command/Config/App/SetConfig.php @@ -199,6 +199,11 @@ class SetConfig extends Base { $current['lazy'] ? 'lazy cache' : 'fast cache' ) ); + $keyDetails = $this->appConfig->getKeyDetails($appName, $configName); + if (($keyDetails['note'] ?? '') !== '') { + $output->writeln('Note: ' . $keyDetails['note']); + } + } else { $output->writeln('Config value were not updated'); } diff --git a/lib/private/AppConfig.php b/lib/private/AppConfig.php index 0a46109c9f9..2280ac1a79f 100644 --- a/lib/private/AppConfig.php +++ b/lib/private/AppConfig.php @@ -17,6 +17,7 @@ use OCP\Config\Lexicon\Entry; use OCP\Config\Lexicon\ILexicon; use OCP\Config\Lexicon\Preset; use OCP\Config\Lexicon\Strictness; +use OCP\Config\ValueType; use OCP\DB\Exception as DBException; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Exceptions\AppConfigIncorrectTypeException; @@ -1094,6 +1095,49 @@ class AppConfig implements IAppConfig { ]; } + /** + * @inheritDoc + * + * @param string $app id of the app + * @param string $key config key + * + * @return array{app: string, key: string, lazy?: bool, valueType?: ValueType, valueTypeName?: string, sensitive?: bool, default?: string, definition?: string, note?: string} + * @since 32.0.0 + */ + public function getKeyDetails(string $app, string $key): array { + $this->assertParams($app, $key); + try { + $details = $this->getDetails($app, $key); + } catch (AppConfigUnknownKeyException $e) { + $details = [ + 'app' => $app, + 'key' => $key + ]; + } + + /** @var Entry $lexiconEntry */ + try { + $lazy = false; + $this->matchAndApplyLexiconDefinition($app, $key, $lazy, lexiconEntry: $lexiconEntry); + } catch (AppConfigTypeConflictException|AppConfigUnknownKeyException) { + // can be ignored + } + + if ($lexiconEntry !== null) { + $details = array_merge($details, [ + 'lazy' => $lexiconEntry->isLazy(), + 'valueType' => $lexiconEntry->getValueType(), + 'valueTypeName' => $lexiconEntry->getValueType()->name, + 'sensitive' => $lexiconEntry->isFlagged(self::FLAG_SENSITIVE), + 'default' => $lexiconEntry->getDefault($this->getLexiconPreset()), + 'definition' => $lexiconEntry->getDefinition(), + 'note' => $lexiconEntry->getNote(), + ]); + } + + return array_filter($details); + } + /** * @param string $type * @@ -1631,6 +1675,7 @@ class AppConfig implements IAppConfig { ?bool &$lazy = null, int &$type = self::VALUE_MIXED, ?string &$default = null, + ?Entry &$lexiconEntry = null, ): bool { if (in_array($key, [ @@ -1655,27 +1700,27 @@ class AppConfig implements IAppConfig { return true; } - /** @var Entry $configValue */ - $configValue = $configDetails['entries'][$key]; + /** @var Entry $lexiconEntry */ + $lexiconEntry = $configDetails['entries'][$key]; $type &= ~self::VALUE_SENSITIVE; - $appConfigValueType = $configValue->getValueType()->toAppConfigFlag(); + $appConfigValueType = $lexiconEntry->getValueType()->toAppConfigFlag(); if ($type === self::VALUE_MIXED) { $type = $appConfigValueType; // we overwrite if value was requested as mixed } elseif ($appConfigValueType !== $type) { throw new AppConfigTypeConflictException('The app config key ' . $app . '/' . $key . ' is typed incorrectly in relation to the config lexicon'); } - $lazy = $configValue->isLazy(); + $lazy = $lexiconEntry->isLazy(); // only look for default if needed, default from Lexicon got priority if ($default !== null) { - $default = $configValue->getDefault($this->getLexiconPreset()) ?? $default; + $default = $lexiconEntry->getDefault($this->getLexiconPreset()) ?? $default; } - if ($configValue->isFlagged(self::FLAG_SENSITIVE)) { + if ($lexiconEntry->isFlagged(self::FLAG_SENSITIVE)) { $type |= self::VALUE_SENSITIVE; } - if ($configValue->isDeprecated()) { + if ($lexiconEntry->isDeprecated()) { $this->logger->notice('App config key ' . $app . '/' . $key . ' is set as deprecated.'); } diff --git a/lib/public/Config/Lexicon/Entry.php b/lib/public/Config/Lexicon/Entry.php index 0e6e664db36..c810522dde5 100644 --- a/lib/public/Config/Lexicon/Entry.php +++ b/lib/public/Config/Lexicon/Entry.php @@ -23,16 +23,20 @@ class Entry { public const RENAME_INVERT_BOOLEAN = 1; private string $definition = ''; + private string $note = ''; private ?string $default = null; /** - * @param string $key config key, can only contain alphanumerical chars and -._ + * @param string $key config key; can only contain alphanumerical chars and underscore "_" * @param ValueType $type type of config value + * @param string|int|float|bool|array|Closure|null $defaultRaw default value to be used in case none known * @param string $definition optional description of config key available when using occ command * @param bool $lazy set config value as lazy * @param int $flags set flags - * @param string|null $rename previous config key to migrate config value from * @param bool $deprecated set config key as deprecated + * @param string|null $rename source in case of a rename of a config key. + * @param int $options additional bitflag options {@see self::RENAME_INVERT_BOOLEAN} + * @param string $note additional note and warning related to the use of the config key. * * @since 32.0.0 * @psalm-suppress PossiblyInvalidCast @@ -48,6 +52,7 @@ class Entry { private readonly bool $deprecated = false, private readonly ?string $rename = null, private readonly int $options = 0, + string $note = '', ) { // key can only contain alphanumeric chars and underscore "_" if (preg_match('/[^[:alnum:]_]/', $key)) { @@ -57,6 +62,7 @@ class Entry { /** @psalm-suppress UndefinedClass */ if (\OC::$CLI) { // only store definition if ran from CLI $this->definition = $definition; + $this->note = $note; } } @@ -187,6 +193,16 @@ class Entry { return $this->definition; } + /** + * returns eventual note + * + * @return string + * @since 32.0.0 + */ + public function getNote(): string { + return $this->note; + } + /** * returns if config key is set as lazy * diff --git a/lib/public/IAppConfig.php b/lib/public/IAppConfig.php index 68d4332146e..d9e3e0d95a7 100644 --- a/lib/public/IAppConfig.php +++ b/lib/public/IAppConfig.php @@ -8,6 +8,7 @@ declare(strict_types=1); */ namespace OCP; +use OCP\Config\ValueType; use OCP\Exceptions\AppConfigUnknownKeyException; /** @@ -449,6 +450,33 @@ interface IAppConfig { */ public function getDetails(string $app, string $key): array; + /** + * returns an array containing details about a config key. + * key/value pair are available only if it exists. + * + * ``` + * [ + * "app" => "myapp", + * "key" => "mykey", + * "value" => "current_value", + * "default" => "default_if_available", + * "definition" => "this is what it does", + * "note" => "enabling this is not compatible with that", + * "lazy" => false, + * "type" => 4, + * "typeString" => "string", + * 'sensitive' => false + * ] + * ``` + * + * @param string $app id of the app + * @param string $key config key + * + * @return array{app: string, key: string, lazy?: bool, valueType?: ValueType, valueTypeName?: string, sensitive?: bool, default?: string, definition?: string, note?: string} + * @since 32.0.0 + */ + public function getKeyDetails(string $app, string $key): array; + /** * Convert string like 'string', 'integer', 'float', 'bool' or 'array' to * to bitflag {@see VALUE_STRING}, {@see VALUE_INT}, {@see VALUE_FLOAT},