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 3, 2024
1 parent 7bd9100 commit e49c1a5
Show file tree
Hide file tree
Showing 9 changed files with 298 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 @@ -211,6 +211,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 @@ -1016,6 +1018,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 @@ -244,6 +244,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 @@ -1049,6 +1051,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
68 changes: 68 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 OCP\ConfigLexicon\IConfigLexicon;
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<string, IConfigLexicon> ['app_id' => IConfigLexicon] */
private array $configLexiconDetails = [];

/**
* $migrationCompleted is only needed to manage the previous structure
Expand All @@ -97,6 +102,7 @@ public function __construct(
protected IDBConnection $connection,
protected LoggerInterface $logger,
protected ICrypto $crypto,
private Coordinator $coordinator,
) {
}

Expand Down Expand Up @@ -455,6 +461,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 @@ -746,6 +753,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 @@ -1506,4 +1514,64 @@ private function getSensitiveKeys(string $app): array {
public function clearCachedConfig(): void {
$this->clearCache();
}

/**
* @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 is not defined in the app config lexicon');
}
return;
}

$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 is typed incorrectly in relation to the app config lexicon');
}

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

private function getConfigDetailsFromLexicon(string $appId): array {

Check failure

Code scanning / Psalm

InvalidReturnType Error

The declared return type 'array<array-key, mixed>' for OC\AppConfig::getConfigDetailsFromLexicon is incorrect, got 'OCP\ConfigLexicon\IConfigLexicon|array{entries: array<string, OCP\ConfigLexicon\IConfigLexiconEntry>, strict: bool}'

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

View workflow job for this annotation

GitHub Actions / static-code-analysis

InvalidReturnType

lib/private/AppConfig.php:1560:63: InvalidReturnType: The declared return type 'array<array-key, mixed>' for OC\AppConfig::getConfigDetailsFromLexicon is incorrect, got 'OCP\ConfigLexicon\IConfigLexicon|array{entries: array<string, OCP\ConfigLexicon\IConfigLexiconEntry>, strict: bool}' (see https://psalm.dev/011)
if (!array_key_exists($appId, $this->configLexiconDetails)) {
$entries = [];
$configLexicon = $this->coordinator->getRegistrationContext()->getConfigLexicon($appId);
foreach ($configLexicon?->getAppConfigs() ?? [] as $configEntry) {
$entries[$configEntry->getKey()] = $configEntry;
}

$this->configLexiconDetails[$appId] = [

Check failure

Code scanning / Psalm

InvalidPropertyAssignmentValue Error

$this->configLexiconDetails with declared type 'array<string, OCP\ConfigLexicon\IConfigLexicon>' cannot be assigned type 'non-empty-array<string, OCP\ConfigLexicon\IConfigLexicon|array{entries: array<string, OCP\ConfigLexicon\IConfigLexiconEntry>, strict: bool}>'

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

View workflow job for this annotation

GitHub Actions / static-code-analysis

InvalidPropertyAssignmentValue

lib/private/AppConfig.php:1568:4: InvalidPropertyAssignmentValue: $this->configLexiconDetails with declared type 'array<string, OCP\ConfigLexicon\IConfigLexicon>' cannot be assigned type 'non-empty-array<string, OCP\ConfigLexicon\IConfigLexicon|array{entries: array<string, OCP\ConfigLexicon\IConfigLexiconEntry>, strict: bool}>' (see https://psalm.dev/145)
'entries' => $entries,
'strict' => $configLexicon?->isStrict() ?? false
];
}

return $this->configLexiconDetails[$appId];

Check failure

Code scanning / Psalm

InvalidReturnStatement Error

The inferred type 'OCP\ConfigLexicon\IConfigLexicon|array{entries: array<string, OCP\ConfigLexicon\IConfigLexiconEntry>, strict: bool}' does not match the declared return type 'array<array-key, mixed>' for OC\AppConfig::getConfigDetailsFromLexicon

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

View workflow job for this annotation

GitHub Actions / static-code-analysis

InvalidReturnStatement

lib/private/AppConfig.php:1574:10: InvalidReturnStatement: The inferred type 'OCP\ConfigLexicon\IConfigLexicon|array{entries: array<string, OCP\ConfigLexicon\IConfigLexiconEntry>, strict: bool}' does not match the declared return type 'array<array-key, mixed>' for OC\AppConfig::getConfigDetailsFromLexicon (see https://psalm.dev/128)
}

}
36 changes: 36 additions & 0 deletions lib/private/AppFramework/Bootstrap/RegistrationContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
use OCP\Calendar\Room\IBackend as IRoomBackend;
use OCP\Capabilities\ICapability;
use OCP\Collaboration\Reference\IReferenceProvider;
use OCP\ConfigLexicon\IConfigLexicon;
use OCP\ConfigLexicon\IConfigValue;
use OCP\Dashboard\IManager;
use OCP\Dashboard\IWidget;
use OCP\EventDispatcher\IEventDispatcher;
Expand All @@ -59,6 +61,8 @@
use OCP\TextProcessing\IProvider as ITextProcessingProvider;
use OCP\Translation\ITranslationProvider;
use OCP\UserMigration\IMigrator as IUserMigrator;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Log\LoggerInterface;
use RuntimeException;
use Throwable;
Expand Down Expand Up @@ -160,6 +164,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 +418,13 @@ public function registerDeclarativeSettings(string $declarativeSettingsClass): v
$declarativeSettingsClass
);
}

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

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

public function registerConfigLexicon(string $appId, string $configLexiconClass): void {
$this->configLexiconClasses[$appId] = $configLexiconClass;
}

/**
* @param App[] $apps
*/
Expand Down Expand Up @@ -920,4 +938,22 @@ 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 {
if (!array_key_exists($appId, $this->configLexiconClasses)) {
return null;
}

$configLexicon = \OCP\Server::get($this->configLexiconClasses[$appId], IConfigLexicon::class);

Check failure

Code scanning / Psalm

TooManyArguments Error

Too many arguments for OCP\Server::get - expecting 1 but saw 2

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

View workflow job for this annotation

GitHub Actions / static-code-analysis

TooManyArguments

lib/private/AppFramework/Bootstrap/RegistrationContext.php:955:20: TooManyArguments: Too many arguments for OCP\Server::get - expecting 1 but saw 2 (see https://psalm.dev/026)
// confirmer IConfigLexicon avant le load ?
return $configLexicon;
}
}
115 changes: 115 additions & 0 deletions lib/private/ConfigLexicon/ConfigLexiconEntry.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<?php

