From 949f58522d7c46d6f6cb0bb782ed6fc8eecd576b Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 27 Apr 2025 10:33:44 +0530 Subject: [PATCH] change: events system. --- app/config/events.php | 28 +- app/config/templates/function.php | 2 +- app/controllers/api/databases.php | 21 +- app/controllers/general.php | 7 +- src/Appwrite/Event/Database.php | 36 +-- src/Appwrite/Event/Realtime.php | 4 +- src/Appwrite/Messaging/Adapter/Realtime.php | 35 +-- src/Appwrite/Platform/Workers/Databases.php | 284 +++++++++--------- .../Request/Filters/DatabaseAliases.php | 38 +++ src/Appwrite/Utopia/Request/Filters/V19.php | 32 +- .../Utopia/Response/Model/Webhook.php | 2 +- 11 files changed, 254 insertions(+), 235 deletions(-) create mode 100644 src/Appwrite/Utopia/Request/Filters/DatabaseAliases.php diff --git a/app/config/events.php b/app/config/events.php index 0bfddf4f1f..3b0cc982a0 100644 --- a/app/config/events.php +++ b/app/config/events.php @@ -95,22 +95,22 @@ return [ '$model' => Response::MODEL_DATABASE, '$resource' => true, '$description' => 'This event triggers on any database event.', - 'collections' => [ + 'tables' => [ '$model' => Response::MODEL_COLLECTION, '$resource' => true, - '$description' => 'This event triggers on any collection event.', - 'documents' => [ + '$description' => 'This event triggers on any table event.', + 'rows' => [ '$model' => Response::MODEL_DOCUMENT, '$resource' => true, - '$description' => 'This event triggers on any documents event.', + '$description' => 'This event triggers on any rows event.', 'create' => [ - '$description' => 'This event triggers when a document is created.', + '$description' => 'This event triggers when a row is created.', ], 'delete' => [ - '$description' => 'This event triggers when a document is deleted.' + '$description' => 'This event triggers when a row is deleted.' ], 'update' => [ - '$description' => 'This event triggers when a document is updated.' + '$description' => 'This event triggers when a row is updated.' ], ], 'indexes' => [ @@ -124,25 +124,25 @@ return [ '$description' => 'This event triggers when an index is deleted.' ] ], - 'attributes' => [ + 'columns' => [ '$model' => Response::MODEL_ATTRIBUTE, '$resource' => true, - '$description' => 'This event triggers on any attributes event.', + '$description' => 'This event triggers on any columns event.', 'create' => [ - '$description' => 'This event triggers when an attribute is created.', + '$description' => 'This event triggers when an column is created.', ], 'delete' => [ - '$description' => 'This event triggers when an attribute is deleted.' + '$description' => 'This event triggers when an column is deleted.' ] ], 'create' => [ - '$description' => 'This event triggers when a collection is created.' + '$description' => 'This event triggers when a table is created.' ], 'delete' => [ - '$description' => 'This event triggers when a collection is deleted.', + '$description' => 'This event triggers when a table is deleted.', ], 'update' => [ - '$description' => 'This event triggers when a collection is updated.', + '$description' => 'This event triggers when a table is updated.', ] ], 'create' => [ diff --git a/app/config/templates/function.php b/app/config/templates/function.php index d8426ad900..b017281dbe 100644 --- a/app/config/templates/function.php +++ b/app/config/templates/function.php @@ -1395,7 +1395,7 @@ return [ 'score' => 5, 'tagline' => 'Convert text to speech using the Hugging Face inference API.', 'permissions' => ['any'], - 'events' => ['databases.*.collections.*.documents.*.create'], + 'events' => ['databases.*.tables.*.rows.*.create'], 'cron' => '', 'timeout' => 15, 'useCases' => ['ai'], diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index d3a34f56e8..d8556dd4fe 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -218,8 +218,8 @@ function createColumn(string $databaseId, string $tableId, Document $column, Res $queueForDatabase ->setType(DATABASE_TYPE_CREATE_ATTRIBUTE) ->setDatabase($db) - ->setCollection($table) - ->setDocument($column); + ->setTable($table) + ->setRow($column); $queueForEvents ->setContext('table', $table) @@ -485,8 +485,7 @@ App::post('/v1/databases') ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('queueForStatsUsage') - ->action(function (string $databaseId, string $name, bool $enabled, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) { + ->action(function (string $databaseId, string $name, bool $enabled, Response $response, Database $dbForProject, Event $queueForEvents) { $databaseId = $databaseId == 'unique()' ? ID::unique() : $databaseId; @@ -1280,7 +1279,7 @@ App::delete('/v1/databases/:databaseId/tables/:tableId') $queueForDatabase ->setType(DATABASE_TYPE_DELETE_COLLECTION) ->setDatabase($database) - ->setCollection($table); + ->setTable($table); $queueForEvents ->setContext('database', $database) @@ -2759,9 +2758,9 @@ App::delete('/v1/databases/:databaseId/tables/:tableId/columns/:key') $queueForDatabase ->setType(DATABASE_TYPE_DELETE_ATTRIBUTE) - ->setCollection($table) + ->setTable($table) ->setDatabase($db) - ->setDocument($column); + ->setRow($column); // Select response model based on type and format $type = $column->getAttribute('type'); @@ -2953,8 +2952,8 @@ App::post('/v1/databases/:databaseId/tables/:tableId/indexes') $queueForDatabase ->setType(DATABASE_TYPE_CREATE_INDEX) ->setDatabase($db) - ->setCollection($table) - ->setDocument($index); + ->setTable($table) + ->setRow($index); $queueForEvents ->setParam('databaseId', $databaseId) @@ -3164,8 +3163,8 @@ App::delete('/v1/databases/:databaseId/tables/:tableId/indexes/:key') $queueForDatabase ->setType(DATABASE_TYPE_DELETE_INDEX) ->setDatabase($db) - ->setCollection($table) - ->setDocument($index); + ->setTable($table) + ->setRow($index); $queueForEvents ->setParam('databaseId', $databaseId) diff --git a/app/controllers/general.php b/app/controllers/general.php index bf048be9d6..fdef1e9cee 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -18,6 +18,7 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Transformation\Adapter\Preview; use Appwrite\Transformation\Transformation; use Appwrite\Utopia\Request; +use Appwrite\Utopia\Request\Filters\DatabaseAliases; use Appwrite\Utopia\Request\Filters\V16 as RequestV16; use Appwrite\Utopia\Request\Filters\V17 as RequestV17; use Appwrite\Utopia\Request\Filters\V18 as RequestV18; @@ -805,12 +806,14 @@ App::init() if (version_compare($requestFormat, '1.6.0', '<')) { $request->addFilter(new RequestV18()); } - // alias filters on 1.7.x, so we use `<=` and not just `<` - if (version_compare($requestFormat, '1.7.0', '<=')) { + if (version_compare($requestFormat, '1.7.0', '<')) { $request->addFilter(new RequestV19()); } } + // process on all databases endpoints! + $request->addFilter(new DatabaseAliases()); + $domain = $request->getHostname(); $domains = Config::getParam('domains', []); if (!array_key_exists($domain, $domains)) { diff --git a/src/Appwrite/Event/Database.php b/src/Appwrite/Event/Database.php index d2f70dddf2..797574d2fc 100644 --- a/src/Appwrite/Event/Database.php +++ b/src/Appwrite/Event/Database.php @@ -10,8 +10,8 @@ class Database extends Event { protected string $type = ''; protected ?Document $database = null; - protected ?Document $collection = null; - protected ?Document $document = null; + protected ?Document $table = null; + protected ?Document $row = null; public function __construct(protected Publisher $publisher) { @@ -55,48 +55,48 @@ class Database extends Event } /** - * Set the collection for this database event. + * Set the table for this database event. * - * @param Document $collection + * @param Document $table * @return self */ - public function setCollection(Document $collection): self + public function setTable(Document $table): self { - $this->collection = $collection; + $this->table = $table; return $this; } /** - * Returns set collection for this event. + * Returns set table for this event. * * @return null|Document */ - public function getCollection(): ?Document + public function getTable(): ?Document { - return $this->collection; + return $this->table; } /** - * Set the document for this database event. + * Set the row for this database event. * - * @param Document $document + * @param Document $row * @return self */ - public function setDocument(Document $document): self + public function setRow(Document $row): self { - $this->document = $document; + $this->row = $row; return $this; } /** - * Returns set document for this database event. + * Returns set row for this database event. * @return null|Document */ - public function getDocument(): ?Document + public function getRow(): ?Document { - return $this->document; + return $this->row; } public function getQueue(): string @@ -123,8 +123,8 @@ class Database extends Event 'project' => $this->project, 'user' => $this->user, 'type' => $this->type, - 'collection' => $this->collection, - 'document' => $this->document, + 'table' => $this->table, + 'row' => $this->row, 'database' => $this->database, 'events' => Event::generateEvents($this->getEvent(), $this->getParams()) ]; diff --git a/src/Appwrite/Event/Realtime.php b/src/Appwrite/Event/Realtime.php index 4d8c9a321b..e1ec891e67 100644 --- a/src/Appwrite/Event/Realtime.php +++ b/src/Appwrite/Event/Realtime.php @@ -76,7 +76,7 @@ class Realtime extends Event $payload = new Document($this->getPayload()); $db = $this->getContext('database'); - $collection = $this->getContext('collection'); + $table = $this->getContext('table'); $bucket = $this->getContext('bucket'); $target = RealtimeAdapter::fromPayload( @@ -85,7 +85,7 @@ class Realtime extends Event payload: $payload, project: $this->getProject(), database: $db, - collection: $collection, + table: $table, bucket: $bucket, ); diff --git a/src/Appwrite/Messaging/Adapter/Realtime.php b/src/Appwrite/Messaging/Adapter/Realtime.php index 568132ceb1..400589f677 100644 --- a/src/Appwrite/Messaging/Adapter/Realtime.php +++ b/src/Appwrite/Messaging/Adapter/Realtime.php @@ -252,12 +252,12 @@ class Realtime extends Adapter * @param Document $payload * @param Document|null $project * @param Document|null $database - * @param Document|null $collection + * @param Document|null $table * @param Document|null $bucket * @return array * @throws \Exception */ - public static function fromPayload(string $event, Document $payload, Document $project = null, Document $database = null, Document $collection = null, Document $bucket = null): array + public static function fromPayload(string $event, Document $payload, Document $project = null, Document $database = null, Document $table = null, Document $bucket = null): array { $channels = []; $roles = []; @@ -273,6 +273,7 @@ class Realtime extends Adapter $roles = [Role::user(ID::custom($parts[1]))->toString()]; break; case 'rules': + case 'migrations': $channels[] = 'console'; $channels[] = 'projects.' . $project->getId(); $projectId = 'console'; @@ -297,26 +298,26 @@ class Realtime extends Adapter $roles = [Role::team(ID::custom($parts[1]))->toString()]; break; case 'databases': - if (in_array($parts[4] ?? [], ['attributes', 'indexes'])) { + if (in_array($parts[4] ?? [], ['columns', 'indexes'])) { $channels[] = 'console'; $channels[] = 'projects.' . $project->getId(); $projectId = 'console'; $roles = [Role::team($project->getAttribute('teamId'))->toString()]; - } elseif (($parts[4] ?? '') === 'documents') { + } elseif (($parts[4] ?? '') === 'rows') { if ($database->isEmpty()) { - throw new \Exception('Database needs to be passed to Realtime for Document events in the Database.'); + throw new \Exception('Database needs to be passed to Realtime for Row events in the Database.'); } - if ($collection->isEmpty()) { - throw new \Exception('Collection needs to be passed to Realtime for Document events in the Database.'); + if ($table->isEmpty()) { + throw new \Exception('Table needs to be passed to Realtime for Row events in the Database.'); } - $channels[] = 'documents'; - $channels[] = 'databases.' . $database->getId() . '.collections.' . $payload->getAttribute('$collectionId') . '.documents'; - $channels[] = 'databases.' . $database->getId() . '.collections.' . $payload->getAttribute('$collectionId') . '.documents.' . $payload->getId(); + $channels[] = 'rows'; + $channels[] = 'databases.' . $database->getId() . '.tables.' . $payload->getAttribute('$collectionId') . '.rows'; + $channels[] = 'databases.' . $database->getId() . '.tables.' . $payload->getAttribute('$collectionId') . '.rows.' . $payload->getId(); - $roles = $collection->getAttribute('documentSecurity', false) - ? \array_merge($collection->getRead(), $payload->getRead()) - : $collection->getRead(); + $roles = $table->getAttribute('documentSecurity', false) + ? \array_merge($table->getRead(), $payload->getRead()) + : $table->getRead(); } break; case 'buckets': @@ -334,7 +335,6 @@ class Realtime extends Adapter } break; - case 'functions': if ($parts[2] === 'executions') { if (!empty($payload->getRead())) { @@ -353,7 +353,6 @@ class Realtime extends Adapter } break; - case 'sites': if ($parts[2] === 'deployments') { $channels[] = 'console'; @@ -362,12 +361,6 @@ class Realtime extends Adapter $roles = [Role::team($project->getAttribute('teamId'))->toString()]; } - break; - case 'migrations': - $channels[] = 'console'; - $channels[] = 'projects.' . $project->getId(); - $projectId = 'console'; - $roles = [Role::team($project->getAttribute('teamId'))->toString()]; break; } diff --git a/src/Appwrite/Platform/Workers/Databases.php b/src/Appwrite/Platform/Workers/Databases.php index b2691b420e..1f3b76d6eb 100644 --- a/src/Appwrite/Platform/Workers/Databases.php +++ b/src/Appwrite/Platform/Workers/Databases.php @@ -60,8 +60,8 @@ class Databases extends Action } $type = $payload['type']; - $collection = new Document($payload['collection'] ?? []); - $document = new Document($payload['document'] ?? []); + $row = new Document($payload['row'] ?? []); + $table = new Document($payload['table'] ?? []); $database = new Document($payload['database'] ?? []); $log->addTag('projectId', $project->getId()); @@ -75,19 +75,19 @@ class Databases extends Action match (\strval($type)) { DATABASE_TYPE_DELETE_DATABASE => $this->deleteDatabase($database, $project, $dbForProject), - DATABASE_TYPE_DELETE_COLLECTION => $this->deleteCollection($database, $collection, $project, $dbForProject), - DATABASE_TYPE_CREATE_ATTRIBUTE => $this->createAttribute($database, $collection, $document, $project, $dbForPlatform, $dbForProject, $queueForRealtime), - DATABASE_TYPE_DELETE_ATTRIBUTE => $this->deleteAttribute($database, $collection, $document, $project, $dbForPlatform, $dbForProject, $queueForRealtime), - DATABASE_TYPE_CREATE_INDEX => $this->createIndex($database, $collection, $document, $project, $dbForPlatform, $dbForProject, $queueForRealtime), - DATABASE_TYPE_DELETE_INDEX => $this->deleteIndex($database, $collection, $document, $project, $dbForPlatform, $dbForProject, $queueForRealtime), + DATABASE_TYPE_DELETE_COLLECTION => $this->deleteTable($database, $table, $project, $dbForProject), + DATABASE_TYPE_CREATE_ATTRIBUTE => $this->createColumn($database, $table, $row, $project, $dbForPlatform, $dbForProject, $queueForRealtime), + DATABASE_TYPE_DELETE_ATTRIBUTE => $this->deleteColumn($database, $table, $row, $project, $dbForPlatform, $dbForProject, $queueForRealtime), + DATABASE_TYPE_CREATE_INDEX => $this->createIndex($database, $table, $row, $project, $dbForPlatform, $dbForProject, $queueForRealtime), + DATABASE_TYPE_DELETE_INDEX => $this->deleteIndex($database, $table, $row, $project, $dbForPlatform, $dbForProject, $queueForRealtime), default => throw new Exception('No database operation for type: ' . \strval($type)), }; } /** * @param Document $database - * @param Document $collection - * @param Document $attribute + * @param Document $table + * @param Document $column * @param Document $project * @param Database $dbForPlatform * @param Database $dbForProject @@ -98,64 +98,64 @@ class Databases extends Action * @throws \Exception * @throws \Throwable */ - private function createAttribute( + private function createColumn( Document $database, - Document $collection, - Document $attribute, + Document $table, + Document $column, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime ): void { - if ($collection->isEmpty()) { - throw new Exception('Missing collection'); + if ($table->isEmpty()) { + throw new Exception('Missing table'); } - if ($attribute->isEmpty()) { - throw new Exception('Missing attribute'); + if ($column->isEmpty()) { + throw new Exception('Missing column'); } $projectId = $project->getId(); - $event = "databases.[databaseId].collections.[collectionId].attributes.[attributeId].update"; + $event = "databases.[databaseId].tables.[tableId].columns.[columnId].update"; /** * TODO @christyjacob4 verify if this is still the case * Fetch attribute from the database, since with Resque float values are loosing informations. */ - $attribute = $dbForProject->getDocument('attributes', $attribute->getId()); + $column = $dbForProject->getDocument('attributes', $column->getId()); - if ($attribute->isEmpty()) { + if ($column->isEmpty()) { // Attribute was deleted before job was processed return; } - $collectionId = $collection->getId(); - $key = $attribute->getAttribute('key', ''); - $type = $attribute->getAttribute('type', ''); - $size = $attribute->getAttribute('size', 0); - $required = $attribute->getAttribute('required', false); - $default = $attribute->getAttribute('default', null); - $signed = $attribute->getAttribute('signed', true); - $array = $attribute->getAttribute('array', false); - $format = $attribute->getAttribute('format', ''); - $formatOptions = $attribute->getAttribute('formatOptions', []); - $filters = $attribute->getAttribute('filters', []); - $options = $attribute->getAttribute('options', []); + $tableId = $table->getId(); + $key = $column->getAttribute('key', ''); + $type = $column->getAttribute('type', ''); + $size = $column->getAttribute('size', 0); + $required = $column->getAttribute('required', false); + $default = $column->getAttribute('default', null); + $signed = $column->getAttribute('signed', true); + $array = $column->getAttribute('array', false); + $format = $column->getAttribute('format', ''); + $formatOptions = $column->getAttribute('formatOptions', []); + $filters = $column->getAttribute('filters', []); + $options = $column->getAttribute('options', []); $project = $dbForPlatform->getDocument('projects', $projectId); - $relatedAttribute = new Document(); - $relatedCollection = new Document(); + $relatedColumn = new Document(); + $relatedTable = new Document(); try { switch ($type) { case Database::VAR_RELATIONSHIP: - $relatedCollection = $dbForProject->getDocument('database_' . $database->getInternalId(), $options['relatedCollection']); - if ($relatedCollection->isEmpty()) { - throw new DatabaseException('Collection not found'); + $relatedTable = $dbForProject->getDocument('database_' . $database->getInternalId(), $options['relatedCollection']); + if ($relatedTable->isEmpty()) { + throw new DatabaseException('Table not found'); } if ( !$dbForProject->createRelationship( - collection: 'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), - relatedCollection: 'database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId(), + collection: 'database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), + relatedCollection: 'database_' . $database->getInternalId() . '_collection_' . $relatedTable->getInternalId(), type: $options['relationType'], twoWay: $options['twoWay'], id: $key, @@ -163,61 +163,61 @@ class Databases extends Action onDelete: $options['onDelete'], ) ) { - throw new DatabaseException('Failed to create Attribute'); + throw new DatabaseException('Failed to create Column'); } if ($options['twoWay']) { - $relatedAttribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $options['twoWayKey']); - $dbForProject->updateDocument('attributes', $relatedAttribute->getId(), $relatedAttribute->setAttribute('status', 'available')); + $relatedColumn = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $options['twoWayKey']); + $dbForProject->updateDocument('attributes', $relatedColumn->getId(), $relatedColumn->setAttribute('status', 'available')); } break; default: - if (!$dbForProject->createAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) { - throw new Exception('Failed to create Attribute'); + if (!$dbForProject->createAttribute('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) { + throw new Exception('Failed to create Column'); } } - $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'available')); + $dbForProject->updateDocument('attributes', $column->getId(), $column->setAttribute('status', 'available')); } catch (\Throwable $e) { Console::error($e->getMessage()); if ($e instanceof DatabaseException) { - $attribute->setAttribute('error', $e->getMessage()); - if (! $relatedAttribute->isEmpty()) { - $relatedAttribute->setAttribute('error', $e->getMessage()); + $column->setAttribute('error', $e->getMessage()); + if (! $relatedColumn->isEmpty()) { + $relatedColumn->setAttribute('error', $e->getMessage()); } } $dbForProject->updateDocument( 'attributes', - $attribute->getId(), - $attribute->setAttribute('status', 'failed') + $column->getId(), + $column->setAttribute('status', 'failed') ); - if (! $relatedAttribute->isEmpty()) { + if (! $relatedColumn->isEmpty()) { $dbForProject->updateDocument( 'attributes', - $relatedAttribute->getId(), - $relatedAttribute->setAttribute('status', 'failed') + $relatedColumn->getId(), + $relatedColumn->setAttribute('status', 'failed') ); } throw $e; } finally { - $this->trigger($database, $collection, $project, $event, $queueForRealtime, $attribute); + $this->trigger($database, $table, $project, $event, $queueForRealtime, $column); - if (! $relatedCollection->isEmpty()) { - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId()); + if (! $relatedTable->isEmpty()) { + $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedTable->getId()); } - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId); + $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $tableId); } } /** * @param Document $database - * @param Document $collection - * @param Document $attribute + * @param Document $table + * @param Document $column * @param Document $project * @param Database $dbForPlatform * @param Database $dbForProject @@ -228,24 +228,24 @@ class Databases extends Action * @throws \Exception * @throws \Throwable **/ - private function deleteAttribute(Document $database, Document $collection, Document $attribute, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime): void + private function deleteColumn(Document $database, Document $table, Document $column, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime): void { - if ($collection->isEmpty()) { + if ($table->isEmpty()) { throw new Exception('Missing collection'); } - if ($attribute->isEmpty()) { + if ($column->isEmpty()) { throw new Exception('Missing attribute'); } $projectId = $project->getId(); - $event = 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].delete'; - $collectionId = $collection->getId(); - $key = $attribute->getAttribute('key', ''); - $type = $attribute->getAttribute('type', ''); + $event = 'databases.[databaseId].tables.[tableId].columns.[columnId].delete'; + $tableId = $table->getId(); + $key = $column->getAttribute('key', ''); + $type = $column->getAttribute('type', ''); $project = $dbForPlatform->getDocument('projects', $projectId); - $options = $attribute->getAttribute('options', []); - $relatedAttribute = new Document(); - $relatedCollection = new Document(); + $options = $column->getAttribute('options', []); + $relatedColumn = new Document(); + $relatedTable = new Document(); // possible states at this point: // - available: should not land in queue; controller flips these to 'deleting' // - processing: hasn't finished creating @@ -257,89 +257,89 @@ class Databases extends Action try { if ($type === Database::VAR_RELATIONSHIP) { if ($options['twoWay']) { - $relatedCollection = $dbForProject->getDocument('database_' . $database->getInternalId(), $options['relatedCollection']); - if ($relatedCollection->isEmpty()) { - throw new DatabaseException('Collection not found'); + $relatedTable = $dbForProject->getDocument('database_' . $database->getInternalId(), $options['relatedCollection']); + if ($relatedTable->isEmpty()) { + throw new DatabaseException('Table not found'); } - $relatedAttribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $options['twoWayKey']); + $relatedColumn = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $options['twoWayKey']); } - if (!$dbForProject->deleteRelationship('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { - $dbForProject->updateDocument('attributes', $relatedAttribute->getId(), $relatedAttribute->setAttribute('status', 'stuck')); + if (!$dbForProject->deleteRelationship('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $key)) { + $dbForProject->updateDocument('attributes', $relatedColumn->getId(), $relatedColumn->setAttribute('status', 'stuck')); throw new DatabaseException('Failed to delete Relationship'); } - } elseif (!$dbForProject->deleteAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { - throw new DatabaseException('Failed to delete Attribute'); + } elseif (!$dbForProject->deleteAttribute('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $key)) { + throw new DatabaseException('Failed to delete Column'); } - $dbForProject->deleteDocument('attributes', $attribute->getId()); + $dbForProject->deleteDocument('attributes', $column->getId()); - if (!$relatedAttribute->isEmpty()) { - $dbForProject->deleteDocument('attributes', $relatedAttribute->getId()); + if (!$relatedColumn->isEmpty()) { + $dbForProject->deleteDocument('attributes', $relatedColumn->getId()); } } catch (NotFound $e) { Console::error($e->getMessage()); - $dbForProject->deleteDocument('attributes', $attribute->getId()); + $dbForProject->deleteDocument('attributes', $column->getId()); - if (!$relatedAttribute->isEmpty()) { - $dbForProject->deleteDocument('attributes', $relatedAttribute->getId()); + if (!$relatedColumn->isEmpty()) { + $dbForProject->deleteDocument('attributes', $relatedColumn->getId()); } } catch (\Throwable $e) { Console::error($e->getMessage()); if ($e instanceof DatabaseException) { - $attribute->setAttribute('error', $e->getMessage()); - if (!$relatedAttribute->isEmpty()) { - $relatedAttribute->setAttribute('error', $e->getMessage()); + $column->setAttribute('error', $e->getMessage()); + if (!$relatedColumn->isEmpty()) { + $relatedColumn->setAttribute('error', $e->getMessage()); } } $dbForProject->updateDocument( 'attributes', - $attribute->getId(), - $attribute->setAttribute('status', 'stuck') + $column->getId(), + $column->setAttribute('status', 'stuck') ); - if (!$relatedAttribute->isEmpty()) { + if (!$relatedColumn->isEmpty()) { $dbForProject->updateDocument( 'attributes', - $relatedAttribute->getId(), - $relatedAttribute->setAttribute('status', 'stuck') + $relatedColumn->getId(), + $relatedColumn->setAttribute('status', 'stuck') ); } throw $e; } finally { - $this->trigger($database, $collection, $project, $event, $queueForRealtime, $attribute); + $this->trigger($database, $table, $project, $event, $queueForRealtime, $column); } // The underlying database removes/rebuilds indexes when attribute is removed // Update indexes table with changes /** @var Document[] $indexes */ - $indexes = $collection->getAttribute('indexes', []); + $indexes = $table->getAttribute('indexes', []); foreach ($indexes as $index) { - /** @var string[] $attributes */ - $attributes = $index->getAttribute('attributes'); + /** @var string[] $columns */ + $columns = $index->getAttribute('attributes'); $lengths = $index->getAttribute('lengths'); $orders = $index->getAttribute('orders'); - $found = \array_search($key, $attributes); + $found = \array_search($key, $columns); if ($found !== false) { // If found, remove entry from attributes, lengths, and orders // array_values wraps array_diff to reindex array keys // when found attribute is removed from array - $attributes = \array_values(\array_diff($attributes, [$attributes[$found]])); + $columns = \array_values(\array_diff($columns, [$columns[$found]])); $lengths = \array_values(\array_diff($lengths, isset($lengths[$found]) ? [$lengths[$found]] : [])); $orders = \array_values(\array_diff($orders, isset($orders[$found]) ? [$orders[$found]] : [])); - if (empty($attributes)) { + if (empty($columns)) { $dbForProject->deleteDocument('indexes', $index->getId()); } else { $index - ->setAttribute('attributes', $attributes, Document::SET_TYPE_ASSIGN) + ->setAttribute('attributes', $columns, Document::SET_TYPE_ASSIGN) ->setAttribute('lengths', $lengths, Document::SET_TYPE_ASSIGN) ->setAttribute('orders', $orders, Document::SET_TYPE_ASSIGN); @@ -357,7 +357,7 @@ class Databases extends Action } if ($exists) { // Delete the duplicate if created, else update in db - $this->deleteIndex($database, $collection, $index, $project, $dbForPlatform, $dbForProject, $queueForRealtime); + $this->deleteIndex($database, $table, $index, $project, $dbForPlatform, $dbForProject, $queueForRealtime); } else { $dbForProject->updateDocument('indexes', $index->getId(), $index); } @@ -365,17 +365,17 @@ class Databases extends Action } } } finally { - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId); + $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $tableId); - if (! $relatedCollection->isEmpty()) { - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId()); + if (! $relatedTable->isEmpty()) { + $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedTable->getId()); } } } /** * @param Document $database - * @param Document $collection + * @param Document $table * @param Document $index * @param Document $project * @param Database $dbForPlatform @@ -388,9 +388,9 @@ class Databases extends Action * @throws DatabaseException * @throws \Throwable */ - private function createIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime): void + private function createIndex(Document $database, Document $table, Document $index, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime): void { - if ($collection->isEmpty()) { + if ($table->isEmpty()) { throw new Exception('Missing collection'); } if ($index->isEmpty()) { @@ -398,8 +398,8 @@ class Databases extends Action } $projectId = $project->getId(); - $event = 'databases.[databaseId].collections.[collectionId].indexes.[indexId].update'; - $collectionId = $collection->getId(); + $event = 'databases.[databaseId].tables.[tableId].indexes.[indexId].update'; + $collectionId = $table->getId(); $key = $index->getAttribute('key', ''); $type = $index->getAttribute('type', ''); $attributes = $index->getAttribute('attributes', []); @@ -408,7 +408,7 @@ class Databases extends Action $project = $dbForPlatform->getDocument('projects', $projectId); try { - if (!$dbForProject->createIndex('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $attributes, $lengths, $orders)) { + if (!$dbForProject->createIndex('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $key, $type, $attributes, $lengths, $orders)) { throw new DatabaseException('Failed to create Index'); } $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'available')); @@ -425,14 +425,14 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $collection, $project, $event, $queueForRealtime, null, $index); + $this->trigger($database, $table, $project, $event, $queueForRealtime, null, $index); $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId); } } /** * @param Document $database - * @param Document $collection + * @param Document $table * @param Document $index * @param Document $project * @param Database $dbForPlatform @@ -445,9 +445,9 @@ class Databases extends Action * @throws DatabaseException * @throws \Throwable */ - private function deleteIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime): void + private function deleteIndex(Document $database, Document $table, Document $index, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime): void { - if ($collection->isEmpty()) { + if ($table->isEmpty()) { throw new Exception('Missing collection'); } if ($index->isEmpty()) { @@ -455,13 +455,13 @@ class Databases extends Action } $projectId = $project->getId(); - $event = 'databases.[databaseId].collections.[collectionId].indexes.[indexId].delete'; + $event = 'databases.[databaseId].tables.[tableId].indexes.[indexId].delete'; $key = $index->getAttribute('key'); $status = $index->getAttribute('status', ''); $project = $dbForPlatform->getDocument('projects', $projectId); try { - if ($status !== 'failed' && !$dbForProject->deleteIndex('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { + if ($status !== 'failed' && !$dbForProject->deleteIndex('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $key)) { throw new DatabaseException('Failed to delete index'); } $dbForProject->deleteDocument('indexes', $index->getId()); @@ -481,8 +481,8 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $collection, $project, $event, $queueForRealtime, null, $index); - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collection->getId()); + $this->trigger($database, $table, $project, $event, $queueForRealtime, null, $index); + $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $table->getId()); } } @@ -496,7 +496,7 @@ class Databases extends Action protected function deleteDatabase(Document $database, Document $project, $dbForProject): void { $this->deleteByGroup('database_' . $database->getInternalId(), [], $dbForProject, function ($collection) use ($database, $project, $dbForProject) { - $this->deleteCollection($database, $collection, $project, $dbForProject); + $this->deleteTable($database, $collection, $project, $dbForProject); }); $dbForProject->deleteCollection('database_' . $database->getInternalId()); @@ -504,7 +504,7 @@ class Databases extends Action /** * @param Document $database - * @param Document $collection + * @param Document $table * @param Document $project * @param Database $dbForProject * @return void @@ -515,17 +515,17 @@ class Databases extends Action * @throws Structure * @throws Exception */ - protected function deleteCollection(Document $database, Document $collection, Document $project, Database $dbForProject): void + protected function deleteTable(Document $database, Document $table, Document $project, Database $dbForProject): void { - if ($collection->isEmpty()) { - throw new Exception('Missing collection'); + if ($table->isEmpty()) { + throw new Exception('Missing table'); } - $collectionId = $collection->getId(); - $collectionInternalId = $collection->getInternalId(); + $collectionId = $table->getId(); + $collectionInternalId = $table->getInternalId(); $databaseInternalId = $database->getInternalId(); - $dbForProject->deleteCollection('database_' . $databaseInternalId . '_collection_' . $collection->getInternalId()); + $dbForProject->deleteCollection('database_' . $databaseInternalId . '_collection_' . $table->getInternalId()); /** * Related collections relating to current collection @@ -558,50 +558,50 @@ class Databases extends Action /** - * @param string $collectionId + * @param string $tableId * @param array $queries * @param Database $database * @param callable|null $callback * @return void * @throws Exception */ - protected function deleteByGroup(string $collectionId, array $queries, Database $database, callable $callback = null): void + protected function deleteByGroup(string $tableId, array $queries, Database $database, callable $callback = null): void { $start = \microtime(true); try { $count = $database->deleteDocuments( - $collectionId, + $tableId, $queries, Database::DELETE_BATCH_SIZE, $callback ); } catch (\Throwable $th) { $tenant = $database->getSharedTables() ? 'Tenant:'.$database->getTenant() : ''; - Console::error("Failed to delete documents for collection:{$database->getNamespace()}_{$collectionId} {$tenant} :{$th->getMessage()}"); + Console::error("Failed to delete rows for table:{$database->getNamespace()}_{$tableId} {$tenant} :{$th->getMessage()}"); return; } $end = \microtime(true); - Console::info("Deleted {$count} documents by group in " . ($end - $start) . " seconds"); + Console::info("Deleted {$count} rows by group in " . ($end - $start) . " seconds"); } /** * @param Document $database - * @param Document $collection + * @param Document $table * @param Document $project * @param Realtime $queueForRealtime - * @param Document|null $attribute + * @param Document|null $column * @param Document|null $index * @return void */ protected function trigger( - Document $database, - Document $collection, - Document $project, - string $event, - Realtime $queueForRealtime, - Document|null $attribute = null, + Document $database, + Document $table, + Document $project, + string $event, + Realtime $queueForRealtime, + Document|null $column = null, Document|null $index = null, ): void { $queueForRealtime @@ -609,14 +609,14 @@ class Databases extends Action ->setSubscribers(['console']) ->setEvent($event) ->setParam('databaseId', $database->getId()) - ->setParam('collectionId', $collection->getId()); + ->setParam('tableId', $table->getId()); - if ($attribute !== null && !empty($attribute)) { + if (! empty($column)) { $queueForRealtime - ->setParam('attributeId', $attribute->getId()) - ->setPayload($attribute->getArrayCopy()); + ->setParam('columnId', $column->getId()) + ->setPayload($column->getArrayCopy()); } - if ($index !== null && !empty($index)) { + if (! empty($index)) { $queueForRealtime ->setParam('indexId', $index->getId()) ->setPayload($index->getArrayCopy()); diff --git a/src/Appwrite/Utopia/Request/Filters/DatabaseAliases.php b/src/Appwrite/Utopia/Request/Filters/DatabaseAliases.php new file mode 100644 index 0000000000..b2b772be11 --- /dev/null +++ b/src/Appwrite/Utopia/Request/Filters/DatabaseAliases.php @@ -0,0 +1,38 @@ + 'rowId', + 'attributes' => 'columns', + 'collectionId' => 'tableId', + 'attributeId' => 'columnId', + 'relatedCollectionId' => 'relatedTableId' + ]; + + public function parse(array $content, string $model): array + { + return $this->overrideDatabaseParams($content, $model); + } + + protected function overrideDatabaseParams(array $content, string $model): array + { + if (!str_starts_with($model, 'databases.')) { + return $content; + } + + $intersect = array_intersect_key(self::PARAMS_MAP, $content); + + foreach ($intersect as $oldKey => $newKey) { + $content[$newKey] = $content[$oldKey]; + unset($content[$oldKey]); + } + + return $content; + } +} diff --git a/src/Appwrite/Utopia/Request/Filters/V19.php b/src/Appwrite/Utopia/Request/Filters/V19.php index 9597f40570..041c126a69 100644 --- a/src/Appwrite/Utopia/Request/Filters/V19.php +++ b/src/Appwrite/Utopia/Request/Filters/V19.php @@ -6,32 +6,18 @@ use Appwrite\Utopia\Request\Filter; class V19 extends Filter { + // Convert 1.6 params to 1.7 public function parse(array $content, string $model): array { - return $this->overrideDatabaseParams($content, $model); - } - - // Database terminology change handling. - protected function overrideDatabaseParams(array $content, string $model): array - { - if (!str_starts_with($model, 'databases.')) { - return $content; - } - - $map = [ - 'collectionId' => 'tableId', - 'attributeId' => 'columnId', - 'attributes' => 'columns', - 'documentId' => 'rowId', - 'relatedCollectionId' => 'relatedTableId' - ]; - - foreach ($map as $oldKey => $newKey) { - if (isset($content[$oldKey])) { - $content[$newKey] = $content[$oldKey]; - unset($content[$oldKey]); - } + /* + Uncomment with first request filter; current is just a copy of V18 + switch ($model) { + case 'functions.create': + $content['something'] = $content['somethingElse'] ?? ""; + unset($content['something']); + break; } + */ return $content; } diff --git a/src/Appwrite/Utopia/Response/Model/Webhook.php b/src/Appwrite/Utopia/Response/Model/Webhook.php index 57abb4900d..8e53b41e6e 100644 --- a/src/Appwrite/Utopia/Response/Model/Webhook.php +++ b/src/Appwrite/Utopia/Response/Model/Webhook.php @@ -49,7 +49,7 @@ class Webhook extends Model 'type' => self::TYPE_STRING, 'description' => 'Webhook trigger events.', 'default' => [], - 'example' => 'database.collections.update', + 'example' => 'database.tables.update', 'array' => true, ]) ->addRule('security', [