From 15d52ed73c7e8a6010218917fc1f3839c2415479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 13 Aug 2026 20:54:39 +0200 Subject: [PATCH] Remove the Proxy cache adapter Cache\Adapter\Proxy hard-codes one deployment's routing into the library: an app domain, a set of network domains, and a list of adapters for everything else. Any consumer whose split is not exactly that cannot use it, and a consumer whose split is that has encoded its topology in a dependency it does not control. #4 has since established that it cannot route key purges at all, since keys are scoped to a service or zone, and told consumers to select the adapter themselves before constructing Cache. That is the same conclusion for every operation, not only keys. Nothing uses it. Its only references were its own test and the README. Certificates\Provider\Proxy is untouched; this removes the cache adapter only. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 27 --------- src/Cdn/Cache/Adapter/Proxy.php | 78 ------------------------- tests/Cdn/Cache/Adapter/ProxyTest.php | 83 --------------------------- 3 files changed, 188 deletions(-) delete mode 100644 src/Cdn/Cache/Adapter/Proxy.php delete mode 100644 tests/Cdn/Cache/Adapter/ProxyTest.php diff --git a/README.md b/README.md index 228c1a8..c11ff13 100644 --- a/README.md +++ b/README.md @@ -78,33 +78,6 @@ $cache->purgeKeys([ Fastly domain purges invalidate the entire configured service. Use one domain per Fastly service when calling `purgeDomain()`. Cloudflare hostname purging depends on the cache-purge features enabled for your plan. -### Cache routing - -`Cache\Adapter\Proxy` routes domain and path purges: it sends the application domain to one adapter, configured network domains to another, and fans custom domains out to every custom adapter. - -```php -use Utopia\Cdn\Cache\Adapter\Proxy; - -$cache = new Cache(new Proxy( - appDomain: 'app.example.com', - appDomainAdapter: $cloudflareCache, - networkAdapter: $fastlyCache, - customDomainAdapters: [$cloudflareCache, $fastlyCache], - networkDomains: ['network.example.com'], -)); -``` - -Cache keys and tags are scoped to a Fastly service or Cloudflare zone. Consequently, `Proxy` does not route `purgeKeys()` calls. The consuming application must select the adapter using its own routing context before constructing `Cache`: - -```php -$adapter = $cdnAdapterResolver->resolve($rule); - -$cache = new Cache($adapter); -$cache->purgeKeys([ - 'domain-' . \strtolower($domain), -]); -``` - ## Certificates The current certificate provider support is focused on CDN-managed certificates through Fastly TLS subscriptions. diff --git a/src/Cdn/Cache/Adapter/Proxy.php b/src/Cdn/Cache/Adapter/Proxy.php deleted file mode 100644 index 27def0f..0000000 --- a/src/Cdn/Cache/Adapter/Proxy.php +++ /dev/null @@ -1,78 +0,0 @@ - $customDomainAdapters - * @param array $networkDomains - */ - public function __construct( - private string $appDomain, - private Adapter $appDomainAdapter, - private Adapter $networkAdapter, - private array $customDomainAdapters, - private array $networkDomains = [], - ) { - $this->appDomain = Domain::validate($this->appDomain); - $this->networkDomains = \array_map([Domain::class, 'validate'], $this->networkDomains); - } - - public function purgePaths(string $domain, array $paths): void - { - $domain = Domain::validate($domain); - $paths = Domain::validatePaths($paths); - - if ($paths === []) { - return; - } - - foreach ($this->select($domain) as $adapter) { - $adapter->purgePaths($domain, $paths); - } - } - - public function purgeDomain(string $domain): void - { - $domain = Domain::validate($domain); - - foreach ($this->select($domain) as $adapter) { - $adapter->purgeDomain($domain); - } - } - - public function purgeKeys(array $keys): void - { - if ($keys === []) { - return; - } - - throw new UnsupportedOperation( - 'Cache key purging cannot be routed by domain. Select the service or zone adapter before purging keys.' - ); - } - - /** @return array */ - private function select(string $domain): array - { - if ($domain === $this->appDomain) { - return [$this->appDomainAdapter]; - } - - if (\in_array($domain, $this->networkDomains, true)) { - return [$this->networkAdapter]; - } - - if ($this->customDomainAdapters === []) { - throw new Configuration('No cache adapters are configured for custom domains.'); - } - - return $this->customDomainAdapters; - } -} diff --git a/tests/Cdn/Cache/Adapter/ProxyTest.php b/tests/Cdn/Cache/Adapter/ProxyTest.php deleted file mode 100644 index dde0e07..0000000 --- a/tests/Cdn/Cache/Adapter/ProxyTest.php +++ /dev/null @@ -1,83 +0,0 @@ -adapter('app', $calls); - $network = $this->adapter('network', $calls); - $customA = $this->adapter('custom-a', $calls); - $customB = $this->adapter('custom-b', $calls); - $proxy = new Proxy('app.example.com', $app, $network, [$customA, $customB], ['network.example.com']); - - $proxy->purgeDomain('app.example.com'); - $proxy->purgePaths('network.example.com', ['/a']); - $proxy->purgeDomain('customer.example.com'); - - $this->assertSame(['app:domain', 'network:paths', 'custom-a:domain', 'custom-b:domain'], $calls->getArrayCopy()); - } - - public function testRejectsMissingCustomAdapters(): void - { - $adapter = $this->adapter('app', new \ArrayObject()); - $proxy = new Proxy('app.example.com', $adapter, $adapter, []); - $this->expectException(Configuration::class); - $proxy->purgeDomain('custom.example.com'); - } - - public function testRejectsKeyPurgeWithoutServiceOrZoneSelection(): void - { - $calls = new \ArrayObject(); - $app = $this->adapter('app', $calls); - $network = $this->adapter('network', $calls); - $custom = $this->adapter('custom', $calls); - $proxy = new Proxy('app.example.com', $app, $network, [$custom]); - - $this->expectException(UnsupportedOperation::class); - $this->expectExceptionMessage('Select the service or zone adapter'); - $proxy->purgeKeys(['key']); - } - - public function testEmptyKeyPurgeIsANoOp(): void - { - $calls = new \ArrayObject(); - $adapter = $this->adapter('adapter', $calls); - $proxy = new Proxy('app.example.com', $adapter, $adapter, [$adapter]); - - $proxy->purgeKeys([]); - - $this->assertSame([], $calls->getArrayCopy()); - } - - /** @param \ArrayObject $calls */ - private function adapter(string $name, \ArrayObject $calls): Adapter - { - return new class ($name, $calls) implements Adapter { - /** @param \ArrayObject $calls */ - public function __construct(private string $name, private \ArrayObject $calls) - { - } - public function purgePaths(string $domain, array $paths): void - { - $this->calls->append($this->name . ':paths'); - } - public function purgeDomain(string $domain): void - { - $this->calls->append($this->name . ':domain'); - } - public function purgeKeys(array $keys): void - { - $this->calls->append($this->name . ':keys'); - } - }; - } -}