diff --git a/composer.lock b/composer.lock index 1449590f7f..bb386b8daa 100644 --- a/composer.lock +++ b/composer.lock @@ -5383,16 +5383,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "1.5.7", + "version": "1.5.8", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "dc6720ba92ed98e2c62b2a319d4371f167ccc808" + "reference": "05367bc4a4c3e020e9aca114ae875b626ce8fc55" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/dc6720ba92ed98e2c62b2a319d4371f167ccc808", - "reference": "dc6720ba92ed98e2c62b2a319d4371f167ccc808", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/05367bc4a4c3e020e9aca114ae875b626ce8fc55", + "reference": "05367bc4a4c3e020e9aca114ae875b626ce8fc55", "shasum": "" }, "require": { @@ -5428,9 +5428,9 @@ "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "support": { "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/1.5.7" + "source": "https://github.com/appwrite/sdk-generator/tree/1.5.8" }, - "time": "2025-11-18T05:57:01+00:00" + "time": "2025-11-20T11:00:34+00:00" }, { "name": "doctrine/annotations", diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php index 1d97b01b1c..99f92271a2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php @@ -72,7 +72,7 @@ class XList extends Action ->param('transactionId', null, new Nullable(new UID()), 'Transaction ID to read uncommitted changes within the transaction.', true) ->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true) ->param('useCache', false, new Boolean(true), 'Opt-in to cached responses for select queries. Disabled by default.', true) - ->param('ttl', 60, new Range(min: 1, max: 86400), 'TTL (seconds) for cached respnses when caching is enabled. Must be between 1 and 86400 (24 hours).', true) + ->param('ttl', 30, new Range(min: 1, max: 86400), 'TTL (seconds) for cached respnses when caching is enabled. Must be between 1 and 86400 (24 hours).', true) ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') @@ -144,7 +144,7 @@ class XList extends Action foreach ($queries as $query) { $serializedQueries[] = $query instanceof Query ? $query->toArray() : $query; } - + $hostname = $dbForProject->getAdapter()->getHostname(); $cacheKeyBase = \sprintf( '%s-cache-%s:%s:%s:collection:%s:%s', @@ -155,13 +155,14 @@ class XList extends Action $collectionId, \md5(\json_encode($serializedQueries)) ); - + $documentsCacheKey = $cacheKeyBase . ':documents'; $totalCacheKey = $cacheKeyBase . ':total'; $documentsCacheHit = $totalDocumentsCacheHit = false; $cachedDocuments = $dbForProject->cache->load($documentsCacheKey, $ttl); + if ($cachedDocuments !== null && $cachedDocuments !== false && \is_array($cachedDocuments)) { @@ -179,9 +180,6 @@ class XList extends Action $dbForProject->cache->save($documentsCacheKey, $documentsArray); } - $response->addHeader('X-Appwrite-Cache-Documents', $documentsCacheHit ? 'hit' : 'miss'); - - if ($includeTotal) { $cachedTotal = $dbForProject->cache->load($totalCacheKey, $ttl); if ($cachedTotal !== null && $cachedTotal !== false) { @@ -195,7 +193,10 @@ class XList extends Action $total = 0; } - $response->addHeader('X-Appwrite-Cache-Documents-Total', $totalDocumentsCacheHit ? 'hit' : 'miss'); + $response + ->addHeader('X-Appwrite-Cache-Documents-Total', $totalDocumentsCacheHit ? 'hit' : 'miss') + ->addHeader('X-Appwrite-Cache-Documents', $documentsCacheHit ? 'hit' : 'miss') + ->addHeader('X-Appwrite-Cache-Documents-Ttl', $ttl); } else { // has selects, allow relationship on documents diff --git a/tests/e2e/Services/Databases/Legacy/DatabasesBase.php b/tests/e2e/Services/Databases/Legacy/DatabasesBase.php index 45356604de..dd68d5cce6 100644 --- a/tests/e2e/Services/Databases/Legacy/DatabasesBase.php +++ b/tests/e2e/Services/Databases/Legacy/DatabasesBase.php @@ -2382,7 +2382,7 @@ trait DatabasesBase { $databaseId = $data['databaseId']; - // useCache=true + select queries + // 1. Using cache with select queries , first request should miss cache. $documents1 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -2392,7 +2392,7 @@ trait DatabasesBase Query::orderAsc('releaseYear')->toString(), ], 'useCache' => true, - 'ttl' => 60, + 'ttl' => 30, ]); $this->assertEquals(200, $documents1['headers']['status-code']); @@ -2400,18 +2400,15 @@ trait DatabasesBase $this->assertEquals(1944, $documents1['body']['documents'][0]['releaseYear']); $this->assertEquals(2017, $documents1['body']['documents'][1]['releaseYear']); $this->assertEquals(2019, $documents1['body']['documents'][2]['releaseYear']); - $this->assertArrayHasKey('title', $documents1['body']['documents'][0]); $this->assertArrayHasKey('releaseYear', $documents1['body']['documents'][0]); $this->assertArrayHasKey('$id', $documents1['body']['documents'][0]); - - // First request should be MISS $this->assertArrayHasKey('x-appwrite-cache-documents', $documents1['headers']); $this->assertEquals('miss', $documents1['headers']['x-appwrite-cache-documents']); $this->assertArrayHasKey('x-appwrite-cache-documents-total', $documents1['headers']); $this->assertEquals('miss', $documents1['headers']['x-appwrite-cache-documents-total']); - // Should return cached results + // 2. Using cache with same select queries , should return cached results. $documents2 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -2421,45 +2418,22 @@ trait DatabasesBase Query::orderAsc('releaseYear')->toString(), ], 'useCache' => true, - 'ttl' => 60, + 'ttl' => 30, ]); - $this->assertEquals(200, $documents2['headers']['status-code']); - $this->assertCount(3, $documents2['body']['documents']); - - // Cached results match the first request + $this->assertEquals(200, $documents1['headers']['status-code']); + $this->assertCount(3, $documents1['body']['documents']); $this->assertEquals($documents1['body']['documents'][0]['$id'], $documents2['body']['documents'][0]['$id']); $this->assertEquals($documents1['body']['documents'][0]['title'], $documents2['body']['documents'][0]['title']); $this->assertEquals($documents1['body']['documents'][0]['releaseYear'], $documents2['body']['documents'][0]['releaseYear']); - - // Second request should hit cache $this->assertArrayHasKey('x-appwrite-cache-documents', $documents2['headers']); $this->assertEquals('hit', $documents2['headers']['x-appwrite-cache-documents']); $this->assertArrayHasKey('x-appwrite-cache-documents-total', $documents2['headers']); $this->assertEquals('hit', $documents2['headers']['x-appwrite-cache-documents-total']); - // useCache=false - should still work and no cache headers - $documents3 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'queries' => [ - Query::select(['title', 'releaseYear', '$id'])->toString(), - Query::orderAsc('releaseYear')->toString(), - ], - 'useCache' => false, - ]); - $this->assertEquals(200, $documents3['headers']['status-code']); - $this->assertCount(3, $documents3['body']['documents']); - $this->assertEquals(1944, $documents3['body']['documents'][0]['releaseYear']); - - // Verify cache headers are not present when useCache=false - $this->assertArrayNotHasKey('x-appwrite-cache-documents', $documents3['headers']); - $this->assertArrayNotHasKey('x-appwrite-cache-documents-total', $documents3['headers']); - - // Test with total=false - $documents4 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ + // 3. Using cache with same select queries but total is false,Should return cached results just for documents. + $documents3 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -2471,9 +2445,87 @@ trait DatabasesBase 'total' => false, ]); + $this->assertEquals(200, $documents3['headers']['status-code']); + $this->assertCount(3, $documents3['body']['documents']); + $this->assertEquals($documents3['body']['documents'][0]['$id'], $documents1['body']['documents'][0]['$id']); + $this->assertEquals($documents3['body']['documents'][0]['title'], $documents1['body']['documents'][0]['title']); + $this->assertEquals($documents3['body']['documents'][0]['releaseYear'], $documents1['body']['documents'][0]['releaseYear']); + $this->assertEquals(0, $documents3['body']['total']); + $this->assertCount(3, $documents3['body']['documents']); + $this->assertArrayHasKey('x-appwrite-cache-documents', $documents3['headers']); + $this->assertEquals('hit', $documents3['headers']['x-appwrite-cache-documents']); + $this->assertArrayHasKey('x-appwrite-cache-documents-total', $documents3['headers']); + $this->assertEquals('miss', $documents3['headers']['x-appwrite-cache-documents-total']); + + + // 3. Using cache with diffrent select queries ,Should miss cache. + $documents4 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::select(['title'])->toString(), + Query::orderAsc('releaseYear')->toString(), + ], + 'useCache' => true, + ]); + $this->assertEquals(200, $documents4['headers']['status-code']); - $this->assertEquals(0, $documents4['body']['total']); + $this->assertEquals(3, $documents4['body']['total']); $this->assertCount(3, $documents4['body']['documents']); + $this->assertEquals($documents4['body']['documents'][0]['title'],$documents1['body']['documents'][0]['title']); + $this->assertEquals($documents4['body']['documents'][1]['title'],$documents1['body']['documents'][1]['title']); + $this->assertEquals($documents4['body']['documents'][2]['title'],$documents1['body']['documents'][2]['title']); + $this->assertArrayHasKey('x-appwrite-cache-documents', $documents3['headers']); + $this->assertEquals('miss', $documents4['headers']['x-appwrite-cache-documents']); + $this->assertArrayHasKey('x-appwrite-cache-documents-total', $documents3['headers']); + $this->assertEquals('miss', $documents4['headers']['x-appwrite-cache-documents-total']); + + + // 4. Not using cache at all + $documents5 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::select(['title', 'releaseYear', '$id'])->toString(), + Query::orderAsc('releaseYear')->toString(), + ], + 'useCache' => false, + ]); + + $this->assertEquals(200, $documents5['headers']['status-code']); + $this->assertCount(3, $documents5['body']['documents']); + $this->assertEquals(1944, $documents5['body']['documents'][0]['releaseYear']); + $this->assertArrayNotHasKey('x-appwrite-cache-documents', $documents5['headers']); + $this->assertArrayNotHasKey('x-appwrite-cache-documents-total', $documents5['headers']); + + sleep(30); + + // Using cache with same select queries but passed ttl time, should miss cache. + $documents6 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::select(['title', 'releaseYear', '$id'])->toString(), + Query::orderAsc('releaseYear')->toString(), + ], + 'useCache' => true, + ]); + + $this->assertEquals(200, $documents6['headers']['status-code']); + $this->assertCount(3, $documents6['body']['documents']); + $this->assertArrayHasKey('title', $documents6['body']['documents'][0]); + $this->assertArrayHasKey('releaseYear', $documents6['body']['documents'][0]); + $this->assertArrayHasKey('$id', $documents6['body']['documents'][0]); + $this->assertEquals($documents4['body']['documents'][0]['$id'],$documents1['body']['documents'][0]['$id']); + $this->assertEquals($documents4['body']['documents'][1]['$id'],$documents1['body']['documents'][1]['$id']); + $this->assertEquals($documents4['body']['documents'][2]['$id'],$documents1['body']['documents'][2]['$id']); + $this->assertArrayHasKey('x-appwrite-cache-documents', $documents6['headers']); + $this->assertEquals('miss', $documents6['headers']['x-appwrite-cache-documents']); + $this->assertArrayHasKey('x-appwrite-cache-documents-total', $documents6['headers']); + $this->assertEquals('miss', $documents6['headers']['x-appwrite-cache-documents-total']); } /**