diff --git a/app/config/errors.php b/app/config/errors.php index 9fadde6e93..1ff8d2e8e7 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -487,6 +487,11 @@ return [ 'description' => 'Index with the requested ID already exists.', 'code' => 409, ], + Exception::INDEX_INVALID => [ + 'name' => Exception::INDEX_INVALID, + 'description' => 'Index invalid.', + 'code' => 400, + ], /** Project Errors */ Exception::PROJECT_NOT_FOUND => [ diff --git a/app/workers/databases.php b/app/workers/databases.php index f3e678d389..ecdb667f0d 100644 --- a/app/workers/databases.php +++ b/app/workers/databases.php @@ -129,7 +129,7 @@ class DatabaseV1 extends Worker } $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'available')); - } catch (Exception $e) { + } catch (\Exception $e) { Console::error($e->getMessage()); if ($e instanceof DatabaseException) { @@ -238,7 +238,7 @@ class DatabaseV1 extends Worker if (!$relatedAttribute->isEmpty()) { $dbForProject->deleteDocument('attributes', $relatedAttribute->getId()); } - } catch (Exception $e) { + } catch (\Exception $e) { Console::error($e->getMessage()); if ($e instanceof DatabaseException) { @@ -346,6 +346,7 @@ class DatabaseV1 extends Worker * @param Document $collection * @param Document $index * @param string $projectId + * @throws \Exception */ protected function createIndex(Document $database, Document $collection, Document $index, string $projectId): void { @@ -370,7 +371,7 @@ class DatabaseV1 extends Worker throw new DatabaseException('Failed to create Index'); } $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'available')); - } catch (Exception $e) { + } catch (\Exception $e) { Console::error($e->getMessage()); if ($e instanceof DatabaseException) { @@ -411,7 +412,6 @@ class DatabaseV1 extends Worker * @param Document $collection * @param Document $index * @param string $projectId - * @throws Exception */ protected function deleteIndex(Document $database, Document $collection, Document $index, string $projectId): void { @@ -432,7 +432,7 @@ class DatabaseV1 extends Worker throw new DatabaseException('Failed to delete index'); } $dbForProject->deleteDocument('indexes', $index->getId()); - } catch (Exception $e) { + } catch (\Exception $e) { Console::error($e->getMessage()); if ($e instanceof DatabaseException) { diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index ebd12851e2..f27d22d0f5 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -154,6 +154,7 @@ class Exception extends \Exception public const INDEX_NOT_FOUND = 'index_not_found'; public const INDEX_LIMIT_EXCEEDED = 'index_limit_exceeded'; public const INDEX_ALREADY_EXISTS = 'index_already_exists'; + public const INDEX_INVALID = 'index_invalid'; /** Projects */ public const PROJECT_NOT_FOUND = 'project_not_found'; diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index 144adfb989..bd9443adec 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -174,7 +174,18 @@ trait DatabasesBase 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'key' => 'description', - 'size' => 256, + 'size' => 512, + 'required' => false, + 'default' => '', + ]); + + $tagline = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/attributes/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'tagline', + 'size' => 512, 'required' => false, 'default' => '', ]); @@ -241,10 +252,17 @@ trait DatabasesBase $this->assertEquals(202, $description['headers']['status-code']); $this->assertEquals($description['body']['key'], 'description'); $this->assertEquals($description['body']['type'], 'string'); - $this->assertEquals($description['body']['size'], 256); + $this->assertEquals($description['body']['size'], 512); $this->assertEquals($description['body']['required'], false); $this->assertEquals($description['body']['default'], ''); + $this->assertEquals(202, $tagline['headers']['status-code']); + $this->assertEquals($tagline['body']['key'], 'tagline'); + $this->assertEquals($tagline['body']['type'], 'string'); + $this->assertEquals($tagline['body']['size'], 512); + $this->assertEquals($tagline['body']['required'], false); + $this->assertEquals($tagline['body']['default'], ''); + $this->assertEquals(202, $releaseYear['headers']['status-code']); $this->assertEquals($releaseYear['body']['key'], 'releaseYear'); $this->assertEquals($releaseYear['body']['type'], 'integer'); @@ -282,17 +300,18 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), []); + ])); $this->assertIsArray($movies['body']['attributes']); - $this->assertCount(7, $movies['body']['attributes']); + $this->assertCount(8, $movies['body']['attributes']); $this->assertEquals($movies['body']['attributes'][0]['key'], $title['body']['key']); $this->assertEquals($movies['body']['attributes'][1]['key'], $description['body']['key']); - $this->assertEquals($movies['body']['attributes'][2]['key'], $releaseYear['body']['key']); - $this->assertEquals($movies['body']['attributes'][3]['key'], $duration['body']['key']); - $this->assertEquals($movies['body']['attributes'][4]['key'], $actors['body']['key']); - $this->assertEquals($movies['body']['attributes'][5]['key'], $datetime['body']['key']); - $this->assertEquals($movies['body']['attributes'][6]['key'], $relationship['body']['key']); + $this->assertEquals($movies['body']['attributes'][2]['key'], $tagline['body']['key']); + $this->assertEquals($movies['body']['attributes'][3]['key'], $releaseYear['body']['key']); + $this->assertEquals($movies['body']['attributes'][4]['key'], $duration['body']['key']); + $this->assertEquals($movies['body']['attributes'][5]['key'], $actors['body']['key']); + $this->assertEquals($movies['body']['attributes'][6]['key'], $datetime['body']['key']); + $this->assertEquals($movies['body']['attributes'][7]['key'], $relationship['body']['key']); return $data; } @@ -888,6 +907,7 @@ trait DatabasesBase public function testCreateIndexes(array $data): array { $databaseId = $data['databaseId']; + $titleIndex = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -956,7 +976,6 @@ trait DatabasesBase $this->assertEquals('available', $movies['body']['indexes'][1]['status']); $this->assertEquals('available', $movies['body']['indexes'][2]['status']); - $releaseWithDate = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -973,6 +992,59 @@ trait DatabasesBase $this->assertCount(1, $releaseWithDate['body']['attributes']); $this->assertEquals('birthDay', $releaseWithDate['body']['attributes'][0]); + // Test for failure + $fulltextReleaseYear = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'releaseYearDated', + 'type' => 'fulltext', + 'attributes' => ['releaseYear'], + ]); + + $this->assertEquals(400, $fulltextReleaseYear['headers']['status-code']); + $this->assertEquals($fulltextReleaseYear['body']['message'], 'Attribute "releaseYear" cannot be part of a FULLTEXT index, must be of type string'); + + $noAttributes = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'none', + 'type' => 'key', + 'attributes' => [], + ]); + + $this->assertEquals(400, $noAttributes['headers']['status-code']); + $this->assertEquals($noAttributes['body']['message'], 'No attributes provided for index'); + + $duplicates = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'duplicate', + 'type' => 'fulltext', + 'attributes' => ['releaseYear', 'releaseYear'], + ]); + + $this->assertEquals(400, $duplicates['headers']['status-code']); + $this->assertEquals($duplicates['body']['message'], 'Duplicate attributes provided'); + + $tooLong = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]), [ + 'key' => 'tooLong', + 'type' => 'key', + 'attributes' => ['description', 'tagline'], + ]); + + $this->assertEquals(400, $tooLong['headers']['status-code']); + $this->assertStringContainsString('Index length is longer than the maximum', $tooLong['body']['message']); + return $data; } @@ -3419,7 +3491,7 @@ trait DatabasesBase ]); $this->assertEquals(400, $documents['headers']['status-code']); - $this->assertEquals('Query not valid: Cannot query nested attribute on: library', $documents['body']['message']); + $this->assertEquals('Invalid query: Cannot query nested attribute on: library', $documents['body']['message']); $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $person['body']['$id'] . '/attributes/library', array_merge([ 'content-type' => 'application/json',