mirror of
https://github.com/nextcloud/server.git
synced 2026-02-20 00:12:30 -05:00
Merge pull request #54383 from nextcloud/feat/cache-app-config
feat(AppConfig): cache the config if local cache is available
This commit is contained in:
commit
6d5dd4b389
16 changed files with 1938 additions and 1726 deletions
|
|
@ -2980,9 +2980,6 @@
|
|||
<code><![CDATA[getAppValue]]></code>
|
||||
<code><![CDATA[setAppValue]]></code>
|
||||
</DeprecatedMethod>
|
||||
<NullArgument>
|
||||
<code><![CDATA[null]]></code>
|
||||
</NullArgument>
|
||||
</file>
|
||||
<file src="core/Command/Encryption/MigrateKeyStorage.php">
|
||||
<DeprecatedClass>
|
||||
|
|
@ -3301,11 +3298,6 @@
|
|||
<code><![CDATA[ActivitySettings[]]]></code>
|
||||
</MoreSpecificReturnType>
|
||||
</file>
|
||||
<file src="lib/private/AllConfig.php">
|
||||
<MoreSpecificImplementedParamType>
|
||||
<code><![CDATA[$key]]></code>
|
||||
</MoreSpecificImplementedParamType>
|
||||
</file>
|
||||
<file src="lib/private/App/DependencyAnalyzer.php">
|
||||
<InvalidNullableReturnType>
|
||||
<code><![CDATA[bool]]></code>
|
||||
|
|
@ -3314,11 +3306,6 @@
|
|||
<code><![CDATA[version_compare($first, $second, $operator)]]></code>
|
||||
</NullableReturnStatement>
|
||||
</file>
|
||||
<file src="lib/private/AppConfig.php">
|
||||
<NullableReturnStatement>
|
||||
<code><![CDATA[$this->fastCache[$app][$key] ?? $default]]></code>
|
||||
</NullableReturnStatement>
|
||||
</file>
|
||||
<file src="lib/private/AppFramework/Bootstrap/FunctionInjector.php">
|
||||
<UndefinedMethod>
|
||||
<code><![CDATA[getName]]></code>
|
||||
|
|
@ -4015,9 +4002,6 @@
|
|||
<code><![CDATA[false]]></code>
|
||||
<code><![CDATA[false]]></code>
|
||||
</InvalidArgument>
|
||||
<NullArgument>
|
||||
<code><![CDATA[null]]></code>
|
||||
</NullArgument>
|
||||
</file>
|
||||
<file src="lib/private/IntegrityCheck/Checker.php">
|
||||
<InvalidArrayAccess>
|
||||
|
|
@ -4394,9 +4378,6 @@
|
|||
<InvalidArgument>
|
||||
<code><![CDATA[$groupsList]]></code>
|
||||
</InvalidArgument>
|
||||
<NullArgument>
|
||||
<code><![CDATA[null]]></code>
|
||||
</NullArgument>
|
||||
</file>
|
||||
<file src="lib/private/legacy/OC_Helper.php">
|
||||
<InvalidArrayOffset>
|
||||
|
|
|
|||
|
|
@ -1792,6 +1792,15 @@ $CONFIG = [
|
|||
*/
|
||||
'cache_chunk_gc_ttl' => 60*60*24,
|
||||
|
||||
/**
|
||||
* Enable caching of the app config values.
|
||||
* If enabled the app config will be cached locally for a short TTL,
|
||||
* reducing database load significatly on larger setups.
|
||||
*
|
||||
* Defaults to ``true``
|
||||
*/
|
||||
'cache_app_config' => true,
|
||||
|
||||
/**
|
||||
* Using Object Store with Nextcloud
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@ class Enable extends Command {
|
|||
$output->writeln('<error>No encryption module is loaded</error>');
|
||||
return 1;
|
||||
}
|
||||
$defaultModule = $this->config->getAppValue('core', 'default_encryption_module', null);
|
||||
if ($defaultModule === null) {
|
||||
$defaultModule = $this->config->getAppValue('core', 'default_encryption_module');
|
||||
if ($defaultModule === '') {
|
||||
$output->writeln('<error>No default module is set</error>');
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -246,3 +246,16 @@ Cypress.Commands.add('userFileExists', (user: string, path: string) => {
|
|||
return cy.runCommand(`stat --printf="%s" "data/${user}/files/${path}"`, { failOnNonZeroExit: true })
|
||||
.then((exec) => Number.parseInt(exec.stdout || '0'))
|
||||
})
|
||||
|
||||
Cypress.Commands.add('runOccCommand', (command: string, options?: Partial<Cypress.ExecOptions>) => {
|
||||
return cy.runCommand(`php ./occ ${command}`, options)
|
||||
.then((context) =>
|
||||
// OCC cannot clear the APCu cache
|
||||
// eslint-disable-next-line cypress/no-unnecessary-waiting
|
||||
cy.wait(
|
||||
command.startsWith('app:') || command.startsWith('config:')
|
||||
? 3000 // clear APCu cache
|
||||
: 0,
|
||||
).then(() => context),
|
||||
)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -195,7 +195,7 @@ class AllConfig implements IConfig {
|
|||
* @deprecated 29.0.0 Use {@see IAppConfig} directly
|
||||
*/
|
||||
public function getAppValue($appName, $key, $default = '') {
|
||||
return \OC::$server->get(AppConfig::class)->getValue($appName, $key, $default);
|
||||
return \OC::$server->get(AppConfig::class)->getValue($appName, $key, $default) ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ use JsonException;
|
|||
use OC\AppFramework\Bootstrap\Coordinator;
|
||||
use OC\Config\ConfigManager;
|
||||
use OC\Config\PresetManager;
|
||||
use OC\Memcache\Factory as CacheFactory;
|
||||
use OCP\Config\Lexicon\Entry;
|
||||
use OCP\Config\Lexicon\ILexicon;
|
||||
use OCP\Config\Lexicon\Strictness;
|
||||
use OCP\Config\ValueType;
|
||||
use OCP\DB\Exception as DBException;
|
||||
|
|
@ -24,6 +24,8 @@ use OCP\Exceptions\AppConfigIncorrectTypeException;
|
|||
use OCP\Exceptions\AppConfigTypeConflictException;
|
||||
use OCP\Exceptions\AppConfigUnknownKeyException;
|
||||
use OCP\IAppConfig;
|
||||
use OCP\ICache;
|
||||
use OCP\ICacheFactory;
|
||||
use OCP\IConfig;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\Security\ICrypto;
|
||||
|
|
@ -53,10 +55,12 @@ class AppConfig implements IAppConfig {
|
|||
private const KEY_MAX_LENGTH = 64;
|
||||
private const ENCRYPTION_PREFIX = '$AppConfigEncryption$';
|
||||
private const ENCRYPTION_PREFIX_LENGTH = 21; // strlen(self::ENCRYPTION_PREFIX)
|
||||
private const LOCAL_CACHE_KEY = 'OC\\AppConfig';
|
||||
private const LOCAL_CACHE_TTL = 3;
|
||||
|
||||
/** @var array<string, array<string, mixed>> ['app_id' => ['config_key' => 'config_value']] */
|
||||
/** @var array<string, array<string, string>> ['app_id' => ['config_key' => 'config_value']] */
|
||||
private array $fastCache = []; // cache for normal config keys
|
||||
/** @var array<string, array<string, mixed>> ['app_id' => ['config_key' => 'config_value']] */
|
||||
/** @var array<string, array<string, string>> ['app_id' => ['config_key' => 'config_value']] */
|
||||
private array $lazyCache = []; // cache for lazy config keys
|
||||
/** @var array<string, array<string, int>> ['app_id' => ['config_key' => bitflag]] */
|
||||
private array $valueTypes = []; // type for all config values
|
||||
|
|
@ -67,6 +71,7 @@ class AppConfig implements IAppConfig {
|
|||
private bool $ignoreLexiconAliases = false;
|
||||
/** @var ?array<string, string> */
|
||||
private ?array $appVersionsCache = null;
|
||||
private ?ICache $localCache = null;
|
||||
|
||||
public function __construct(
|
||||
protected IDBConnection $connection,
|
||||
|
|
@ -75,7 +80,13 @@ class AppConfig implements IAppConfig {
|
|||
private readonly PresetManager $presetManager,
|
||||
protected LoggerInterface $logger,
|
||||
protected ICrypto $crypto,
|
||||
readonly CacheFactory $cacheFactory,
|
||||
) {
|
||||
if ($config->getSystemValueBool('cache_app_config', true) && $cacheFactory->isLocalCacheAvailable()) {
|
||||
$cacheFactory->withServerVersionPrefix(function (ICacheFactory $factory) {
|
||||
$this->localCache = $factory->createLocal();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -85,7 +96,7 @@ class AppConfig implements IAppConfig {
|
|||
* @since 7.0.0
|
||||
*/
|
||||
public function getApps(): array {
|
||||
$this->loadConfigAll();
|
||||
$this->loadConfig(lazy: true);
|
||||
$apps = array_merge(array_keys($this->fastCache), array_keys($this->lazyCache));
|
||||
sort($apps);
|
||||
|
||||
|
|
@ -103,7 +114,7 @@ class AppConfig implements IAppConfig {
|
|||
*/
|
||||
public function getKeys(string $app): array {
|
||||
$this->assertParams($app);
|
||||
$this->loadConfigAll($app);
|
||||
$this->loadConfig($app, true);
|
||||
$keys = array_merge(array_keys($this->fastCache[$app] ?? []), array_keys($this->lazyCache[$app] ?? []));
|
||||
sort($keys);
|
||||
|
||||
|
|
@ -149,19 +160,16 @@ class AppConfig implements IAppConfig {
|
|||
*/
|
||||
public function hasKey(string $app, string $key, ?bool $lazy = false): bool {
|
||||
$this->assertParams($app, $key);
|
||||
$this->loadConfig($app, $lazy);
|
||||
$this->loadConfig($app, $lazy ?? true);
|
||||
$this->matchAndApplyLexiconDefinition($app, $key);
|
||||
|
||||
$hasLazy = isset($this->lazyCache[$app][$key]);
|
||||
$hasFast = isset($this->fastCache[$app][$key]);
|
||||
if ($lazy === null) {
|
||||
$appCache = $this->getAllValues($app);
|
||||
return isset($appCache[$key]);
|
||||
return $hasLazy || $hasFast;
|
||||
} else {
|
||||
return $lazy ? $hasLazy : $hasFast;
|
||||
}
|
||||
|
||||
if ($lazy) {
|
||||
return isset($this->lazyCache[$app][$key]);
|
||||
}
|
||||
|
||||
return isset($this->fastCache[$app][$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -175,7 +183,7 @@ class AppConfig implements IAppConfig {
|
|||
*/
|
||||
public function isSensitive(string $app, string $key, ?bool $lazy = false): bool {
|
||||
$this->assertParams($app, $key);
|
||||
$this->loadConfig(null, $lazy);
|
||||
$this->loadConfig(null, $lazy ?? true);
|
||||
$this->matchAndApplyLexiconDefinition($app, $key);
|
||||
|
||||
if (!isset($this->valueTypes[$app][$key])) {
|
||||
|
|
@ -227,7 +235,7 @@ class AppConfig implements IAppConfig {
|
|||
public function getAllValues(string $app, string $prefix = '', bool $filtered = false): array {
|
||||
$this->assertParams($app, $prefix);
|
||||
// if we want to filter values, we need to get sensitivity
|
||||
$this->loadConfigAll($app);
|
||||
$this->loadConfig($app, true);
|
||||
// array_merge() will remove numeric keys (here config keys), so addition arrays instead
|
||||
$values = $this->formatAppValues($app, ($this->fastCache[$app] ?? []) + ($this->lazyCache[$app] ?? []));
|
||||
$values = array_filter(
|
||||
|
|
@ -479,7 +487,7 @@ class AppConfig implements IAppConfig {
|
|||
return $default;
|
||||
}
|
||||
|
||||
$this->loadConfig($app, $lazy);
|
||||
$this->loadConfig($app, $lazy ?? true);
|
||||
|
||||
/**
|
||||
* We ignore check if mixed type is requested.
|
||||
|
|
@ -551,7 +559,7 @@ class AppConfig implements IAppConfig {
|
|||
}
|
||||
|
||||
$this->assertParams($app, $key);
|
||||
$this->loadConfig($app, $lazy);
|
||||
$this->loadConfig($app, $lazy ?? true);
|
||||
|
||||
if (!isset($this->valueTypes[$app][$key])) {
|
||||
throw new AppConfigUnknownKeyException('unknown config key');
|
||||
|
|
@ -788,7 +796,7 @@ class AppConfig implements IAppConfig {
|
|||
if (!$this->matchAndApplyLexiconDefinition($app, $key, $lazy, $type)) {
|
||||
return false; // returns false as database is not updated
|
||||
}
|
||||
$this->loadConfig(null, $lazy);
|
||||
$this->loadConfig(null, $lazy ?? true);
|
||||
|
||||
$sensitive = $this->isTyped(self::VALUE_SENSITIVE, $type);
|
||||
$inserted = $refreshCache = false;
|
||||
|
|
@ -803,7 +811,7 @@ class AppConfig implements IAppConfig {
|
|||
* no update if key is already known with set lazy status and value is
|
||||
* not different, unless sensitivity is switched from false to true.
|
||||
*/
|
||||
if ($origValue === $this->getTypedValue($app, $key, $value, $lazy, $type)
|
||||
if ($origValue === $this->getTypedValue($app, $key, $value, $lazy ?? true, $type)
|
||||
&& (!$sensitive || $this->isSensitive($app, $key, $lazy))) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -835,7 +843,7 @@ class AppConfig implements IAppConfig {
|
|||
if (!$inserted) {
|
||||
$currType = $this->valueTypes[$app][$key] ?? 0;
|
||||
if ($currType === 0) { // this might happen when switching lazy loading status
|
||||
$this->loadConfigAll();
|
||||
$this->loadConfig(lazy: true);
|
||||
$currType = $this->valueTypes[$app][$key] ?? 0;
|
||||
}
|
||||
|
||||
|
|
@ -856,7 +864,7 @@ class AppConfig implements IAppConfig {
|
|||
&& ($type | self::VALUE_SENSITIVE) !== ($currType | self::VALUE_SENSITIVE)) {
|
||||
try {
|
||||
$currType = $this->convertTypeToString($currType);
|
||||
$type = $this->convertTypeToString($type);
|
||||
$this->convertTypeToString($type);
|
||||
} catch (AppConfigIncorrectTypeException) {
|
||||
// can be ignored, this was just needed for a better exception message.
|
||||
}
|
||||
|
|
@ -895,6 +903,7 @@ class AppConfig implements IAppConfig {
|
|||
$this->fastCache[$app][$key] = $value;
|
||||
}
|
||||
$this->valueTypes[$app][$key] = $type;
|
||||
$this->clearLocalCache();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -916,7 +925,7 @@ class AppConfig implements IAppConfig {
|
|||
*/
|
||||
public function updateType(string $app, string $key, int $type = self::VALUE_MIXED): bool {
|
||||
$this->assertParams($app, $key);
|
||||
$this->loadConfigAll();
|
||||
$this->loadConfig(lazy: true);
|
||||
$this->matchAndApplyLexiconDefinition($app, $key);
|
||||
$this->isLazy($app, $key); // confirm key exists
|
||||
|
||||
|
|
@ -959,7 +968,7 @@ class AppConfig implements IAppConfig {
|
|||
*/
|
||||
public function updateSensitive(string $app, string $key, bool $sensitive): bool {
|
||||
$this->assertParams($app, $key);
|
||||
$this->loadConfigAll();
|
||||
$this->loadConfig(lazy: true);
|
||||
$this->matchAndApplyLexiconDefinition($app, $key);
|
||||
|
||||
try {
|
||||
|
|
@ -1019,7 +1028,7 @@ class AppConfig implements IAppConfig {
|
|||
*/
|
||||
public function updateLazy(string $app, string $key, bool $lazy): bool {
|
||||
$this->assertParams($app, $key);
|
||||
$this->loadConfigAll();
|
||||
$this->loadConfig(lazy: true);
|
||||
$this->matchAndApplyLexiconDefinition($app, $key);
|
||||
|
||||
try {
|
||||
|
|
@ -1055,7 +1064,7 @@ class AppConfig implements IAppConfig {
|
|||
*/
|
||||
public function getDetails(string $app, string $key): array {
|
||||
$this->assertParams($app, $key);
|
||||
$this->loadConfigAll();
|
||||
$this->loadConfig(lazy: true);
|
||||
$this->matchAndApplyLexiconDefinition($app, $key);
|
||||
$lazy = $this->isLazy($app, $key);
|
||||
|
||||
|
|
@ -1198,6 +1207,7 @@ class AppConfig implements IAppConfig {
|
|||
unset($this->lazyCache[$app][$key]);
|
||||
unset($this->fastCache[$app][$key]);
|
||||
unset($this->valueTypes[$app][$key]);
|
||||
$this->clearLocalCache();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1227,12 +1237,13 @@ class AppConfig implements IAppConfig {
|
|||
public function clearCache(bool $reload = false): void {
|
||||
$this->lazyLoaded = $this->fastLoaded = false;
|
||||
$this->lazyCache = $this->fastCache = $this->valueTypes = $this->configLexiconDetails = [];
|
||||
$this->localCache?->remove(self::LOCAL_CACHE_KEY);
|
||||
|
||||
if (!$reload) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->loadConfigAll();
|
||||
$this->loadConfig(lazy: true);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1293,35 +1304,49 @@ class AppConfig implements IAppConfig {
|
|||
}
|
||||
}
|
||||
|
||||
private function loadConfigAll(?string $app = null): void {
|
||||
$this->loadConfig($app, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load normal config or config set as lazy loaded
|
||||
*
|
||||
* @param bool|null $lazy set to TRUE to load config set as lazy loaded, set to NULL to load all config
|
||||
* @param bool $lazy set to TRUE to also load config values set as lazy loaded
|
||||
*/
|
||||
private function loadConfig(?string $app = null, ?bool $lazy = false): void {
|
||||
private function loadConfig(?string $app = null, bool $lazy = false): void {
|
||||
if ($this->isLoaded($lazy)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if lazy is null or true, we debug log
|
||||
if (($lazy ?? true) !== false && $app !== null) {
|
||||
if ($lazy === true && $app !== null) {
|
||||
$exception = new \RuntimeException('The loading of lazy AppConfig values have been triggered by app "' . $app . '"');
|
||||
$this->logger->debug($exception->getMessage(), ['exception' => $exception, 'app' => $app]);
|
||||
}
|
||||
|
||||
$loadLazyOnly = $lazy && $this->isLoaded();
|
||||
|
||||
/** @var array<mixed> */
|
||||
$cacheContent = $this->localCache?->get(self::LOCAL_CACHE_KEY) ?? [];
|
||||
$includesLazyValues = !empty($cacheContent) && !empty($cacheContent['lazyCache']);
|
||||
if (!empty($cacheContent) && (!$lazy || $includesLazyValues)) {
|
||||
$this->valueTypes = $cacheContent['valueTypes'];
|
||||
$this->fastCache = $cacheContent['fastCache'];
|
||||
$this->fastLoaded = !empty($this->fastCache);
|
||||
if ($includesLazyValues) {
|
||||
$this->lazyCache = $cacheContent['lazyCache'];
|
||||
$this->lazyLoaded = !empty($this->lazyCache);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise no cache available and we need to fetch from database
|
||||
$qb = $this->connection->getQueryBuilder();
|
||||
$qb->from('appconfig');
|
||||
$qb->from('appconfig')
|
||||
->select('appid', 'configkey', 'configvalue', 'type');
|
||||
|
||||
// we only need value from lazy when loadConfig does not specify it
|
||||
$qb->select('appid', 'configkey', 'configvalue', 'type');
|
||||
|
||||
if ($lazy !== null) {
|
||||
$qb->where($qb->expr()->eq('lazy', $qb->createNamedParameter($lazy ? 1 : 0, IQueryBuilder::PARAM_INT)));
|
||||
if ($lazy === false) {
|
||||
$qb->where($qb->expr()->eq('lazy', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
|
||||
} else {
|
||||
if ($loadLazyOnly) {
|
||||
$qb->where($qb->expr()->eq('lazy', $qb->createNamedParameter(1, IQueryBuilder::PARAM_INT)));
|
||||
}
|
||||
$qb->addSelect('lazy');
|
||||
}
|
||||
|
||||
|
|
@ -1329,56 +1354,34 @@ class AppConfig implements IAppConfig {
|
|||
$rows = $result->fetchAll();
|
||||
foreach ($rows as $row) {
|
||||
// most of the time, 'lazy' is not in the select because its value is already known
|
||||
if (($row['lazy'] ?? ($lazy ?? 0) ? 1 : 0) === 1) {
|
||||
if ($lazy && ((int)$row['lazy']) === 1) {
|
||||
$this->lazyCache[$row['appid']][$row['configkey']] = $row['configvalue'] ?? '';
|
||||
} else {
|
||||
$this->fastCache[$row['appid']][$row['configkey']] = $row['configvalue'] ?? '';
|
||||
}
|
||||
$this->valueTypes[$row['appid']][$row['configkey']] = (int)($row['type'] ?? 0);
|
||||
}
|
||||
|
||||
$result->closeCursor();
|
||||
$this->setAsLoaded($lazy);
|
||||
$this->localCache?->set(
|
||||
self::LOCAL_CACHE_KEY,
|
||||
[
|
||||
'fastCache' => $this->fastCache,
|
||||
'lazyCache' => $this->lazyCache,
|
||||
'valueTypes' => $this->valueTypes,
|
||||
],
|
||||
self::LOCAL_CACHE_TTL,
|
||||
);
|
||||
|
||||
$this->fastLoaded = true;
|
||||
$this->lazyLoaded = $lazy;
|
||||
}
|
||||
|
||||
/**
|
||||
* if $lazy is:
|
||||
* - false: will returns true if fast config is loaded
|
||||
* - true : will returns true if lazy config is loaded
|
||||
* - null : will returns true if both config are loaded
|
||||
*
|
||||
* @param bool $lazy
|
||||
*
|
||||
* @return bool
|
||||
* @param bool $lazy - If set to true then also check if lazy values are loaded
|
||||
*/
|
||||
private function isLoaded(?bool $lazy): bool {
|
||||
if ($lazy === null) {
|
||||
return $this->lazyLoaded && $this->fastLoaded;
|
||||
}
|
||||
|
||||
return $lazy ? $this->lazyLoaded : $this->fastLoaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* if $lazy is:
|
||||
* - false: set fast config as loaded
|
||||
* - true : set lazy config as loaded
|
||||
* - null : set both config as loaded
|
||||
*
|
||||
* @param bool $lazy
|
||||
*/
|
||||
private function setAsLoaded(?bool $lazy): void {
|
||||
if ($lazy === null) {
|
||||
$this->fastLoaded = true;
|
||||
$this->lazyLoaded = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($lazy) {
|
||||
$this->lazyLoaded = true;
|
||||
} else {
|
||||
$this->fastLoaded = true;
|
||||
}
|
||||
private function isLoaded(bool $lazy = false): bool {
|
||||
return $this->fastLoaded && (!$lazy || $this->lazyLoaded);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1386,7 +1389,7 @@ class AppConfig implements IAppConfig {
|
|||
*
|
||||
* @param string $app app
|
||||
* @param string $key key
|
||||
* @param string $default = null, default value if the key does not exist
|
||||
* @param string $default - Default value if the key does not exist
|
||||
*
|
||||
* @return string the value or $default
|
||||
* @deprecated 29.0.0 use getValue*()
|
||||
|
|
@ -1394,7 +1397,7 @@ class AppConfig implements IAppConfig {
|
|||
* This function gets a value from the appconfig table. If the key does
|
||||
* not exist the default value will be returned
|
||||
*/
|
||||
public function getValue($app, $key, $default = null) {
|
||||
public function getValue($app, $key, $default = '') {
|
||||
$this->loadConfig($app);
|
||||
$this->matchAndApplyLexiconDefinition($app, $key);
|
||||
|
||||
|
|
@ -1421,7 +1424,7 @@ class AppConfig implements IAppConfig {
|
|||
* or enabled (lazy=lazy-2)
|
||||
*
|
||||
* this solution would remove the loading of config values from disabled app
|
||||
* unless calling the method {@see loadConfigAll()}
|
||||
* unless calling the method.
|
||||
*/
|
||||
return $this->setTypedValue($app, $key, (string)$value, false, self::VALUE_MIXED);
|
||||
}
|
||||
|
|
@ -1733,7 +1736,7 @@ class AppConfig implements IAppConfig {
|
|||
*
|
||||
* @return bool TRUE if conflict can be fully ignored, FALSE if action should be not performed
|
||||
* @throws AppConfigUnknownKeyException if strictness implies exception
|
||||
* @see ILexicon::getStrictness()
|
||||
* @see \OCP\Config\Lexicon\ILexicon::getStrictness()
|
||||
*/
|
||||
private function applyLexiconStrictness(
|
||||
?Strictness $strictness,
|
||||
|
|
@ -1772,8 +1775,9 @@ class AppConfig implements IAppConfig {
|
|||
$configLexicon = $bootstrapCoordinator->getRegistrationContext()?->getConfigLexicon($appId);
|
||||
foreach ($configLexicon?->getAppConfigs() ?? [] as $configEntry) {
|
||||
$entries[$configEntry->getKey()] = $configEntry;
|
||||
if ($configEntry->getRename() !== null) {
|
||||
$aliases[$configEntry->getRename()] = $configEntry->getKey();
|
||||
$newName = $configEntry->getRename();
|
||||
if ($newName !== null) {
|
||||
$aliases[$newName] = $configEntry->getKey();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1819,4 +1823,8 @@ class AppConfig implements IAppConfig {
|
|||
}
|
||||
return $this->appVersionsCache;
|
||||
}
|
||||
|
||||
private function clearLocalCache(): void {
|
||||
$this->localCache?->remove(self::LOCAL_CACHE_KEY);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -546,7 +546,7 @@ class Installer {
|
|||
while (false !== ($filename = readdir($dir))) {
|
||||
if ($filename[0] !== '.' and is_dir($app_dir['path'] . "/$filename")) {
|
||||
if (file_exists($app_dir['path'] . "/$filename/appinfo/info.xml")) {
|
||||
if ($config->getAppValue($filename, 'installed_version', null) === null) {
|
||||
if ($config->getAppValue($filename, 'installed_version') === '') {
|
||||
$enabled = $appManager->isDefaultEnabled($filename);
|
||||
if (($enabled || in_array($filename, $appManager->getAlwaysEnabledApps()))
|
||||
&& $config->getAppValue($filename, 'enabled') !== 'no') {
|
||||
|
|
|
|||
|
|
@ -7,72 +7,65 @@
|
|||
*/
|
||||
namespace OC\Memcache;
|
||||
|
||||
use Closure;
|
||||
use OC\SystemConfig;
|
||||
use OCP\Cache\CappedMemoryCache;
|
||||
use OCP\IAppConfig;
|
||||
use OCP\ICache;
|
||||
use OCP\ICacheFactory;
|
||||
use OCP\IMemcache;
|
||||
use OCP\Profiler\IProfiler;
|
||||
use OCP\ServerVersion;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class Factory implements ICacheFactory {
|
||||
public const NULL_CACHE = NullCache::class;
|
||||
|
||||
private ?string $globalPrefix = null;
|
||||
|
||||
private LoggerInterface $logger;
|
||||
|
||||
protected ?string $globalPrefix = null;
|
||||
/**
|
||||
* @var ?class-string<ICache> $localCacheClass
|
||||
* @var class-string<ICache> $localCacheClass
|
||||
*/
|
||||
private ?string $localCacheClass;
|
||||
protected string $localCacheClass;
|
||||
|
||||
/**
|
||||
* @var ?class-string<ICache> $distributedCacheClass
|
||||
* @var class-string<ICache> $distributedCacheClass
|
||||
*/
|
||||
private ?string $distributedCacheClass;
|
||||
protected string $distributedCacheClass;
|
||||
|
||||
/**
|
||||
* @var ?class-string<IMemcache> $lockingCacheClass
|
||||
* @var class-string<IMemcache> $lockingCacheClass
|
||||
*/
|
||||
private ?string $lockingCacheClass;
|
||||
|
||||
private string $logFile;
|
||||
|
||||
private IProfiler $profiler;
|
||||
protected string $lockingCacheClass;
|
||||
|
||||
/**
|
||||
* @param Closure $globalPrefixClosure
|
||||
* @param LoggerInterface $logger
|
||||
* @param ?class-string<ICache> $localCacheClass
|
||||
* @param ?class-string<ICache> $distributedCacheClass
|
||||
* @param ?class-string<IMemcache> $lockingCacheClass
|
||||
* @param string $logFile
|
||||
*/
|
||||
public function __construct(
|
||||
private Closure $globalPrefixClosure,
|
||||
LoggerInterface $logger,
|
||||
IProfiler $profiler,
|
||||
protected LoggerInterface $logger,
|
||||
protected IProfiler $profiler,
|
||||
protected ServerVersion $serverVersion,
|
||||
?string $localCacheClass = null,
|
||||
?string $distributedCacheClass = null,
|
||||
?string $lockingCacheClass = null,
|
||||
string $logFile = '',
|
||||
protected string $logFile = '',
|
||||
) {
|
||||
$this->logFile = $logFile;
|
||||
|
||||
if (!$localCacheClass) {
|
||||
$localCacheClass = self::NULL_CACHE;
|
||||
}
|
||||
$localCacheClass = ltrim($localCacheClass, '\\');
|
||||
|
||||
if (!$distributedCacheClass) {
|
||||
$distributedCacheClass = $localCacheClass;
|
||||
}
|
||||
|
||||
$distributedCacheClass = ltrim($distributedCacheClass, '\\');
|
||||
|
||||
$missingCacheMessage = 'Memcache {class} not available for {use} cache';
|
||||
$missingCacheHint = 'Is the matching PHP module installed and enabled?';
|
||||
if (!class_exists($localCacheClass) || !$localCacheClass::isAvailable()) {
|
||||
if (!class_exists($localCacheClass)
|
||||
|| !is_a($localCacheClass, ICache::class, true)
|
||||
|| !$localCacheClass::isAvailable()
|
||||
) {
|
||||
if (\OC::$CLI && !defined('PHPUNIT_RUN') && $localCacheClass === APCu::class) {
|
||||
// CLI should not fail if APCu is not available but fallback to NullCache.
|
||||
// This can be the case if APCu is used without apc.enable_cli=1.
|
||||
|
|
@ -84,7 +77,11 @@ class Factory implements ICacheFactory {
|
|||
]), $missingCacheHint);
|
||||
}
|
||||
}
|
||||
if (!class_exists($distributedCacheClass) || !$distributedCacheClass::isAvailable()) {
|
||||
|
||||
if (!class_exists($distributedCacheClass)
|
||||
|| !is_a($distributedCacheClass, ICache::class, true)
|
||||
|| !$distributedCacheClass::isAvailable()
|
||||
) {
|
||||
if (\OC::$CLI && !defined('PHPUNIT_RUN') && $distributedCacheClass === APCu::class) {
|
||||
// CLI should not fail if APCu is not available but fallback to NullCache.
|
||||
// This can be the case if APCu is used without apc.enable_cli=1.
|
||||
|
|
@ -96,25 +93,51 @@ class Factory implements ICacheFactory {
|
|||
]), $missingCacheHint);
|
||||
}
|
||||
}
|
||||
if (!($lockingCacheClass && class_exists($lockingCacheClass) && $lockingCacheClass::isAvailable())) {
|
||||
|
||||
if (!$lockingCacheClass
|
||||
|| !class_exists($lockingCacheClass)
|
||||
|| !is_a($lockingCacheClass, IMemcache::class, true)
|
||||
|| !$lockingCacheClass::isAvailable()
|
||||
) {
|
||||
// don't fall back since the fallback might not be suitable for storing lock
|
||||
$lockingCacheClass = self::NULL_CACHE;
|
||||
}
|
||||
/** @var class-string<IMemcache> */
|
||||
$lockingCacheClass = ltrim($lockingCacheClass, '\\');
|
||||
|
||||
$this->localCacheClass = $localCacheClass;
|
||||
$this->distributedCacheClass = $distributedCacheClass;
|
||||
$this->lockingCacheClass = $lockingCacheClass;
|
||||
$this->profiler = $profiler;
|
||||
}
|
||||
|
||||
private function getGlobalPrefix(): ?string {
|
||||
if (is_null($this->globalPrefix)) {
|
||||
$this->globalPrefix = ($this->globalPrefixClosure)();
|
||||
protected function getGlobalPrefix(): string {
|
||||
if ($this->globalPrefix === null) {
|
||||
$config = \OCP\Server::get(SystemConfig::class);
|
||||
$versions = [];
|
||||
if ($config->getValue('installed', false)) {
|
||||
$appConfig = \OCP\Server::get(IAppConfig::class);
|
||||
$versions = $appConfig->getAppInstalledVersions();
|
||||
}
|
||||
$versions['core'] = implode('.', $this->serverVersion->getVersion());
|
||||
$this->globalPrefix = hash('xxh128', implode(',', $versions));
|
||||
}
|
||||
return $this->globalPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the global prefix for a specific closure.
|
||||
* This should only be used internally for bootstrapping purpose!
|
||||
*
|
||||
* @param string $globalPrefix - The prefix to use during the closure execution
|
||||
* @param \Closure $closure - The closure with the cache factory as the first parameter
|
||||
*/
|
||||
public function withServerVersionPrefix(\Closure $closure): void {
|
||||
$backupPrefix = $this->globalPrefix;
|
||||
$this->globalPrefix = hash('xxh128', implode('.', $this->serverVersion->getVersion()));
|
||||
$closure($this);
|
||||
$this->globalPrefix = $backupPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* create a cache instance for storing locks
|
||||
*
|
||||
|
|
@ -122,22 +145,17 @@ class Factory implements ICacheFactory {
|
|||
* @return IMemcache
|
||||
*/
|
||||
public function createLocking(string $prefix = ''): IMemcache {
|
||||
$globalPrefix = $this->getGlobalPrefix();
|
||||
if (is_null($globalPrefix)) {
|
||||
return new ArrayCache($prefix);
|
||||
}
|
||||
$cache = new $this->lockingCacheClass($this->getGlobalPrefix() . '/' . $prefix);
|
||||
if ($this->lockingCacheClass === Redis::class) {
|
||||
if ($this->profiler->isEnabled()) {
|
||||
// We only support the profiler with Redis
|
||||
$cache = new ProfilerWrapperCache($cache, 'Locking');
|
||||
$this->profiler->add($cache);
|
||||
}
|
||||
|
||||
assert($this->lockingCacheClass !== null);
|
||||
$cache = new $this->lockingCacheClass($globalPrefix . '/' . $prefix);
|
||||
if ($this->lockingCacheClass === Redis::class && $this->profiler->isEnabled()) {
|
||||
// We only support the profiler with Redis
|
||||
$cache = new ProfilerWrapperCache($cache, 'Locking');
|
||||
$this->profiler->add($cache);
|
||||
}
|
||||
|
||||
if ($this->lockingCacheClass === Redis::class
|
||||
&& $this->logFile !== '' && is_writable(dirname($this->logFile)) && (!file_exists($this->logFile) || is_writable($this->logFile))) {
|
||||
$cache = new LoggerWrapperCache($cache, $this->logFile);
|
||||
if ($this->logFile !== '' && is_writable(dirname($this->logFile)) && (!file_exists($this->logFile) || is_writable($this->logFile))) {
|
||||
$cache = new LoggerWrapperCache($cache, $this->logFile);
|
||||
}
|
||||
}
|
||||
return $cache;
|
||||
}
|
||||
|
|
@ -149,22 +167,17 @@ class Factory implements ICacheFactory {
|
|||
* @return ICache
|
||||
*/
|
||||
public function createDistributed(string $prefix = ''): ICache {
|
||||
$globalPrefix = $this->getGlobalPrefix();
|
||||
if (is_null($globalPrefix)) {
|
||||
return new ArrayCache($prefix);
|
||||
}
|
||||
$cache = new $this->distributedCacheClass($this->getGlobalPrefix() . '/' . $prefix);
|
||||
if ($this->distributedCacheClass === Redis::class) {
|
||||
if ($this->profiler->isEnabled()) {
|
||||
// We only support the profiler with Redis
|
||||
$cache = new ProfilerWrapperCache($cache, 'Distributed');
|
||||
$this->profiler->add($cache);
|
||||
}
|
||||
|
||||
assert($this->distributedCacheClass !== null);
|
||||
$cache = new $this->distributedCacheClass($globalPrefix . '/' . $prefix);
|
||||
if ($this->distributedCacheClass === Redis::class && $this->profiler->isEnabled()) {
|
||||
// We only support the profiler with Redis
|
||||
$cache = new ProfilerWrapperCache($cache, 'Distributed');
|
||||
$this->profiler->add($cache);
|
||||
}
|
||||
|
||||
if ($this->distributedCacheClass === Redis::class && $this->logFile !== ''
|
||||
&& is_writable(dirname($this->logFile)) && (!file_exists($this->logFile) || is_writable($this->logFile))) {
|
||||
$cache = new LoggerWrapperCache($cache, $this->logFile);
|
||||
if ($this->logFile !== '' && is_writable(dirname($this->logFile)) && (!file_exists($this->logFile) || is_writable($this->logFile))) {
|
||||
$cache = new LoggerWrapperCache($cache, $this->logFile);
|
||||
}
|
||||
}
|
||||
return $cache;
|
||||
}
|
||||
|
|
@ -176,22 +189,17 @@ class Factory implements ICacheFactory {
|
|||
* @return ICache
|
||||
*/
|
||||
public function createLocal(string $prefix = ''): ICache {
|
||||
$globalPrefix = $this->getGlobalPrefix();
|
||||
if (is_null($globalPrefix)) {
|
||||
return new ArrayCache($prefix);
|
||||
}
|
||||
$cache = new $this->localCacheClass($this->getGlobalPrefix() . '/' . $prefix);
|
||||
if ($this->localCacheClass === Redis::class) {
|
||||
if ($this->profiler->isEnabled()) {
|
||||
// We only support the profiler with Redis
|
||||
$cache = new ProfilerWrapperCache($cache, 'Local');
|
||||
$this->profiler->add($cache);
|
||||
}
|
||||
|
||||
assert($this->localCacheClass !== null);
|
||||
$cache = new $this->localCacheClass($globalPrefix . '/' . $prefix);
|
||||
if ($this->localCacheClass === Redis::class && $this->profiler->isEnabled()) {
|
||||
// We only support the profiler with Redis
|
||||
$cache = new ProfilerWrapperCache($cache, 'Local');
|
||||
$this->profiler->add($cache);
|
||||
}
|
||||
|
||||
if ($this->localCacheClass === Redis::class && $this->logFile !== ''
|
||||
&& is_writable(dirname($this->logFile)) && (!file_exists($this->logFile) || is_writable($this->logFile))) {
|
||||
$cache = new LoggerWrapperCache($cache, $this->logFile);
|
||||
if ($this->logFile !== '' && is_writable(dirname($this->logFile)) && (!file_exists($this->logFile) || is_writable($this->logFile))) {
|
||||
$cache = new LoggerWrapperCache($cache, $this->logFile);
|
||||
}
|
||||
}
|
||||
return $cache;
|
||||
}
|
||||
|
|
@ -217,4 +225,11 @@ class Factory implements ICacheFactory {
|
|||
public function isLocalCacheAvailable(): bool {
|
||||
return $this->localCacheClass !== self::NULL_CACHE;
|
||||
}
|
||||
|
||||
public function clearAll(): void {
|
||||
$this->createLocal()->clear();
|
||||
$this->createDistributed()->clear();
|
||||
$this->createLocking()->clear();
|
||||
$this->createInMemory()->clear();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -585,62 +585,37 @@ class Server extends ServerContainer implements IServerContainer {
|
|||
|
||||
$this->registerAlias(IURLGenerator::class, URLGenerator::class);
|
||||
|
||||
$this->registerService(ICache::class, function ($c) {
|
||||
return new Cache\File();
|
||||
});
|
||||
|
||||
$this->registerAlias(ICache::class, Cache\File::class);
|
||||
$this->registerService(Factory::class, function (Server $c) {
|
||||
$profiler = $c->get(IProfiler::class);
|
||||
$arrayCacheFactory = new \OC\Memcache\Factory(fn () => '', $c->get(LoggerInterface::class),
|
||||
$profiler,
|
||||
ArrayCache::class,
|
||||
ArrayCache::class,
|
||||
ArrayCache::class
|
||||
);
|
||||
$logger = $c->get(LoggerInterface::class);
|
||||
$serverVersion = $c->get(ServerVersion::class);
|
||||
/** @var SystemConfig $config */
|
||||
$config = $c->get(SystemConfig::class);
|
||||
/** @var ServerVersion $serverVersion */
|
||||
$serverVersion = $c->get(ServerVersion::class);
|
||||
|
||||
if ($config->getValue('installed', false) && !(defined('PHPUNIT_RUN') && PHPUNIT_RUN)) {
|
||||
$logQuery = $config->getValue('log_query');
|
||||
$prefixClosure = function () use ($logQuery, $serverVersion): ?string {
|
||||
if (!$logQuery) {
|
||||
try {
|
||||
$v = \OCP\Server::get(IAppConfig::class)->getAppInstalledVersions(true);
|
||||
} catch (\Doctrine\DBAL\Exception $e) {
|
||||
// Database service probably unavailable
|
||||
// Probably related to https://github.com/nextcloud/server/issues/37424
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// If the log_query is enabled, we can not get the app versions
|
||||
// as that does a query, which will be logged and the logging
|
||||
// depends on redis and here we are back again in the same function.
|
||||
$v = [
|
||||
'log_query' => 'enabled',
|
||||
];
|
||||
}
|
||||
$v['core'] = implode(',', $serverVersion->getVersion());
|
||||
$version = implode(',', array_keys($v)) . implode(',', $v);
|
||||
$instanceId = \OC_Util::getInstanceId();
|
||||
$path = \OC::$SERVERROOT;
|
||||
return md5($instanceId . '-' . $version . '-' . $path);
|
||||
};
|
||||
return new \OC\Memcache\Factory($prefixClosure,
|
||||
$c->get(LoggerInterface::class),
|
||||
if (!$config->getValue('installed', false) || (defined('PHPUNIT_RUN') && PHPUNIT_RUN)) {
|
||||
return new \OC\Memcache\Factory(
|
||||
$logger,
|
||||
$profiler,
|
||||
/** @psalm-taint-escape callable */
|
||||
$config->getValue('memcache.local', null),
|
||||
/** @psalm-taint-escape callable */
|
||||
$config->getValue('memcache.distributed', null),
|
||||
/** @psalm-taint-escape callable */
|
||||
$config->getValue('memcache.locking', null),
|
||||
/** @psalm-taint-escape callable */
|
||||
$config->getValue('redis_log_file')
|
||||
$serverVersion,
|
||||
ArrayCache::class,
|
||||
ArrayCache::class,
|
||||
ArrayCache::class
|
||||
);
|
||||
}
|
||||
return $arrayCacheFactory;
|
||||
|
||||
return new \OC\Memcache\Factory(
|
||||
$logger,
|
||||
$profiler,
|
||||
$serverVersion,
|
||||
/** @psalm-taint-escape callable */
|
||||
$config->getValue('memcache.local', null),
|
||||
/** @psalm-taint-escape callable */
|
||||
$config->getValue('memcache.distributed', null),
|
||||
/** @psalm-taint-escape callable */
|
||||
$config->getValue('memcache.locking', null),
|
||||
/** @psalm-taint-escape callable */
|
||||
$config->getValue('redis_log_file')
|
||||
);
|
||||
});
|
||||
$this->registerAlias(ICacheFactory::class, Factory::class);
|
||||
|
||||
|
|
|
|||
|
|
@ -685,7 +685,7 @@ class OC_App {
|
|||
//set remote/public handlers
|
||||
if (array_key_exists('ocsid', $appData)) {
|
||||
\OC::$server->getConfig()->setAppValue($appId, 'ocsid', $appData['ocsid']);
|
||||
} elseif (\OC::$server->getConfig()->getAppValue($appId, 'ocsid', null) !== null) {
|
||||
} elseif (\OC::$server->getConfig()->getAppValue($appId, 'ocsid') !== '') {
|
||||
\OC::$server->getConfig()->deleteAppValue($appId, 'ocsid');
|
||||
}
|
||||
foreach ($appData['remote'] as $name => $path) {
|
||||
|
|
|
|||
|
|
@ -112,8 +112,8 @@ interface IConfig {
|
|||
* Writes a new app wide value
|
||||
*
|
||||
* @param string $appName the appName that we want to store the value under
|
||||
* @param string|float|int $key the key of the value, under which will be saved
|
||||
* @param string $value the value that should be stored
|
||||
* @param string $key the key of the value, under which will be saved
|
||||
* @param string|float|int $value the value that should be stored
|
||||
* @return void
|
||||
* @since 6.0.0
|
||||
* @deprecated 29.0.0 Use {@see IAppConfig} directly
|
||||
|
|
|
|||
|
|
@ -48,9 +48,9 @@ class EnableTest extends TestCase {
|
|||
|
||||
public static function dataEnable(): array {
|
||||
return [
|
||||
['no', null, [], true, 'Encryption enabled', 'No encryption module is loaded'],
|
||||
['yes', null, [], false, 'Encryption is already enabled', 'No encryption module is loaded'],
|
||||
['no', null, ['OC_TEST_MODULE' => []], true, 'Encryption enabled', 'No default module is set'],
|
||||
['no', '', [], true, 'Encryption enabled', 'No encryption module is loaded'],
|
||||
['yes', '', [], false, 'Encryption is already enabled', 'No encryption module is loaded'],
|
||||
['no', '', ['OC_TEST_MODULE' => []], true, 'Encryption enabled', 'No default module is set'],
|
||||
['no', 'OC_NO_MODULE', ['OC_TEST_MODULE' => []], true, 'Encryption enabled', 'The current default module does not exist: OC_NO_MODULE'],
|
||||
['no', 'OC_TEST_MODULE', ['OC_TEST_MODULE' => []], true, 'Encryption enabled', 'Default module: OC_TEST_MODULE'],
|
||||
];
|
||||
|
|
@ -79,7 +79,7 @@ class EnableTest extends TestCase {
|
|||
->method('getAppValue')
|
||||
->willReturnMap([
|
||||
['core', 'encryption_enabled', 'no', $oldStatus],
|
||||
['core', 'default_encryption_module', null, $defaultModule],
|
||||
['core', 'default_encryption_module', '', $defaultModule],
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -233,28 +233,25 @@ class AppManagerTest extends TestCase {
|
|||
$this->manager->disableApp('files_trashbin');
|
||||
}
|
||||
$this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new AppEnableEvent('files_trashbin'));
|
||||
|
||||
$this->manager->enableApp('files_trashbin');
|
||||
$this->assertEquals('yes', $this->appConfig->getValue('files_trashbin', 'enabled', 'no'));
|
||||
}
|
||||
|
||||
public function testDisableApp(): void {
|
||||
$this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new AppDisableEvent('files_trashbin'));
|
||||
|
||||
$this->manager->disableApp('files_trashbin');
|
||||
$this->assertEquals('no', $this->appConfig->getValue('files_trashbin', 'enabled', 'no'));
|
||||
}
|
||||
|
||||
public function testNotEnableIfNotInstalled(): void {
|
||||
try {
|
||||
$this->manager->enableApp('some_random_name_which_i_hope_is_not_an_app');
|
||||
$this->assertFalse(true, 'If this line is reached the expected exception is not thrown.');
|
||||
} catch (AppPathNotFoundException $e) {
|
||||
// Exception is expected
|
||||
$this->assertEquals('Could not find path for some_random_name_which_i_hope_is_not_an_app', $e->getMessage());
|
||||
}
|
||||
$this->expectException(AppPathNotFoundException::class);
|
||||
$this->expectExceptionMessage('Could not find path for some_random_name_which_i_hope_is_not_an_app');
|
||||
$this->appConfig->expects(self::never())
|
||||
->method('setValue');
|
||||
|
||||
$this->assertEquals('no', $this->appConfig->getValue(
|
||||
'some_random_name_which_i_hope_is_not_an_app', 'enabled', 'no'
|
||||
));
|
||||
$this->manager->enableApp('some_random_name_which_i_hope_is_not_an_app');
|
||||
}
|
||||
|
||||
public function testEnableAppForGroups(): void {
|
||||
|
|
@ -289,7 +286,9 @@ class AppManagerTest extends TestCase {
|
|||
->with('test')
|
||||
->willReturn('apps/test');
|
||||
|
||||
$this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new AppEnableEvent('test', ['group1', 'group2']));
|
||||
$this->eventDispatcher->expects($this->once())
|
||||
->method('dispatchTyped')
|
||||
->with(new AppEnableEvent('test', ['group1', 'group2']));
|
||||
|
||||
$manager->enableAppForGroups('test', $groups);
|
||||
$this->assertEquals('["group1","group2"]', $this->appConfig->getValue('test', 'enabled', 'no'));
|
||||
|
|
|
|||
1515
tests/lib/AppConfigIntegrationTest.php
Normal file
1515
tests/lib/AppConfigIntegrationTest.php
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -12,6 +12,7 @@ use OC\Memcache\Factory;
|
|||
use OC\Memcache\NullCache;
|
||||
use OCP\HintException;
|
||||
use OCP\Profiler\IProfiler;
|
||||
use OCP\ServerVersion;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class Test_Factory_Available_Cache1 extends NullCache {
|
||||
|
|
@ -111,7 +112,8 @@ class FactoryTest extends \Test\TestCase {
|
|||
$expectedLocalCache, $expectedDistributedCache, $expectedLockingCache): void {
|
||||
$logger = $this->getMockBuilder(LoggerInterface::class)->getMock();
|
||||
$profiler = $this->getMockBuilder(IProfiler::class)->getMock();
|
||||
$factory = new Factory(fn () => 'abc', $logger, $profiler, $localCache, $distributedCache, $lockingCache);
|
||||
$serverVersion = $this->createMock(ServerVersion::class);
|
||||
$factory = new Factory($logger, $profiler, $serverVersion, $localCache, $distributedCache, $lockingCache);
|
||||
$this->assertTrue(is_a($factory->createLocal(), $expectedLocalCache));
|
||||
$this->assertTrue(is_a($factory->createDistributed(), $expectedDistributedCache));
|
||||
$this->assertTrue(is_a($factory->createLocking(), $expectedLockingCache));
|
||||
|
|
@ -123,13 +125,15 @@ class FactoryTest extends \Test\TestCase {
|
|||
|
||||
$logger = $this->getMockBuilder(LoggerInterface::class)->getMock();
|
||||
$profiler = $this->getMockBuilder(IProfiler::class)->getMock();
|
||||
new Factory(fn () => 'abc', $logger, $profiler, $localCache, $distributedCache);
|
||||
$serverVersion = $this->createMock(ServerVersion::class);
|
||||
new Factory($logger, $profiler, $serverVersion, $localCache, $distributedCache);
|
||||
}
|
||||
|
||||
public function testCreateInMemory(): void {
|
||||
$logger = $this->getMockBuilder(LoggerInterface::class)->getMock();
|
||||
$profiler = $this->getMockBuilder(IProfiler::class)->getMock();
|
||||
$factory = new Factory(fn () => 'abc', $logger, $profiler, null, null, null);
|
||||
$serverVersion = $this->createMock(ServerVersion::class);
|
||||
$factory = new Factory($logger, $profiler, $serverVersion, null, null, null);
|
||||
|
||||
$cache = $factory->createInMemory();
|
||||
$cache->set('test', 48);
|
||||
|
|
|
|||
Loading…
Reference in a new issue