From 7a56c55476a65f5f7a4b2bb2c87c66ecf58b0f71 Mon Sep 17 00:00:00 2001 From: Maxence Lange Date: Mon, 27 Nov 2023 21:02:44 -0100 Subject: [PATCH] ILazyConfig Signed-off-by: Maxence Lange --- core/Command/Config/ListConfigs.php | 25 +- .../Version29000Date20231126110901.php | 60 ++ core/register_command.php | 2 +- lib/private/AllConfig.php | 42 +- lib/private/LazyConfig.php | 566 ++++++++++++++++++ lib/private/Server.php | 2 + lib/private/SystemConfig.php | 6 +- lib/public/ILazyConfig.php | 171 ++++++ tests/Core/Command/Config/ListConfigsTest.php | 7 +- 9 files changed, 865 insertions(+), 16 deletions(-) create mode 100644 core/Migrations/Version29000Date20231126110901.php create mode 100644 lib/private/LazyConfig.php create mode 100644 lib/public/ILazyConfig.php diff --git a/core/Command/Config/ListConfigs.php b/core/Command/Config/ListConfigs.php index 4adb0a9df5b79..e530466da61e8 100644 --- a/core/Command/Config/ListConfigs.php +++ b/core/Command/Config/ListConfigs.php @@ -25,6 +25,7 @@ use OC\Core\Command\Base; use OC\SystemConfig; use OCP\IAppConfig; +use OCP\ILazyConfig; use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -37,6 +38,7 @@ class ListConfigs extends Base { public function __construct( protected SystemConfig $systemConfig, protected IAppConfig $appConfig, + protected ILazyConfig $lazyConfig, ) { parent::__construct(); } @@ -83,17 +85,20 @@ protected function execute(InputInterface $input, OutputInterface $output): int $configs = [ 'system' => $this->getSystemConfigs($noSensitiveValues), 'apps' => [], + 'lazy' => [], ]; foreach ($apps as $appName) { $configs['apps'][$appName] = $this->getAppConfigs($appName, $noSensitiveValues); } + foreach($this->lazyConfig->getApps() as $appId) { + $configs['lazy'][$appId] = $this->getLazyConfigs($appId, $noSensitiveValues); + } break; default: $configs = [ - 'apps' => [ - $app => $this->getAppConfigs($app, $noSensitiveValues), - ], + 'apps' => [$app => $this->getAppConfigs($app, $noSensitiveValues)], + 'lazy' => [$app => $this->getLazyConfigs($app, $noSensitiveValues)] ]; } @@ -107,7 +112,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int * @param bool $noSensitiveValues * @return array */ - protected function getSystemConfigs($noSensitiveValues) { + protected function getSystemConfigs(bool $noSensitiveValues): array { $keys = $this->systemConfig->getKeys(); $configs = []; @@ -133,14 +138,22 @@ protected function getSystemConfigs($noSensitiveValues) { * @param bool $noSensitiveValues * @return array */ - protected function getAppConfigs($app, $noSensitiveValues) { + protected function getAppConfigs(string $app, bool $noSensitiveValues): array { if ($noSensitiveValues) { - return $this->appConfig->getFilteredValues($app, false); + return $this->appConfig->getFilteredValues($app); } else { return $this->appConfig->getValues($app, false); } } + protected function getLazyConfigs(string $app, bool $noSensitiveValues): array { + if ($noSensitiveValues) { + return $this->lazyConfig->getFilteredValues($app); + } else { + return $this->lazyConfig->getValues($app); + } + } + /** * @param string $argumentName * @param CompletionContext $context diff --git a/core/Migrations/Version29000Date20231126110901.php b/core/Migrations/Version29000Date20231126110901.php new file mode 100644 index 0000000000000..e853d23444e05 --- /dev/null +++ b/core/Migrations/Version29000Date20231126110901.php @@ -0,0 +1,60 @@ + + * + * @author Maxence Lange + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + +namespace OC\Core\Migrations; + +use Closure; +use OCP\DB\ISchemaWrapper; +use OCP\DB\Types; +use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; + +// Create new tables for the ILazyConfig API (lazyconfig). +class Version29000Date20231126110901 extends SimpleMigrationStep { + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + $updated = false; + + if (!$schema->hasTable('appconfig_lazy')) { + $table = $schema->createTable('appconfig_lazy'); + $table->addColumn('app_id', Types::STRING, ['length' => 32]); + $table->addColumn('extra', Types::STRING, ['length' => 32]); + $table->addColumn('config_key', Types::STRING, ['length' => 64]); + $table->addColumn('config_value', Types::TEXT); + + $table->setPrimaryKey(['app_id', 'config_key'], 'lazy_app_prim'); + $table->addIndex(['app_id', 'extra'], 'lazy_app_id_i'); + $table->addIndex(['app_id', 'extra', 'config_key'], 'lazy_app_id_key_i'); + $updated = true; + } + + if (!$updated) { + return null; + } + + return $schema; + } +} diff --git a/core/register_command.php b/core/register_command.php index 27863cfdc8d78..9d4ba7efbd4d0 100644 --- a/core/register_command.php +++ b/core/register_command.php @@ -99,7 +99,7 @@ $application->add(new OC\Core\Command\Config\App\GetConfig(\OC::$server->getConfig())); $application->add(new OC\Core\Command\Config\App\SetConfig(\OC::$server->getConfig())); $application->add(new OC\Core\Command\Config\Import(\OC::$server->getConfig())); - $application->add(new OC\Core\Command\Config\ListConfigs(\OC::$server->getSystemConfig(), \OC::$server->getAppConfig())); + $application->add(\OCP\Server::get(\OC\Core\Command\Config\ListConfigs::class)); $application->add(new OC\Core\Command\Config\System\DeleteConfig(\OC::$server->getSystemConfig())); $application->add(new OC\Core\Command\Config\System\GetConfig(\OC::$server->getSystemConfig())); $application->add(new OC\Core\Command\Config\System\SetConfig(\OC::$server->getSystemConfig())); diff --git a/lib/private/AllConfig.php b/lib/private/AllConfig.php index 2a0e8f53b1494..9cefd62787b2b 100644 --- a/lib/private/AllConfig.php +++ b/lib/private/AllConfig.php @@ -36,14 +36,15 @@ use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IConfig; use OCP\IDBConnection; +use OCP\ILazyConfig; use OCP\PreConditionNotMetException; /** * Class to combine all the configuration options ownCloud offers */ class AllConfig implements IConfig { - private SystemConfig $systemConfig; private ?IDBConnection $connection = null; + private ?ILazyConfig $lazyConfig = null; /** * 3 dimensional array with the following structure: @@ -67,9 +68,10 @@ class AllConfig implements IConfig { */ private CappedMemoryCache $userCache; - public function __construct(SystemConfig $systemConfig) { + public function __construct( + private SystemConfig $systemConfig + ) { $this->userCache = new CappedMemoryCache(); - $this->systemConfig = $systemConfig; } /** @@ -546,4 +548,38 @@ public function getUsersForUserValueCaseInsensitive($appName, $key, $value) { public function getSystemConfig() { return $this->systemConfig; } + + public function getLazyConfig(): ILazyConfig { + if ($this->lazyConfig === null) { + $this->lazyConfig = \OCP\Server::get(ILazyConfig::class); + } + + return $this->lazyConfig; + } + + /** + * export AppConfig keys/values into LazyConfig + * + * $this->config->exportToLazy('my_app', [ + * 'first-config' => 'default_value', + * 'my-other-config' => 12, + * 'boolean-flag' => '0' + * ]); + * + * @param string $app + * @param array $configKeys an array with keys to export as index, default as value + * @since 29.0.0 + */ + public function exportToLazy(string $app, array $configKeys): void { + foreach ($configKeys as $key => $default) { + $value = $this->getAppValue($app, $key, (string)$default); + if ($this->getLazyConfig()->hasKey($app, $key) + || $default === $value) { + continue; + } + + $this->getLazyConfig()->setValueString($app, $key, $value); + $this->deleteAppValue($app, $key); + } + } } diff --git a/lib/private/LazyConfig.php b/lib/private/LazyConfig.php new file mode 100644 index 0000000000000..9b532d6c59c9c --- /dev/null +++ b/lib/private/LazyConfig.php @@ -0,0 +1,566 @@ + + * + * @author Maxence Lange + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + +namespace OC; + +use JsonException; +use OC\DB\Connection; +use OCP\DB\Exception as DBException; +use OCP\IConfig; +use OCP\ILazyConfig; +use Psr\Log\LoggerInterface; + +/** + * Lazy load of your app config values. + * + * store a value: + * $this->lazyConfig->setValueString('my_app', 'my_param', 'configuration_value'); + * + * retrieve a value: + * $this->lazyConfig->getValueString('my_app', 'my_param', 'default_value'); + * + * The caching is made in a way that all configured value of my_app + * are retrieved when getValue*() is called for the first time + * + * There is a way to get values only when needed, without filling the cache with rarely needed values + * when getValue*() is called with 'my_app'. A group of values can be set by using 'my_app.my_group': + * + * $this->lazyConfig->setValueString('my_app.my_group', 'my_param', 'configuration_value'); + * $this->lazyConfig->getValueString('my_app.my_group', 'my_param', 'default_value'); + * + * Values set in group 'my_group' are not cached when calling getValue*() on 'my_app', but + * only when using 'my_app.my_group'. + * + * @since 29.0.0 + */ +class LazyConfig implements ILazyConfig { + public const APP_EXTRA_SEPARATOR = '.'; + + private array $cache = []; // cached config + private array $extra = []; // list of loaded extra config + protected array $sensitiveValues = [ + 'circles' => [ + '/^key_pairs$/', + '/^local_gskey$/', + ], + 'external' => [ + '/^sites$/', + ], + 'integration_discourse' => [ + '/^private_key$/', + '/^public_key$/', + ], + 'integration_dropbox' => [ + '/^client_id$/', + '/^client_secret$/', + ], + 'integration_github' => [ + '/^client_id$/', + '/^client_secret$/', + ], + 'integration_gitlab' => [ + '/^client_id$/', + '/^client_secret$/', + '/^oauth_instance_url$/', + ], + 'integration_google' => [ + '/^client_id$/', + '/^client_secret$/', + ], + 'integration_jira' => [ + '/^client_id$/', + '/^client_secret$/', + '/^forced_instance_url$/', + ], + 'integration_onedrive' => [ + '/^client_id$/', + '/^client_secret$/', + ], + 'integration_openproject' => [ + '/^client_id$/', + '/^client_secret$/', + '/^oauth_instance_url$/', + ], + 'integration_reddit' => [ + '/^client_id$/', + '/^client_secret$/', + ], + 'integration_suitecrm' => [ + '/^client_id$/', + '/^client_secret$/', + '/^oauth_instance_url$/', + ], + 'integration_twitter' => [ + '/^consumer_key$/', + '/^consumer_secret$/', + '/^followed_user$/', + ], + 'integration_zammad' => [ + '/^client_id$/', + '/^client_secret$/', + '/^oauth_instance_url$/', + ], + 'notify_push' => [ + '/^cookie$/', + ], + 'spreed' => [ + '/^bridge_bot_password$/', + '/^hosted-signaling-server-(.*)$/', + '/^recording_servers$/', + '/^signaling_servers$/', + '/^signaling_ticket_secret$/', + '/^signaling_token_privkey_(.*)$/', + '/^signaling_token_pubkey_(.*)$/', + '/^sip_bridge_dialin_info$/', + '/^sip_bridge_shared_secret$/', + '/^stun_servers$/', + '/^turn_servers$/', + '/^turn_server_secret$/', + ], + 'support' => [ + '/^last_response$/', + '/^potential_subscription_key$/', + '/^subscription_key$/', + ], + 'theming' => [ + '/^imprintUrl$/', + '/^privacyUrl$/', + '/^slogan$/', + '/^url$/', + ], + 'user_ldap' => [ + '/^(s..)?ldap_agent_password$/', + ], + 'user_saml' => [ + '/^idp-x509cert$/', + ], + ]; + + public function __construct( + private Connection $connection, + private LoggerInterface $logger, + ) { + } + + /** + * **WARNING**: will load all lazy configs from database, unless $loadValues is false + * + * @param bool $loadValues + * + * @inheritDoc + * @return array + * @throws DBException + * @since 29.0.0 + */ + public function getApps(bool $loadValues = true): array { + if ($loadValues) { + $this->loadConfigAll(); + return array_keys($this->cache); + } + + $sql = $this->connection->getQueryBuilder(); + $sql->selectDistinct('app_id') + ->from('appconfig_lazy') + ->groupBy('app_id'); + $result = $sql->execute(); + + $rows = $result->fetchAll(); + $apps = []; + foreach ($rows as $row) { + $apps[] = $row['app_id']; + } + + return $apps; + } + + /** + * @param string $app + * + * @inheritDoc + * @return array + * @throws DBException + * @since 29.0.0 + */ + public function getKeys(string $app): array { + $appId = $this->loadConfig($app); + return array_keys($this->cache[$appId]); + } + + /** + * @param string $app + * @param string $key + * + * @inheritDoc + * @return bool + * @since 29.0.0 + */ + public function hasKey(string $app, string $key): bool { + $appId = $this->loadConfig($app); + return isset($this->cache[$appId][$key]); + } + + /** + * @param string $app + * @param string $key + * + * @inheritDoc + * @return array + * @since 29.0.0 + */ + public function getValues(string $app, string $key = ''): array { + $appId = $this->loadConfig($app, true); + + if ($key === '') { + return $this->cache[$appId] ?? []; + } + + $values = []; + foreach (($this->cache[$appId] ?? []) as $configKey => $configValue) { + if (str_starts_with($configKey, $key)) { + $values[$configKey] = $configValue; + } + } + + return $values; + } + + /** + * @param string $app + * + * @inheritDoc + * @return array + * @since 29.0.0 + */ + public function getFilteredValues(string $app): array { + $values = $this->getValues($app); + foreach (($this->sensitiveValues[$app] ?? []) as $sensitiveKeyExp) { + $sensitiveKeys = preg_grep($sensitiveKeyExp, array_keys($values)); + foreach ($sensitiveKeys as $sensitiveKey) { + $values[$sensitiveKey] = IConfig::SENSITIVE_VALUE; + } + } + + return $values; + } + + /** + * @param string $app + * @param string $key + * @param string $default + * + * @inheritDoc + * @return string + * @since 29.0.0 + */ + public function getValueString(string $app, string $key, string $default = ''): string { + $appId = $this->loadConfig($app); + if (!$this->hasKey($app, $key)) { + return $default; + } + + return $this->cache[$appId][$key]; + } + + /** + * @param string $app + * @param string $key + * @param int $default + * + * @inheritDoc + * @return int + * @since 29.0.0 + */ + public function getValueInt(string $app, string $key, int $default = 0): int { + return (int) $this->getValueString($app, $key, (string)$default); + } + + /** + * @param string $app + * @param string $key + * @param bool $default + * + * @inheritDoc + * @return bool + * @since 29.0.0 + */ + public function getValueBool(string $app, string $key, bool $default = false): bool { + return in_array($this->getValueString($app, $key, $default ? 'true' : 'false'), ['1', 'true', 'yes']); + } + + /** + * @param string $app + * @param string $key + * @param array $default + * + * @inheritDoc + * @return array + * @since 29.0.0 + */ + public function getValueArray(string $app, string $key, array $default = []): array { + try { + $defaultJson = json_encode($default, JSON_THROW_ON_ERROR); + $value = json_decode($this->getValueString($app, $key, $defaultJson), true, JSON_THROW_ON_ERROR); + return (is_array($value)) ? $value : [$value]; + } catch (JsonException) { + return []; + } + } + + /** + * @param string $app + * @param string $key + * @param string $value + * + * @inheritDoc + * @return bool + * @throws DBException + * @since 29.0.0 + */ + public function setValueString(string $app, string $key, string $value): bool { + $appId = $this->loadConfig($app); + $updated = !$this->hasKey($app, $key) || $value !== $this->getValueString($app, $key); + if (!$updated) { + return false; + } + + [, $extra] = $this->parseExtra($app); + $insert = $this->connection->getQueryBuilder(); + $insert->insert('appconfig_lazy') + ->setValue('app_id', $insert->createNamedParameter($appId)) + ->setValue('extra', $insert->createNamedParameter($extra)) + ->setValue('config_key', $insert->createNamedParameter($key)) + ->setValue('config_value', $insert->createNamedParameter($value)); + try { + $insert->executeStatement(); + } catch (DBException $e) { + if ($e->getReason() !== DBException::REASON_UNIQUE_CONSTRAINT_VIOLATION) { + throw $e; // throw exception or just log and returns false !? + } + + $update = $this->connection->getQueryBuilder(); + $update->update('appconfig_lazy') + ->set('config_value', $update->createNamedParameter($value)) + ->set('extra', $update->createNamedParameter($extra)) + ->where($update->expr()->eq('app_id', $update->createNamedParameter($app))) + ->andWhere($update->expr()->eq('config_key', $update->createNamedParameter($key))); + $update->executeStatement(); + } + + return true; + } + + /** + * @param string $app + * @param string $key + * @param int $value + * + * @inheritDoc + * @return bool + * @throws DBException + * @since 29.0.0 + */ + public function setValueInt(string $app, string $key, int $value): bool { + return $this->setValueString($app, $key, (string) $value); + } + + /** + * @param string $app + * @param string $key + * @param bool $value + * + * @inheritDoc + * @return bool + * @throws DBException + * @since 29.0.0 + */ + public function setValueBool(string $app, string $key, bool $value): bool { + return $this->setValueString($app, $key, $value ? 'true' : 'false'); + } + + /** + * @param string $app + * @param string $key + * @param array $value + * + * @inheritDoc + * @return bool + * @throws DBException + * @since 29.0.0 + */ + public function setValueArray(string $app, string $key, array $value): bool { + try { + return $this->setValueString($app, $key, json_encode($value, JSON_THROW_ON_ERROR)); + } catch (JsonException $e) { + $this->logger->warning('could not setValueArray', ['exception' => $e]); + } + + return false; + } + + /** + * @param string $app + * @param string $key + * + * @inheritDoc + * @throws DBException + * @since 29.0.0 + */ + public function unsetKey(string $app, string $key): void { + if (!$this->hasKey($app, $key)) { + return; + } + + $qb = $this->connection->getQueryBuilder(); + $qb->delete('appconfig_lazy') + ->where($qb->expr()->eq('app_id', $qb->createNamedParameter($app))) + ->andWhere($qb->expr()->eq('config_key', $qb->createNamedParameter($key))); + $qb->executeStatement(); + + unset($this->cache[$app][$key]); + } + + /** + * @param string $app + * + * @inheritDoc + * @throws DBException + * @since 29.0.0 + */ + public function deleteApp(string $app): void { + $appId = $this->loadConfig($app); + //[$appId,] = $this->parseExtra($app); + + $sql = $this->connection->getQueryBuilder(); + $sql->delete('appconfig_lazy') + ->where($sql->expr()->eq('app_id', $sql->createNamedParameter($appId))); + $sql->executeStatement(); + + $this->clearCache($appId); + } + + /** + * @param string $app + * + * @return string[] + */ + private function parseExtra(string $app): array { + if (!strpos($app, self::APP_EXTRA_SEPARATOR)) { + return [$app, '']; + } + + return explode(self::APP_EXTRA_SEPARATOR, $app, 2); + } + + /** + * @param string $app + * @param bool $includeExtras + * + * @return string + */ + private function loadConfig(string $app, bool $includeExtras = false): string { + [$appId, $extra] = $this->parseExtra($app); + if ($this->isLoaded($appId, $extra)) { + return $appId; + } + + if (!array_key_exists($appId, $this->cache)) { + $this->cache[$appId] = []; + $this->extra[$appId] = []; + } + + $qb = $this->connection->getQueryBuilder(); + $qb->select('config_key', 'config_value') + ->from('appconfig_lazy') + ->where($qb->expr()->eq('app_id', $qb->createNamedParameter($appId))); + + if (!$includeExtras) { + $qb->andWhere($qb->expr()->eq('extra', $qb->createNamedParameter($extra))); + } + $result = $qb->execute(); + + $rows = $result->fetchAll(); + foreach ($rows as $row) { + $this->cache[$appId][$row['config_key']] = $row['config_value']; + $this->setLoadedStatus($appId, $extra); + } + $result->closeCursor(); + + return $appId; + } + + /** + * @throws DBException + */ + private function loadConfigAll(): void { + $sql = $this->connection->getQueryBuilder(); + $sql->select('app_id', 'extra', 'config_key', 'config_value') + ->from('appconfig_lazy'); + $result = $sql->execute(); + + $rows = $result->fetchAll(); + foreach ($rows as $row) { + $this->cache[$row['app_id']][$row['config_key']] = $row['config_value']; + $this->setLoadedStatus($row['app_id'], $row['extra']); + } + } + + + private function isLoaded(string $appId, string $extra): bool { + if (!array_key_exists($appId, $this->cache)) { + return false; + } + + if (!in_array($extra, $this->extra[$appId])) { + return false; + } + + return true; + } + + private function setLoadedStatus(string $appId, string $extra): void { + if (in_array($extra, $this->extra[$appId] ?? [])) { + return; + } + + $this->extra[$appId][] = $extra; + } + + + /** + * @inheritDoc + * @param string $app + * + * @since 29.0.0 + */ + public function clearCache(string $app = ''): void { + if ($app !== '') { + unset($this->cache[$app]); + unset($this->extra[$app]); + return; + } + + $this->cache = []; + } +} diff --git a/lib/private/Server.php b/lib/private/Server.php index cf4262e2d5043..a7d5b137e123b 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -218,6 +218,7 @@ use OCP\IGroupManager; use OCP\IInitialStateService; use OCP\IL10N; +use OCP\ILazyConfig; use OCP\ILogger; use OCP\INavigationManager; use OCP\IPhoneNumberUtil; @@ -626,6 +627,7 @@ public function __construct($webRoot, \OC\Config $config) { /** @deprecated 19.0.0 */ $this->registerDeprecatedAlias('AllConfig', \OC\AllConfig::class); $this->registerAlias(\OCP\IConfig::class, \OC\AllConfig::class); + $this->registerAlias(ILazyConfig::class, LazyConfig::class); $this->registerService(\OC\SystemConfig::class, function ($c) use ($config) { return new \OC\SystemConfig($config); diff --git a/lib/private/SystemConfig.php b/lib/private/SystemConfig.php index c104f00180916..559847d77785f 100644 --- a/lib/private/SystemConfig.php +++ b/lib/private/SystemConfig.php @@ -123,11 +123,7 @@ class SystemConfig { ], ]; - /** @var Config */ - private $config; - - public function __construct(Config $config) { - $this->config = $config; + public function __construct(private Config $config) { } /** diff --git a/lib/public/ILazyConfig.php b/lib/public/ILazyConfig.php new file mode 100644 index 0000000000000..cdbaff233cb66 --- /dev/null +++ b/lib/public/ILazyConfig.php @@ -0,0 +1,171 @@ + + * + * @author Maxence Lange + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ +namespace OCP; + +/** + * @since 29.0.0 + */ +interface ILazyConfig { + /** + * @param bool $loadValues + * + * @return array + * @since 29.0.0 + */ + public function getApps(bool $loadValues = true): array; + + /** + * @param string $app + * + * @return array + * @since 29.0.0 + */ + public function getKeys(string $app): array; + + /** + * @param string $app + * @param string $key + * + * @return bool + * @since 29.0.0 + */ + public function hasKey(string $app, string $key): bool; + + /** + * @param string $app + * @param string $key + * + * @return array + * @since 29.0.0 + */ + public function getValues(string $app, string $key = ''): array; + + /** + * @param string $app + * + * @return array + * @since 29.0.0 + */ + public function getFilteredValues(string $app): array; + + /** + * @param string $app + * @param string $key + * @param string $default + * + * @return string + * @since 29.0.0 + */ + public function getValueString(string $app, string $key, string $default = ''): string; + + /** + * @param string $app + * @param string $key + * @param int $default + * + * @return int + * @since 29.0.0 + */ + public function getValueInt(string $app, string $key, int $default = 0): int; + + /** + * @param string $app + * @param string $key + * @param bool $default + * + * @return bool + * @since 29.0.0 + */ + public function getValueBool(string $app, string $key, bool $default = false): bool; + + /** + * @param string $app + * @param string $key + * @param array $default + * + * @return array + * @since 29.0.0 + */ + public function getValueArray(string $app, string $key, array $default = []): array; + + /** + * @param string $app + * @param string $key + * @param string $value + * + * @return bool + * @since 29.0.0 + */ + public function setValueString(string $app, string $key, string $value): bool; + + /** + * @param string $app + * @param string $key + * @param int $value + * + * @return bool + * @since 29.0.0 + */ + public function setValueInt(string $app, string $key, int $value): bool; + + /** + * @param string $app + * @param string $key + * @param bool $value + * + * @return bool + * @since 29.0.0 + */ + public function setValueBool(string $app, string $key, bool $value): bool; + + /** + * @param string $app + * @param string $key + * @param array $value + * + * @return bool + * @since 29.0.0 + */ + public function setValueArray(string $app, string $key, array $value): bool; + + /** + * @param string $app + * @param string $key + * @since 29.0.0 + */ + public function unsetKey(string $app, string $key): void; + + /** + * @param string $app + * @since 29.0.0 + */ + public function deleteApp(string $app): void; + + /** + * @param string $app + * @since 29.0.0 + */ + public function clearCache(string $app = ''): void; +} diff --git a/tests/Core/Command/Config/ListConfigsTest.php b/tests/Core/Command/Config/ListConfigsTest.php index 01d5f51249480..a436fa342f2a6 100644 --- a/tests/Core/Command/Config/ListConfigsTest.php +++ b/tests/Core/Command/Config/ListConfigsTest.php @@ -25,6 +25,7 @@ use OC\SystemConfig; use OCP\IAppConfig; use OCP\IConfig; +use OCP\ILazyConfig; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Test\TestCase; @@ -52,12 +53,16 @@ protected function setUp(): void { $appConfig = $this->appConfig = $this->getMockBuilder(IAppConfig::class) ->disableOriginalConstructor() ->getMock(); + $lazyConfig = $this->lazyConfig = $this->getMockBuilder(ILazyConfig::class) + ->disableOriginalConstructor() + ->getMock(); $this->consoleInput = $this->getMockBuilder(InputInterface::class)->getMock(); $this->consoleOutput = $this->getMockBuilder(OutputInterface::class)->getMock(); /** @var \OC\SystemConfig $systemConfig */ /** @var \OCP\IAppConfig $appConfig */ - $this->command = new ListConfigs($systemConfig, $appConfig); + /** @var \OCP\ILazyConfig $lazyConfig */ + $this->command = new ListConfigs($systemConfig, $appConfig, $lazyConfig); } public function listData() {