diff --git a/ProcessMaker/Cache/CacheManagerBase.php b/ProcessMaker/Cache/CacheManagerBase.php new file mode 100644 index 0000000000..f7545504e3 --- /dev/null +++ b/ProcessMaker/Cache/CacheManagerBase.php @@ -0,0 +1,51 @@ +keys($prefix . '*'); + // Filter keys by pattern + return array_filter($keys, fn ($key) => preg_match('/' . $pattern . '/', $key)); + } catch (Exception $e) { + Log::info('CacheManagerBase: ' . $e->getMessage()); + } + + return []; + } +} diff --git a/ProcessMaker/Cache/CacheManagerException.php b/ProcessMaker/Cache/CacheManagerException.php new file mode 100644 index 0000000000..e0352c28f7 --- /dev/null +++ b/ProcessMaker/Cache/CacheManagerException.php @@ -0,0 +1,9 @@ +determineCacheDriver(); + $this->manager = $cacheManager; - $this->cacheManager = $cacheManager; - $this->cacheManager->store($driver); + $this->setCacheDriver(); } /** - * Determine the cache driver to use. + * Determine and set the cache driver to use. + * + * @param CacheManager $cacheManager * - * @return string + * @return void */ - private function determineCacheDriver(): string + private function setCacheDriver(): void { $defaultCache = config('cache.default'); - if (in_array($defaultCache, ['redis', 'cache_settings'])) { - return self::DEFAULT_CACHE_DRIVER; - } + $isAvailableConnection = in_array($defaultCache, self::AVAILABLE_CONNECTIONS); - return $defaultCache; + // Set the cache driver to use + $cacheDriver = $isAvailableConnection ? self::DEFAULT_CACHE_DRIVER : $defaultCache; + // Store the cache driver + $this->cacheManager = $this->manager->store($cacheDriver); } /** @@ -140,22 +146,19 @@ public function clear(): bool */ public function clearBy(string $pattern): void { - $defaultDriver = $this->cacheManager->getDefaultDriver(); + $defaultDriver = $this->manager->getDefaultDriver(); if ($defaultDriver !== 'cache_settings') { throw new SettingCacheException('The cache driver must be Redis.'); } try { - // get the connection name from the cache manager - $connection = $this->cacheManager->connection()->getName(); - // Get all keys - $keys = Redis::connection($connection)->keys($this->cacheManager->getPrefix() . '*'); + $prefix = $this->manager->getPrefix(); // Filter keys by pattern - $matchedKeys = array_filter($keys, fn ($key) => preg_match('/' . $pattern . '/', $key)); + $matchedKeys = $this->getKeysByPattern($pattern, $defaultDriver, $prefix); if (!empty($matchedKeys)) { - Redis::connection($connection)->del($matchedKeys); + Redis::connection($defaultDriver)->del($matchedKeys); } } catch (\Exception $e) { Log::error('SettingCacheException' . $e->getMessage()); diff --git a/ProcessMaker/Console/Commands/CacheSettingClear.php b/ProcessMaker/Console/Commands/CacheSettingClear.php new file mode 100644 index 0000000000..86afc59556 --- /dev/null +++ b/ProcessMaker/Console/Commands/CacheSettingClear.php @@ -0,0 +1,32 @@ +info('Settings cache cleared.'); + } +} diff --git a/tests/Feature/Cache/CacheManagerBaseTest.php b/tests/Feature/Cache/CacheManagerBaseTest.php new file mode 100644 index 0000000000..b051663f0b --- /dev/null +++ b/tests/Feature/Cache/CacheManagerBaseTest.php @@ -0,0 +1,108 @@ +set('cache.default', 'redis'); + } + + protected function tearDown(): void + { + config()->set('cache.default', 'array'); + + parent::tearDown(); + } + + public function testGetKeysByPatternWithValidConnectionAndMatchingKeys() + { + $this->cacheManagerBase = $this->getMockForAbstractClass(CacheManagerBase::class); + + $pattern = 'test-pattern'; + $prefix = config('cache.prefix'); + $keys = [$prefix . ':test-pattern:1', $prefix . ':test-pattern:2']; + + Redis::shouldReceive('connection') + ->with('redis') + ->andReturnSelf(); + + Redis::shouldReceive('keys') + ->with($prefix . '*') + ->andReturn($keys); + + $result = $this->cacheManagerBase->getKeysByPattern($pattern); + + $this->assertCount(2, $result); + $this->assertEquals($keys, $result); + } + + public function testGetKeysByPatternWithValidConnectionAndNoMatchingKeys() + { + $this->cacheManagerBase = $this->getMockForAbstractClass(CacheManagerBase::class); + + $pattern = 'non-matching-pattern'; + $prefix = config('cache.prefix'); + $keys = [$prefix . ':test-pattern:1', $prefix . ':test-pattern:2']; + + Redis::shouldReceive('connection') + ->with('redis') + ->andReturnSelf(); + + Redis::shouldReceive('keys') + ->with($prefix . '*') + ->andReturn($keys); + + $result = $this->cacheManagerBase->getKeysByPattern($pattern); + + $this->assertCount(0, $result); + } + + public function testGetKeysByPatternWithInvalidConnection() + { + config()->set('cache.default', 'array'); + + $this->cacheManagerBase = $this->getMockForAbstractClass(CacheManagerBase::class); + + $this->expectException(CacheManagerException::class); + $this->expectExceptionMessage('`getKeysByPattern` method only supports Redis connections.'); + + $this->cacheManagerBase->getKeysByPattern('pattern'); + } + + public function testGetKeysByPatternWithExceptionDuringKeyRetrieval() + { + $this->cacheManagerBase = $this->getMockForAbstractClass(CacheManagerBase::class); + + $pattern = 'test-pattern'; + $prefix = config('cache.prefix'); + + Redis::shouldReceive('connection') + ->with('redis') + ->andReturnSelf(); + + Redis::shouldReceive('keys') + ->with($prefix . '*') + ->andThrow(new Exception('Redis error')); + + Log::shouldReceive('info') + ->with('CacheManagerBase: ' . 'Redis error') + ->once(); + + $result = $this->cacheManagerBase->getKeysByPattern($pattern); + + $this->assertCount(0, $result); + } +} diff --git a/tests/Feature/Cache/SettingCacheTest.php b/tests/Feature/Cache/SettingCacheTest.php index 3016ab3867..2f34d50784 100644 --- a/tests/Feature/Cache/SettingCacheTest.php +++ b/tests/Feature/Cache/SettingCacheTest.php @@ -142,44 +142,44 @@ public function testGetSettingByNotExistingKey() public function testClearByPattern() { - \SettingCache::set('password-policies.users_can_change', 1); - \SettingCache::set('password-policies.numbers', 2); - \SettingCache::set('password-policies.uppercase', 3); + Cache::store('cache_settings')->put('password-policies.users_can_change', 1); + Cache::store('cache_settings')->put('password-policies.numbers', 2); + Cache::store('cache_settings')->put('password-policies.uppercase', 3); Cache::put('session-control.ip_restriction', 0); - $this->assertEquals(1, \SettingCache::get('password-policies.users_can_change')); - $this->assertEquals(2, \SettingCache::get('password-policies.numbers')); - $this->assertEquals(3, \SettingCache::get('password-policies.uppercase')); + $this->assertEquals(1, Cache::store('cache_settings')->get('password-policies.users_can_change')); + $this->assertEquals(2, Cache::store('cache_settings')->get('password-policies.numbers')); + $this->assertEquals(3, Cache::store('cache_settings')->get('password-policies.uppercase')); $pattern = 'password-policies'; \SettingCache::clearBy($pattern); - $this->assertNull(\SettingCache::get('password-policies.users_can_change')); - $this->assertNull(\SettingCache::get('password-policies.numbers')); - $this->assertNull(\SettingCache::get('password-policies.uppercase')); + $this->assertNull(Cache::store('cache_settings')->get('password-policies.users_can_change')); + $this->assertNull(Cache::store('cache_settings')->get('password-policies.numbers')); + $this->assertNull(Cache::store('cache_settings')->get('password-policies.uppercase')); } public function testClearByPatternRemainUnmatched() { - \SettingCache::set('session-control.ip_restriction', 0); - \SettingCache::set('password-policies.users_can_change', 1); - \SettingCache::set('password-policies.numbers', 2); - \SettingCache::set('password-policies.uppercase', 3); + Cache::store('cache_settings')->put('session-control.ip_restriction', 0); + Cache::store('cache_settings')->put('password-policies.users_can_change', 1); + Cache::store('cache_settings')->put('password-policies.numbers', 2); + Cache::store('cache_settings')->put('password-policies.uppercase', 3); - $this->assertEquals(0, \SettingCache::get('session-control.ip_restriction')); - $this->assertEquals(1, \SettingCache::get('password-policies.users_can_change')); - $this->assertEquals(2, \SettingCache::get('password-policies.numbers')); - $this->assertEquals(3, \SettingCache::get('password-policies.uppercase')); + $this->assertEquals(0, Cache::store('cache_settings')->get('session-control.ip_restriction')); + $this->assertEquals(1, Cache::store('cache_settings')->get('password-policies.users_can_change')); + $this->assertEquals(2, Cache::store('cache_settings')->get('password-policies.numbers')); + $this->assertEquals(3, Cache::store('cache_settings')->get('password-policies.uppercase')); $pattern = 'password-policies'; \SettingCache::clearBy($pattern); - $this->assertEquals(0, \SettingCache::get('session-control.ip_restriction')); - $this->assertNull(\SettingCache::get('password-policies.users_can_change')); - $this->assertNull(\SettingCache::get('password-policies.numbers')); - $this->assertNull(\SettingCache::get('password-policies.uppercase')); + $this->assertEquals(0, Cache::store('cache_settings')->get('session-control.ip_restriction')); + $this->assertNull(Cache::store('cache_settings')->get('password-policies.users_can_change')); + $this->assertNull(Cache::store('cache_settings')->get('password-policies.numbers')); + $this->assertNull(Cache::store('cache_settings')->get('password-policies.uppercase')); } public function testClearByPatternWithFailedDeletion() @@ -192,8 +192,13 @@ public function testClearByPatternWithFailedDeletion() \SettingCache::set('test_pattern:1', 1); \SettingCache::set('test_pattern:2', 2); + // Set up the expectation for the connection method + Redis::shouldReceive('connection') + ->with('cache_settings') + ->andReturnSelf(); + Redis::shouldReceive('keys') - ->with('*settings:*') + ->with('settings:*') ->andReturn($keys); Redis::shouldReceive('del') @@ -238,24 +243,19 @@ public function testClearOnlySettings() \SettingCache::set('password-policies.users_can_change', 1); \SettingCache::set('password-policies.numbers', 2); - config()->set('cache.default', 'array'); - Cache::put('password-policies.uppercase', 3); + Cache::store('file')->put('password-policies.uppercase', 3); - config()->set('cache.default', 'cache_settings'); $this->assertEquals(1, \SettingCache::get('password-policies.users_can_change')); $this->assertEquals(2, \SettingCache::get('password-policies.numbers')); - config()->set('cache.default', 'array'); - $this->assertEquals(3, Cache::get('password-policies.uppercase')); + $this->assertEquals(3, Cache::store('file')->get('password-policies.uppercase')); - config()->set('cache.default', 'cache_settings'); \SettingCache::clear(); $this->assertNull(\SettingCache::get('password-policies.users_can_change')); $this->assertNull(\SettingCache::get('password-policies.numbers')); - config()->set('cache.default', 'array'); - $this->assertEquals(3, Cache::get('password-policies.uppercase')); + $this->assertEquals(3, Cache::store('file')->get('password-policies.uppercase')); } public function testInvalidateOnSaved()