declare(strict_types=1);
/**
* @copyright Copyright (c) 2024 Maxence Lange <[email protected]>
*
* @author Maxence Lange <[email protected]>
*
* @license AGPL-3.0 or later
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/

namespace OC\ConfigLexicon;

use OC;
use OCP\ConfigLexicon\IConfigLexiconEntry;

class ConfigLexiconEntry implements IConfigLexiconEntry {
private string $definition = '';
private ?string $default = null;
private bool $lazy = false;
private bool $sensitive = false;
private bool $deprecated = false;

public function __construct(
private string $key,
private int $valueType,
string $definition = '',
) {

if (OC::$CLI) { // only store definition if ran from CLI
$this->definition = $definition;
}
}

public function getKey(): string {
return $this->key;
}

public function getValueType(): int {
return $this->valueType;
}

public function withDefaultString(string $default): self {
$this->default = $default;
return $this;
}

public function withDefaultInt(int $default): self {
$this->default = (string) $default;
return $this;
}

public function withDefaultFloat(float $default): self {
$this->default = (string) $default;
return $this;
}

public function withDefaultBool(bool $default): self {
$this->default = ($default) ? '1' : '0';
return $this;
}

public function withDefaultArray(array $default): self {
$this->default = json_encode($default);
return $this;
}

public function getDefault(): ?string {
return $this->default;
}

public function getDefinition(): string {
return $this->definition;
}

public function asLazy(bool $lazy = true): self {
$this->lazy = $lazy;
return $this;
}

public function isLazy(): bool {
return $this->lazy;
}

public function asSensitive(bool $sensitive = true): self {
$this->sensitive = $sensitive;
return $this;
}

public function isSensitive(): bool {
return $this->sensitive;
}

public function asDeprecated(bool $deprecated = true): self {
$this->deprecated = $deprecated;
return $this;
}

public function isDeprecated(): bool {
return $this->deprecated;
}
}
2 changes: 2 additions & 0 deletions lib/public/AppFramework/Bootstrap/IRegistrationContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -410,4 +410,6 @@ public function registerSetupCheck(string $setupCheckClass): void;
* @since 29.0.0
*/
public function registerDeclarativeSettings(string $declarativeSettingsClass): void;

public function registerConfigLexicon(string $configLexiconClass): void;

Check failure on line 414 in lib/public/AppFramework/Bootstrap/IRegistrationContext.php

View workflow job for this annotation

GitHub Actions / static-code-analysis-ocp

InvalidDocblock

lib/public/AppFramework/Bootstrap/IRegistrationContext.php:414:2: InvalidDocblock: PHPDoc is required for methods in OCP. (see https://psalm.dev/008)
}
16 changes: 16 additions & 0 deletions lib/public/ConfigLexicon/IConfigLexicon.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace OCP\ConfigLexicon;

interface IConfigLexicon {

Check failure on line 5 in lib/public/ConfigLexicon/IConfigLexicon.php

View workflow job for this annotation

GitHub Actions / static-code-analysis-ocp

InvalidDocblock

lib/public/ConfigLexicon/IConfigLexicon.php:5:1: InvalidDocblock: PHPDoc is required for classes/interfaces in OCP. (see https://psalm.dev/008)
public function isStrict(): bool;

Check failure on line 6 in lib/public/ConfigLexicon/IConfigLexicon.php

View workflow job for this annotation

GitHub Actions / static-code-analysis-ocp

InvalidDocblock

lib/public/ConfigLexicon/IConfigLexicon.php:6:2: InvalidDocblock: PHPDoc is required for methods in OCP. (see https://psalm.dev/008)

/**
* @return IConfigLexiconEntry[]
*/
public function getAppConfigs(): array;

Check failure on line 11 in lib/public/ConfigLexicon/IConfigLexicon.php

View workflow job for this annotation

GitHub Actions / static-code-analysis-ocp

InvalidDocblock

lib/public/ConfigLexicon/IConfigLexicon.php:11:2: InvalidDocblock: @SInCE is required for methods in OCP. (see https://psalm.dev/008)
/**
* @return IConfigLexiconEntry[]
*/
public function getUserPreferences(): array;

Check failure on line 15 in lib/public/ConfigLexicon/IConfigLexicon.php

View workflow job for this annotation

GitHub Actions / static-code-analysis-ocp

InvalidDocblock

lib/public/ConfigLexicon/IConfigLexicon.php:15:2: InvalidDocblock: @SInCE is required for methods in OCP. (see https://psalm.dev/008)
}
Loading

0 comments on commit e49c1a5

Please sign in to comment.