feat(lexicon): add note and key-details

Signed-off-by: Maxence Lange <maxence@artificial-owl.com>
pull/54064/head
Maxence Lange 2025-07-23 15:22:51 +07:00
parent 3b4a268c66
commit 9380375cc6
6 changed files with 117 additions and 9 deletions

@ -170,6 +170,8 @@ class Base extends Command implements CompletionAwareInterface {
return 'true'; return 'true';
} elseif ($value === null) { } elseif ($value === null) {
return $returnNull ? null : 'null'; return $returnNull ? null : 'null';
} if ($value instanceof \UnitEnum) {
return $value->value;
} else { } else {
return $value; return $value;
} }

@ -37,6 +37,12 @@ class GetConfig extends Base {
InputOption::VALUE_NONE, InputOption::VALUE_NONE,
'returns complete details about the app config value' 'returns complete details about the app config value'
) )
->addOption(
'--key-details',
null,
InputOption::VALUE_NONE,
'returns complete details about the app config key'
)
->addOption( ->addOption(
'default-value', 'default-value',
null, null,
@ -66,6 +72,12 @@ class GetConfig extends Base {
return 0; return 0;
} }
if ($input->getOption('key-details')) {
$details = $this->appConfig->getKeyDetails($appName, $configName);
$this->writeArrayInOutputFormat($input, $output, $details);
return 0;
}
try { try {
$configValue = $this->appConfig->getDetails($appName, $configName)['value']; $configValue = $this->appConfig->getDetails($appName, $configName)['value'];
} catch (AppConfigUnknownKeyException $e) { } catch (AppConfigUnknownKeyException $e) {

@ -199,6 +199,11 @@ class SetConfig extends Base {
$current['lazy'] ? 'lazy cache' : 'fast cache' $current['lazy'] ? 'lazy cache' : 'fast cache'
) )
); );
$keyDetails = $this->appConfig->getKeyDetails($appName, $configName);
if (($keyDetails['note'] ?? '') !== '') {
$output->writeln('<comment>Note:</comment> ' . $keyDetails['note']);
}
} else { } else {
$output->writeln('<info>Config value were not updated</info>'); $output->writeln('<info>Config value were not updated</info>');
} }

@ -17,6 +17,7 @@ use OCP\Config\Lexicon\Entry;
use OCP\Config\Lexicon\ILexicon; use OCP\Config\Lexicon\ILexicon;
use OCP\Config\Lexicon\Preset; use OCP\Config\Lexicon\Preset;
use OCP\Config\Lexicon\Strictness; use OCP\Config\Lexicon\Strictness;
use OCP\Config\ValueType;
use OCP\DB\Exception as DBException; use OCP\DB\Exception as DBException;
use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Exceptions\AppConfigIncorrectTypeException; 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 * @param string $type
* *
@ -1631,6 +1675,7 @@ class AppConfig implements IAppConfig {
?bool &$lazy = null, ?bool &$lazy = null,
int &$type = self::VALUE_MIXED, int &$type = self::VALUE_MIXED,
?string &$default = null, ?string &$default = null,
?Entry &$lexiconEntry = null,
): bool { ): bool {
if (in_array($key, if (in_array($key,
[ [
@ -1655,27 +1700,27 @@ class AppConfig implements IAppConfig {
return true; return true;
} }
/** @var Entry $configValue */ /** @var Entry $lexiconEntry */
$configValue = $configDetails['entries'][$key]; $lexiconEntry = $configDetails['entries'][$key];
$type &= ~self::VALUE_SENSITIVE; $type &= ~self::VALUE_SENSITIVE;
$appConfigValueType = $configValue->getValueType()->toAppConfigFlag(); $appConfigValueType = $lexiconEntry->getValueType()->toAppConfigFlag();
if ($type === self::VALUE_MIXED) { if ($type === self::VALUE_MIXED) {
$type = $appConfigValueType; // we overwrite if value was requested as mixed $type = $appConfigValueType; // we overwrite if value was requested as mixed
} elseif ($appConfigValueType !== $type) { } elseif ($appConfigValueType !== $type) {
throw new AppConfigTypeConflictException('The app config key ' . $app . '/' . $key . ' is typed incorrectly in relation to the config lexicon'); 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 // only look for default if needed, default from Lexicon got priority
if ($default !== null) { 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; $type |= self::VALUE_SENSITIVE;
} }
if ($configValue->isDeprecated()) { if ($lexiconEntry->isDeprecated()) {
$this->logger->notice('App config key ' . $app . '/' . $key . ' is set as deprecated.'); $this->logger->notice('App config key ' . $app . '/' . $key . ' is set as deprecated.');
} }

@ -23,16 +23,20 @@ class Entry {
public const RENAME_INVERT_BOOLEAN = 1; public const RENAME_INVERT_BOOLEAN = 1;
private string $definition = ''; private string $definition = '';
private string $note = '';
private ?string $default = null; 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 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 string $definition optional description of config key available when using occ command
* @param bool $lazy set config value as lazy * @param bool $lazy set config value as lazy
* @param int $flags set flags * @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 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 * @since 32.0.0
* @psalm-suppress PossiblyInvalidCast * @psalm-suppress PossiblyInvalidCast
@ -48,6 +52,7 @@ class Entry {
private readonly bool $deprecated = false, private readonly bool $deprecated = false,
private readonly ?string $rename = null, private readonly ?string $rename = null,
private readonly int $options = 0, private readonly int $options = 0,
string $note = '',
) { ) {
// key can only contain alphanumeric chars and underscore "_" // key can only contain alphanumeric chars and underscore "_"
if (preg_match('/[^[:alnum:]_]/', $key)) { if (preg_match('/[^[:alnum:]_]/', $key)) {
@ -57,6 +62,7 @@ class Entry {
/** @psalm-suppress UndefinedClass */ /** @psalm-suppress UndefinedClass */
if (\OC::$CLI) { // only store definition if ran from CLI if (\OC::$CLI) { // only store definition if ran from CLI
$this->definition = $definition; $this->definition = $definition;
$this->note = $note;
} }
} }
@ -187,6 +193,16 @@ class Entry {
return $this->definition; 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 * returns if config key is set as lazy
* *

@ -8,6 +8,7 @@ declare(strict_types=1);
*/ */
namespace OCP; namespace OCP;
use OCP\Config\ValueType;
use OCP\Exceptions\AppConfigUnknownKeyException; use OCP\Exceptions\AppConfigUnknownKeyException;
/** /**
@ -449,6 +450,33 @@ interface IAppConfig {
*/ */
public function getDetails(string $app, string $key): array; 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 * Convert string like 'string', 'integer', 'float', 'bool' or 'array' to
* to bitflag {@see VALUE_STRING}, {@see VALUE_INT}, {@see VALUE_FLOAT}, * to bitflag {@see VALUE_STRING}, {@see VALUE_INT}, {@see VALUE_FLOAT},