Skip to content

Commit

Permalink
feat(config): implement config lexicon
Browse files Browse the repository at this point in the history
Signed-off-by: Maxence Lange <[email protected]>
  • Loading branch information
ArtificialOwl committed Apr 22, 2024
1 parent 097f04c commit 923d52a
Show file tree
Hide file tree
Showing 9 changed files with 542 additions and 0 deletions.
3 changes: 3 additions & 0 deletions lib/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@
'OCP\\Comments\\MessageTooLongException' => $baseDir . '/lib/public/Comments/MessageTooLongException.php',
'OCP\\Comments\\NotFoundException' => $baseDir . '/lib/public/Comments/NotFoundException.php',
'OCP\\Common\\Exception\\NotFoundException' => $baseDir . '/lib/public/Common/Exception/NotFoundException.php',
'OCP\\ConfigLexicon\\IConfigLexicon' => $baseDir . '/lib/public/ConfigLexicon/IConfigLexicon.php',
'OCP\\ConfigLexicon\\IConfigLexiconEntry' => $baseDir . '/lib/public/ConfigLexicon/IConfigLexiconEntry.php',
'OCP\\Config\\BeforePreferenceDeletedEvent' => $baseDir . '/lib/public/Config/BeforePreferenceDeletedEvent.php',
'OCP\\Config\\BeforePreferenceSetEvent' => $baseDir . '/lib/public/Config/BeforePreferenceSetEvent.php',
'OCP\\Console\\ConsoleEvent' => $baseDir . '/lib/public/Console/ConsoleEvent.php',
Expand Down Expand Up @@ -1025,6 +1027,7 @@
'OC\\Comments\\Manager' => $baseDir . '/lib/private/Comments/Manager.php',
'OC\\Comments\\ManagerFactory' => $baseDir . '/lib/private/Comments/ManagerFactory.php',
'OC\\Config' => $baseDir . '/lib/private/Config.php',
'OC\\ConfigLexicon\\ConfigLexiconEntry' => $baseDir . '/lib/private/ConfigLexicon/ConfigLexiconEntry.php',
'OC\\Console\\Application' => $baseDir . '/lib/private/Console/Application.php',
'OC\\Console\\TimestampFormatter' => $baseDir . '/lib/private/Console/TimestampFormatter.php',
'OC\\ContactsManager' => $baseDir . '/lib/private/ContactsManager.php',
Expand Down
3 changes: 3 additions & 0 deletions lib/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,8 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Comments\\MessageTooLongException' => __DIR__ . '/../../..' . '/lib/public/Comments/MessageTooLongException.php',
'OCP\\Comments\\NotFoundException' => __DIR__ . '/../../..' . '/lib/public/Comments/NotFoundException.php',
'OCP\\Common\\Exception\\NotFoundException' => __DIR__ . '/../../..' . '/lib/public/Common/Exception/NotFoundException.php',
'OCP\\ConfigLexicon\\IConfigLexicon' => __DIR__ . '/../../..' . '/lib/public/ConfigLexicon/IConfigLexicon.php',
'OCP\\ConfigLexicon\\IConfigLexiconEntry' => __DIR__ . '/../../..' . '/lib/public/ConfigLexicon/IConfigLexiconEntry.php',
'OCP\\Config\\BeforePreferenceDeletedEvent' => __DIR__ . '/../../..' . '/lib/public/Config/BeforePreferenceDeletedEvent.php',
'OCP\\Config\\BeforePreferenceSetEvent' => __DIR__ . '/../../..' . '/lib/public/Config/BeforePreferenceSetEvent.php',
'OCP\\Console\\ConsoleEvent' => __DIR__ . '/../../..' . '/lib/public/Console/ConsoleEvent.php',
Expand Down Expand Up @@ -1058,6 +1060,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Comments\\Manager' => __DIR__ . '/../../..' . '/lib/private/Comments/Manager.php',
'OC\\Comments\\ManagerFactory' => __DIR__ . '/../../..' . '/lib/private/Comments/ManagerFactory.php',
'OC\\Config' => __DIR__ . '/../../..' . '/lib/private/Config.php',
'OC\\ConfigLexicon\\ConfigLexiconEntry' => __DIR__ . '/../../..' . '/lib/private/ConfigLexicon/ConfigLexiconEntry.php',
'OC\\Console\\Application' => __DIR__ . '/../../..' . '/lib/private/Console/Application.php',
'OC\\Console\\TimestampFormatter' => __DIR__ . '/../../..' . '/lib/private/Console/TimestampFormatter.php',
'OC\\ContactsManager' => __DIR__ . '/../../..' . '/lib/private/ContactsManager.php',
Expand Down
77 changes: 77 additions & 0 deletions lib/private/AppConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@

use InvalidArgumentException;
use JsonException;
use OC\AppFramework\Bootstrap\Coordinator;
use OC\ConfigLexicon\ConfigLexiconEntry;
use OCP\ConfigLexicon\IConfigLexiconEntry;
use OCP\DB\Exception as DBException;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Exceptions\AppConfigIncorrectTypeException;
Expand Down Expand Up @@ -82,6 +85,8 @@ class AppConfig implements IAppConfig {
private array $valueTypes = []; // type for all config values
private bool $fastLoaded = false;
private bool $lazyLoaded = false;
/** @var array<array-key, array{entries: array<array-key, IConfigLexiconEntry>, strict: bool}> ['app_id' => ['strict' => bool, 'entries' => ['config_key' => IConfigLexiconEntry[]]] */
private array $configLexiconDetails = [];

/**
* $migrationCompleted is only needed to manage the previous structure
Expand Down Expand Up @@ -457,6 +462,7 @@ private function getTypedValue(
int $type
): string {
$this->assertParams($app, $key, valueType: $type);
$this->compareRegisteredConfigValues($app, $key, $lazy, $type, $default);
$this->loadConfig($lazy);

/**
Expand Down Expand Up @@ -748,6 +754,7 @@ private function setTypedValue(
int $type
): bool {
$this->assertParams($app, $key);
$this->compareRegisteredConfigValues($app, $key, $lazy, $type);
$this->loadConfig($lazy);

$sensitive = $this->isTyped(self::VALUE_SENSITIVE, $type);
Expand Down Expand Up @@ -1567,4 +1574,74 @@ private function getSensitiveKeys(string $app): array {
public function clearCachedConfig(): void {
$this->clearCache();
}

/**
* verify and compare current use of config values with defined lexicon
*
* @throws AppConfigUnknownKeyException
* @throws AppConfigTypeConflictException
*/
private function compareRegisteredConfigValues(
string $app,
string $key,
bool &$lazy,
int &$type,
string &$default = '',
): void {
$configDetails = $this->getConfigDetailsFromLexicon($app);
if (!array_key_exists($key, $configDetails['entries'])) {
if ($configDetails['strict'] === true) {
throw new AppConfigUnknownKeyException('The key ' . $app . '/' . $key . ' is not defined in the config lexicon');
}
return;
}

/** @var ConfigLexiconEntry $configValue */
$configValue = $configDetails['entries'][$key];
$type &= ~self::VALUE_SENSITIVE;

if ($configValue->getValueType() !== match($type) {
self::VALUE_STRING => IConfigLexiconEntry::TYPE_STRING,
self::VALUE_INT => IConfigLexiconEntry::TYPE_INT,
self::VALUE_FLOAT => IConfigLexiconEntry::TYPE_FLOAT,
self::VALUE_BOOL => IConfigLexiconEntry::TYPE_BOOL,
self::VALUE_ARRAY => IConfigLexiconEntry::TYPE_ARRAY,
}) {
throw new AppConfigTypeConflictException('The key ' . $app . '/' . $key . ' is typed incorrectly in relation to the config lexicon');
}

$lazy = $configValue->isLazy();
$default = $configValue->getDefault() ?? $default;
if ($configValue->isSensitive()) {
$type |= self::VALUE_SENSITIVE;
}
if ($configValue->isDeprecated()) {
$this->logger->notice('config value ' . $app . '/' . $key . ' is set as deprecated.');
}
}

/**
* extract details from registered $appId's config lexicon
*
* @param string $appId
*
* @return array{entries: array<array-key, IConfigLexiconEntry>, strict: bool}

Check failure

Code scanning / Psalm

MoreSpecificReturnType Error

The declared return type 'array{entries: array<array-key, OCP\ConfigLexicon\IConfigLexiconEntry>, strict: bool}' for OC\AppConfig::getConfigDetailsFromLexicon is more specific than the inferred return type 'array{entries: array<array-key, OCP\ConfigLexicon\IConfigLexiconEntry|mixed>, strict: bool|mixed}'

Check failure on line 1628 in lib/private/AppConfig.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

MoreSpecificReturnType

lib/private/AppConfig.php:1628:13: MoreSpecificReturnType: The declared return type 'array{entries: array<array-key, OCP\ConfigLexicon\IConfigLexiconEntry>, strict: bool}' for OC\AppConfig::getConfigDetailsFromLexicon is more specific than the inferred return type 'array{entries: array<array-key, OCP\ConfigLexicon\IConfigLexiconEntry|mixed>, strict: bool|mixed}' (see https://psalm.dev/070)
*/
private function getConfigDetailsFromLexicon(string $appId): array {
if (!array_key_exists($appId, $this->configLexiconDetails)) {
$entries = [];
$bootstrapCoordinator = \OCP\Server::get(Coordinator::class);
$configLexicon = $bootstrapCoordinator->getRegistrationContext()?->getConfigLexicon($appId);
foreach ($configLexicon?->getAppConfigs() ?? [] as $configEntry) {

Check failure

Code scanning / Psalm

UndefinedClass Error

Class, interface or enum named OC\AppFramework\Bootstrap\IConfigLexicon does not exist

Check failure on line 1635 in lib/private/AppConfig.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

UndefinedClass

lib/private/AppConfig.php:1635:13: UndefinedClass: Class, interface or enum named OC\AppFramework\Bootstrap\IConfigLexicon does not exist (see https://psalm.dev/019)
$entries[$configEntry->getKey()] = $configEntry;
}

$this->configLexiconDetails[$appId] = [
'entries' => $entries,
'strict' => $configLexicon?->isStrict() ?? false

Check failure

Code scanning / Psalm

UndefinedClass Error

Class, interface or enum named OC\AppFramework\Bootstrap\IConfigLexicon does not exist

Check failure on line 1641 in lib/private/AppConfig.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

UndefinedClass

lib/private/AppConfig.php:1641:17: UndefinedClass: Class, interface or enum named OC\AppFramework\Bootstrap\IConfigLexicon does not exist (see https://psalm.dev/019)
];
}

return $this->configLexiconDetails[$appId];

Check failure

Code scanning / Psalm

LessSpecificReturnStatement Error

The type 'array{entries: array<array-key, OCP\ConfigLexicon\IConfigLexiconEntry|mixed>, strict: bool|mixed}' is more general than the declared return type 'array{entries: array<array-key, OCP\ConfigLexicon\IConfigLexiconEntry>, strict: bool}' for OC\AppConfig::getConfigDetailsFromLexicon

Check failure on line 1645 in lib/private/AppConfig.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

LessSpecificReturnStatement

lib/private/AppConfig.php:1645:10: LessSpecificReturnStatement: The type 'array{entries: array<array-key, OCP\ConfigLexicon\IConfigLexiconEntry|mixed>, strict: bool|mixed}' is more general than the declared return type 'array{entries: array<array-key, OCP\ConfigLexicon\IConfigLexiconEntry>, strict: bool}' for OC\AppConfig::getConfigDetailsFromLexicon (see https://psalm.dev/129)
}
}
33 changes: 33 additions & 0 deletions lib/private/AppFramework/Bootstrap/RegistrationContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ class RegistrationContext {
/** @var ServiceRegistration<IDeclarativeSettingsForm>[] */
private array $declarativeSettings = [];

/** @var array<array-key, string> */
private array $configLexiconClasses = [];

/** @var ServiceRegistration<ITeamResourceProvider>[] */
private array $teamResourceProviders = [];

Expand Down Expand Up @@ -411,6 +414,13 @@ public function registerDeclarativeSettings(string $declarativeSettingsClass): v
$declarativeSettingsClass
);
}

public function registerConfigLexicon(string $configLexiconClass): void {
$this->context->registerConfigLexicon(
$this->appId,
$configLexiconClass

Check failure

Code scanning / Psalm

InvalidArgument Error

Argument 2 of OC\AppFramework\Bootstrap\RegistrationContext::registerConfigLexicon expects class-string<OC\AppFramework\Bootstrap\IConfigLexicon>, but class-string<OCP\ConfigLexicon\IConfigLexicon> provided

Check failure on line 421 in lib/private/AppFramework/Bootstrap/RegistrationContext.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

InvalidArgument

lib/private/AppFramework/Bootstrap/RegistrationContext.php:421:6: InvalidArgument: Argument 2 of OC\AppFramework\Bootstrap\RegistrationContext::registerConfigLexicon expects class-string<OC\AppFramework\Bootstrap\IConfigLexicon>, but class-string<OCP\ConfigLexicon\IConfigLexicon> provided (see https://psalm.dev/004)
);
}
};
}

Expand Down Expand Up @@ -590,6 +600,13 @@ public function registerDeclarativeSettings(string $appId, string $declarativeSe
$this->declarativeSettings[] = new ServiceRegistration($appId, $declarativeSettingsClass);
}

/**
* @psalm-param class-string<IConfigLexicon> $configLexiconClass

Check failure

Code scanning / Psalm

UndefinedDocblockClass Error

Docblock-defined class, interface or enum named OC\AppFramework\Bootstrap\IConfigLexicon does not exist

Check failure on line 604 in lib/private/AppFramework/Bootstrap/RegistrationContext.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

UndefinedDocblockClass

lib/private/AppFramework/Bootstrap/RegistrationContext.php:604:18: UndefinedDocblockClass: Docblock-defined class, interface or enum named OC\AppFramework\Bootstrap\IConfigLexicon does not exist (see https://psalm.dev/200)
*/
public function registerConfigLexicon(string $appId, string $configLexiconClass): void {
$this->configLexiconClasses[$appId] = $configLexiconClass;
}

/**
* @param App[] $apps
*/
Expand Down Expand Up @@ -920,4 +937,20 @@ public function getTeamResourceProviders(): array {
public function getDeclarativeSettings(): array {
return $this->declarativeSettings;
}

/**
* returns IConfigLexicon registered by the app.
* null if none registered.
*
* @param string $appId
*
* @return IConfigLexicon|null
*/
public function getConfigLexicon(string $appId): ?IConfigLexicon {

Check failure

Code scanning / Psalm

UndefinedClass Error

Class, interface or enum named OC\AppFramework\Bootstrap\IConfigLexicon does not exist

Check failure on line 949 in lib/private/AppFramework/Bootstrap/RegistrationContext.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

UndefinedClass

lib/private/AppFramework/Bootstrap/RegistrationContext.php:949:51: UndefinedClass: Class, interface or enum named OC\AppFramework\Bootstrap\IConfigLexicon does not exist (see https://psalm.dev/019)
if (!array_key_exists($appId, $this->configLexiconClasses)) {
return null;
}

return \OCP\Server::get($this->configLexiconClasses[$appId]);
}
}
Loading

0 comments on commit 923d52a

Please sign in to comment.