diff --git a/.env.example b/.env.example index 9349b002..2a24f720 100644 --- a/.env.example +++ b/.env.example @@ -1,14 +1,32 @@ -DESTINATION_APPWRITE_TEST_PROJECT=testProject -DESTINATION_APPWRITE_TEST_ENDPOINT=http://localhost/v1 -DESTINATION_APPWRITE_TEST_KEY=xxxxxxxxxxxxxxxxxx +# Providers +SOURCE_PROVIDER=appwrite +DESTINATION_PROVIDER=appwrite + +# Appwrite (Database mode) +SOURCE_APPWRITE_TEST_DSN=mysql:host=127.0.0.1;port=3306;dbname=appwrite +SOURCE_APPWRITE_TEST_USER=user +SOURCE_APPWRITE_TEST_PASSWORD=password +SOURCE_APPWRITE_TEST_NAMESPACE=1 + +DESTINATION_APPWRITE_TEST_DSN=mysql:host=127.0.0.1;port=3306;dbname=appwrite +DESTINATION_APPWRITE_TEST_USER=user +DESTINATION_APPWRITE_TEST_PASSWORD=password +DESTINATION_APPWRITE_TEST_NAMESPACE=2 +# Appwrite (API Key mode) SOURCE_APPWRITE_TEST_PROJECT=testProject SOURCE_APPWRITE_TEST_ENDPOINT=http://localhost/v1 SOURCE_APPWRITE_TEST_KEY=xxxxxxxxxxxxxxxxxx +DESTINATION_APPWRITE_TEST_PROJECT=testProject +DESTINATION_APPWRITE_TEST_ENDPOINT=http://localhost/v1 +DESTINATION_APPWRITE_TEST_KEY=xxxxxxxxxxxxxxxxxx + +# Firebase FIREBASE_TEST_PROJECT=testProject FIREBASE_TEST_ACCOUNT='{type: "service_account", ...}' +# Supabase SUPABASE_TEST_ENDPOINT=https://xxxxxxxxxxxx.supabase.co SUPABASE_TEST_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxx SUPABASE_TEST_HOST=db.xxxxxxxxxxxxxxxx.supabase.co @@ -16,6 +34,7 @@ SUPABASE_TEST_DATABASE=postgres SUPABASE_TEST_USERNAME=postgres SUPABASE_TEST_PASSWORD=xxxxxxxxxxxxxxxxxxxxx +# NHost NHOST_TEST_SUBDOMAIN=xxxxxxxxxxx NHOST_TEST_REGION=eu-central-1 NHOST_TEST_SECRET=xxxxxxxxxxxxxxxxx diff --git a/bin/MigrationCLI.php b/bin/MigrationCLI.php index ab616333..96cd99dd 100644 --- a/bin/MigrationCLI.php +++ b/bin/MigrationCLI.php @@ -215,7 +215,7 @@ public function getSource(): Source $_ENV['SOURCE_APPWRITE_TEST_ENDPOINT'], $_ENV['SOURCE_APPWRITE_TEST_KEY'], Appwrite::SOURCE_DATABASE, - $this->getDatabase(), + $this->getDatabase('source'), ); case 'supabase': return new Supabase( @@ -252,7 +252,7 @@ public function getDestination(): Destination $_ENV['DESTINATION_APPWRITE_TEST_PROJECT'], $_ENV['DESTINATION_APPWRITE_TEST_ENDPOINT'], $_ENV['DESTINATION_APPWRITE_TEST_KEY'], - $this->getDatabase(), + $this->getDatabase('destination'), self::STRUCTURE ); case 'local': @@ -262,7 +262,7 @@ public function getDestination(): Destination } } - public function getDatabase(): Database + public function getDatabase(string $type): Database { Database::addFilter( 'subQueryAttributes', @@ -277,12 +277,21 @@ function (mixed $value, Document $document, Database $database) { ]); foreach ($attributes as $attribute) { - if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) { - $options = $attribute->getAttribute('options'); - foreach ($options as $key => $value) { - $attribute->setAttribute($key, $value); - } - $attribute->removeAttribute('options'); + $attributeType = $attribute->getAttribute('type'); + + switch ($attributeType) { + case Database::VAR_RELATIONSHIP: + $options = $attribute->getAttribute('options'); + foreach ($options as $key => $value) { + $attribute->setAttribute($key, $value); + } + $attribute->removeAttribute('options'); + break; + + case Database::VAR_STRING: + $filters = $attribute->getAttribute('filters', []); + $attribute->setAttribute('encrypt', in_array('encrypt', $filters)); + break; } } @@ -363,10 +372,16 @@ function (mixed $value, Document $attribute) { } ); + $prefix = match ($type) { + 'source' => 'SOURCE_APPWRITE_TEST_', + 'destination' => 'DESTINATION_APPWRITE_TEST_', + default => throw new Exception('Invalid type for database'), + }; + $database = new Database(new MariaDB(new PDO( - $_ENV['DESTINATION_APPWRITE_TEST_DSN'], - $_ENV['DESTINATION_APPWRITE_TEST_USER'], - $_ENV['DESTINATION_APPWRITE_TEST_PASSWORD'], + $_ENV[$prefix . 'DSN'], + $_ENV[$prefix . 'USER'], + $_ENV[$prefix . 'PASSWORD'], [ PDO::ATTR_TIMEOUT => 3, PDO::ATTR_PERSISTENT => true, @@ -378,7 +393,7 @@ function (mixed $value, Document $attribute) { $database ->setDatabase('appwrite') - ->setNamespace('_' . $_ENV['DESTINATION_APPWRITE_TEST_NAMESPACE']); + ->setNamespace('_' . $_ENV[$prefix . 'NAMESPACE']); return $database; } diff --git a/src/Migration/Cache.php b/src/Migration/Cache.php index 7befd48c..c0c4af97 100644 --- a/src/Migration/Cache.php +++ b/src/Migration/Cache.php @@ -39,10 +39,10 @@ public function add(Resource $resource): void $resource->setSequence(uniqid()); } - if ($resource->getName() == Resource::TYPE_DOCUMENT) { + if ($resource->getName() == Resource::TYPE_ROW || $resource->getName() == Resource::TYPE_DOCUMENT) { $status = $resource->getStatus(); - $documentId = $resource->getSequence(); - $this->cache[$resource->getName()][$documentId] = $status; + $rowId = $resource->getSequence(); + $this->cache[$resource->getName()][$rowId] = $status; return; } @@ -78,14 +78,14 @@ public function addAll(array $resources): void */ public function update(Resource $resource): void { - // if documents then updating the status counter only - if ($resource->getName() == Resource::TYPE_DOCUMENT) { - $documentId = $resource->getSequence(); - if (!isset($this->cache[$resource->getName()][$documentId])) { + // if rows then updating the status counter only + if ($resource->getName() == Resource::TYPE_ROW || $resource->getName() == Resource::TYPE_DOCUMENT) { + $rowId = $resource->getSequence(); + if (!isset($this->cache[$resource->getName()][$rowId])) { $this->add($resource); } else { $status = $resource->getStatus(); - $this->cache[$resource->getName()][$documentId] = $status; + $this->cache[$resource->getName()][$rowId] = $status; } return; } @@ -119,7 +119,7 @@ public function updateAll(array $resources): void */ public function remove(Resource $resource): void { - if ($resource->getName() === Resource::TYPE_DOCUMENT) { + if ($resource->getName() == Resource::TYPE_ROW || $resource->getName() == Resource::TYPE_DOCUMENT) { if (! isset($this->cache[$resource->getName()][$resource->getSequence()])) { throw new \Exception('Resource does not exist in cache'); } diff --git a/src/Migration/Destinations/Appwrite.php b/src/Migration/Destinations/Appwrite.php index c915eb25..9505dfad 100644 --- a/src/Migration/Destinations/Appwrite.php +++ b/src/Migration/Destinations/Appwrite.php @@ -33,11 +33,11 @@ use Utopia\Migration\Resources\Auth\Membership; use Utopia\Migration\Resources\Auth\Team; use Utopia\Migration\Resources\Auth\User; -use Utopia\Migration\Resources\Database\Attribute; -use Utopia\Migration\Resources\Database\Collection; +use Utopia\Migration\Resources\Database\Column; use Utopia\Migration\Resources\Database\Database; -use Utopia\Migration\Resources\Database\Document; use Utopia\Migration\Resources\Database\Index; +use Utopia\Migration\Resources\Database\Row; +use Utopia\Migration\Resources\Database\Table; use Utopia\Migration\Resources\Functions\Deployment; use Utopia\Migration\Resources\Functions\EnvVar; use Utopia\Migration\Resources\Functions\Func; @@ -52,7 +52,6 @@ class Appwrite extends Destination protected string $key; - private Functions $functions; private Storage $storage; private Teams $teams; @@ -61,7 +60,7 @@ class Appwrite extends Destination /** * @var array */ - private array $documentBuffer = []; + private array $rowBuffer = []; /** * @param string $project @@ -110,10 +109,15 @@ public static function getSupportedResources(): array // Database Resource::TYPE_DATABASE, - Resource::TYPE_COLLECTION, - Resource::TYPE_ATTRIBUTE, + Resource::TYPE_TABLE, + Resource::TYPE_COLUMN, Resource::TYPE_INDEX, + Resource::TYPE_ROW, + + // legacy Resource::TYPE_DOCUMENT, + Resource::TYPE_ATTRIBUTE, + Resource::TYPE_COLLECTION, // Storage Resource::TYPE_BUCKET, @@ -272,21 +276,24 @@ public function importDatabaseResource(Resource $resource, bool $isLast): Resour /** @var Database $resource */ $success = $this->createDatabase($resource); break; + case Resource::TYPE_TABLE: case Resource::TYPE_COLLECTION: - /** @var Collection $resource */ - $success = $this->createCollection($resource); + /** @var Table $resource */ + $success = $this->createTable($resource); break; + case Resource::TYPE_COLUMN: case Resource::TYPE_ATTRIBUTE: - /** @var Attribute $resource */ - $success = $this->createAttribute($resource); + /** @var Column $resource */ + $success = $this->createColumn($resource); break; case Resource::TYPE_INDEX: /** @var Index $resource */ $success = $this->createIndex($resource); break; + case Resource::TYPE_ROW: case Resource::TYPE_DOCUMENT: - /** @var Document $resource */ - $success = $this->createDocument($resource, $isLast); + /** @var Row $resource */ + $success = $this->createRow($resource, $isLast); break; default: $success = false; @@ -334,10 +341,11 @@ protected function createDatabase(Database $resource): bool $resource->setSequence($database->getSequence()); - $attributes = \array_map( + $columns = \array_map( fn ($attr) => new UtopiaDocument($attr), $this->collectionStructure['attributes'] ); + $indexes = \array_map( fn ($index) => new UtopiaDocument($index), $this->collectionStructure['indexes'] @@ -345,7 +353,7 @@ protected function createDatabase(Database $resource): bool $this->database->createCollection( 'database_' . $database->getSequence(), - $attributes, + $columns, $indexes ); @@ -358,7 +366,7 @@ protected function createDatabase(Database $resource): bool * @throws StructureException * @throws Exception */ - protected function createCollection(Collection $resource): bool + protected function createTable(Table $resource): bool { if ($resource->getId() == 'unique()') { $resource->setId(ID::unique()); @@ -389,25 +397,25 @@ protected function createCollection(Collection $resource): bool ); } - $collection = $this->database->createDocument('database_' . $database->getSequence(), new UtopiaDocument([ + $table = $this->database->createDocument('database_' . $database->getSequence(), new UtopiaDocument([ '$id' => $resource->getId(), 'databaseInternalId' => $database->getSequence(), 'databaseId' => $resource->getDatabase()->getId(), '$permissions' => Permission::aggregate($resource->getPermissions()), - 'documentSecurity' => $resource->getDocumentSecurity(), + 'documentSecurity' => $resource->getRowSecurity(), 'enabled' => $resource->getEnabled(), - 'name' => $resource->getCollectionName(), - 'search' => implode(' ', [$resource->getId(), $resource->getCollectionName()]), + 'name' => $resource->getTableName(), + 'search' => implode(' ', [$resource->getId(), $resource->getTableName()]), '$createdAt' => $resource->getCreatedAt(), '$updatedAt' => $resource->getUpdatedAt(), ])); - $resource->setSequence($collection->getSequence()); + $resource->setSequence($table->getSequence()); $this->database->createCollection( 'database_' . $database->getSequence() . '_collection_' . $resource->getSequence(), permissions: $resource->getPermissions(), - documentSecurity: $resource->getDocumentSecurity() + documentSecurity: $resource->getRowSecurity() ); return true; @@ -418,25 +426,25 @@ protected function createCollection(Collection $resource): bool * @throws \Exception * @throws \Throwable */ - protected function createAttribute(Attribute $resource): bool + protected function createColumn(Column $resource): bool { $type = match ($resource->getType()) { - Attribute::TYPE_DATETIME => UtopiaDatabase::VAR_DATETIME, - Attribute::TYPE_BOOLEAN => UtopiaDatabase::VAR_BOOLEAN, - Attribute::TYPE_INTEGER => UtopiaDatabase::VAR_INTEGER, - Attribute::TYPE_FLOAT => UtopiaDatabase::VAR_FLOAT, - Attribute::TYPE_RELATIONSHIP => UtopiaDatabase::VAR_RELATIONSHIP, - Attribute::TYPE_STRING, - Attribute::TYPE_IP, - Attribute::TYPE_EMAIL, - Attribute::TYPE_URL, - Attribute::TYPE_ENUM => UtopiaDatabase::VAR_STRING, + Column::TYPE_DATETIME => UtopiaDatabase::VAR_DATETIME, + Column::TYPE_BOOLEAN => UtopiaDatabase::VAR_BOOLEAN, + Column::TYPE_INTEGER => UtopiaDatabase::VAR_INTEGER, + Column::TYPE_FLOAT => UtopiaDatabase::VAR_FLOAT, + Column::TYPE_RELATIONSHIP => UtopiaDatabase::VAR_RELATIONSHIP, + Column::TYPE_STRING, + Column::TYPE_IP, + Column::TYPE_EMAIL, + Column::TYPE_URL, + Column::TYPE_ENUM => UtopiaDatabase::VAR_STRING, default => throw new \Exception('Invalid resource type '.$resource->getType()), }; $database = $this->database->getDocument( 'databases', - $resource->getCollection()->getDatabase()->getId(), + $resource->getTable()->getDatabase()->getId(), ); if ($database->isEmpty()) { @@ -448,17 +456,17 @@ protected function createAttribute(Attribute $resource): bool ); } - $collection = $this->database->getDocument( + $table = $this->database->getDocument( 'database_' . $database->getSequence(), - $resource->getCollection()->getId(), + $resource->getTable()->getId(), ); - if ($collection->isEmpty()) { + if ($table->isEmpty()) { throw new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), - message: 'Collection not found', + message: 'Table not found', ); } @@ -468,50 +476,53 @@ protected function createAttribute(Attribute $resource): bool resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), - message: "Format {$resource->getFormat()} not available for attribute type {$type}", + message: "Format {$resource->getFormat()} not available for column type {$type}", ); } } + if ($resource->isRequired() && $resource->getDefault() !== null) { throw new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), - message: 'Cannot set default value for required attribute', + message: 'Cannot set default value for required column', ); } + if ($resource->isArray() && $resource->getDefault() !== null) { throw new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), - message: 'Cannot set default value for array attribute', + message: 'Cannot set default value for array column', ); } + if ($type === UtopiaDatabase::VAR_RELATIONSHIP) { $resource->getOptions()['side'] = UtopiaDatabase::RELATION_SIDE_PARENT; - $relatedCollection = $this->database->getDocument( + $relatedTable = $this->database->getDocument( 'database_' . $database->getSequence(), $resource->getOptions()['relatedCollection'] ); - if ($relatedCollection->isEmpty()) { + if ($relatedTable->isEmpty()) { throw new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), - message: 'Related collection not found', + message: 'Related table not found', ); } } try { - $attribute = new UtopiaDocument([ - '$id' => ID::custom($database->getSequence() . '_' . $collection->getSequence() . '_' . $resource->getKey()), + $column = new UtopiaDocument([ + '$id' => ID::custom($database->getSequence() . '_' . $table->getSequence() . '_' . $resource->getKey()), 'key' => $resource->getKey(), 'databaseInternalId' => $database->getSequence(), 'databaseId' => $database->getId(), - 'collectionInternalId' => $collection->getSequence(), - 'collectionId' => $collection->getId(), + 'collectionInternalId' => $table->getSequence(), + 'collectionId' => $table->getId(), 'type' => $type, 'status' => 'available', 'size' => $resource->getSize(), @@ -527,9 +538,9 @@ protected function createAttribute(Attribute $resource): bool '$updatedAt' => $resource->getUpdatedAt(), ]); - $this->database->checkAttribute($collection, $attribute); + $this->database->checkAttribute($table, $column); - $attribute = $this->database->createDocument('attributes', $attribute); + $column = $this->database->createDocument('attributes', $column); } catch (DuplicateException) { throw new Exception( resourceName: $resource->getName(), @@ -545,31 +556,31 @@ protected function createAttribute(Attribute $resource): bool message: 'Attribute limit exceeded', ); } catch (\Throwable $e) { - $this->database->purgeCachedDocument('database_' . $database->getSequence(), $collection->getId()); - $this->database->purgeCachedCollection('database_' . $database->getSequence() . '_collection_' . $collection->getSequence()); + $this->database->purgeCachedDocument('database_' . $database->getSequence(), $table->getId()); + $this->database->purgeCachedCollection('database_' . $database->getSequence() . '_collection_' . $table->getSequence()); throw $e; } - $this->database->purgeCachedDocument('database_' . $database->getSequence(), $collection->getId()); - $this->database->purgeCachedCollection('database_' . $database->getSequence() . '_collection_' . $collection->getSequence()); + $this->database->purgeCachedDocument('database_' . $database->getSequence(), $table->getId()); + $this->database->purgeCachedCollection('database_' . $database->getSequence() . '_collection_' . $table->getSequence()); $options = $resource->getOptions(); $twoWayKey = null; if ($type === UtopiaDatabase::VAR_RELATIONSHIP && $options['twoWay']) { $twoWayKey = $options['twoWayKey']; - $options['relatedCollection'] = $collection->getId(); + $options['relatedCollection'] = $table->getId(); $options['twoWayKey'] = $resource->getKey(); $options['side'] = UtopiaDatabase::RELATION_SIDE_CHILD; try { $twoWayAttribute = new UtopiaDocument([ - '$id' => ID::custom($database->getSequence() . '_' . $relatedCollection->getSequence() . '_' . $twoWayKey), + '$id' => ID::custom($database->getSequence() . '_' . $relatedTable->getSequence() . '_' . $twoWayKey), 'key' => $twoWayKey, 'databaseInternalId' => $database->getSequence(), 'databaseId' => $database->getId(), - 'collectionInternalId' => $relatedCollection->getSequence(), - 'collectionId' => $relatedCollection->getId(), + 'collectionInternalId' => $relatedTable->getSequence(), + 'collectionId' => $relatedTable->getId(), 'type' => $type, 'status' => 'available', 'size' => $resource->getSize(), @@ -587,7 +598,7 @@ protected function createAttribute(Attribute $resource): bool $this->database->createDocument('attributes', $twoWayAttribute); } catch (DuplicateException) { - $this->database->deleteDocument('attributes', $attribute->getId()); + $this->database->deleteDocument('attributes', $column->getId()); throw new Exception( resourceName: $resource->getName(), @@ -596,17 +607,17 @@ protected function createAttribute(Attribute $resource): bool message: 'Attribute already exists', ); } catch (LimitException) { - $this->database->deleteDocument('attributes', $attribute->getId()); + $this->database->deleteDocument('attributes', $column->getId()); throw new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), - message: 'Attribute limit exceeded', + message: 'Column limit exceeded', ); } catch (\Throwable $e) { - $this->database->purgeCachedDocument('database_' . $database->getSequence(), $relatedCollection->getId()); - $this->database->purgeCachedCollection('database_' . $database->getSequence() . '_collection_' . $relatedCollection->getSequence()); + $this->database->purgeCachedDocument('database_' . $database->getSequence(), $relatedTable->getId()); + $this->database->purgeCachedCollection('database_' . $database->getSequence() . '_collection_' . $relatedTable->getSequence()); throw $e; } } @@ -615,8 +626,8 @@ protected function createAttribute(Attribute $resource): bool switch ($type) { case UtopiaDatabase::VAR_RELATIONSHIP: if (!$this->database->createRelationship( - collection: 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), - relatedCollection: 'database_' . $database->getSequence() . '_collection_' . $relatedCollection->getSequence(), + collection: 'database_' . $database->getSequence() . '_collection_' . $table->getSequence(), + relatedCollection: 'database_' . $database->getSequence() . '_collection_' . $relatedTable->getSequence(), type: $options['relationType'], twoWay: $options['twoWay'], id: $resource->getKey(), @@ -633,7 +644,7 @@ protected function createAttribute(Attribute $resource): bool break; default: if (!$this->database->createAttribute( - 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), + 'database_' . $database->getSequence() . '_collection_' . $table->getSequence(), $resource->getKey(), $type, $resource->getSize(), @@ -645,11 +656,11 @@ protected function createAttribute(Attribute $resource): bool $resource->getFormatOptions(), $resource->getFilters(), )) { - throw new \Exception('Failed to create Attribute'); + throw new \Exception('Failed to create Column'); } } } catch (\Throwable) { - $this->database->deleteDocument('attributes', $attribute->getId()); + $this->database->deleteDocument('attributes', $column->getId()); if (isset($twoWayAttribute)) { $this->database->deleteDocument('attributes', $twoWayAttribute->getId()); @@ -659,16 +670,16 @@ protected function createAttribute(Attribute $resource): bool resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), - message: 'Failed to create attribute', + message: 'Failed to create column', ); } if ($type === UtopiaDatabase::VAR_RELATIONSHIP && $options['twoWay']) { - $this->database->purgeCachedDocument('database_' . $database->getSequence(), $relatedCollection->getId()); + $this->database->purgeCachedDocument('database_' . $database->getSequence(), $relatedTable->getId()); } - $this->database->purgeCachedDocument('database_' . $database->getSequence(), $collection->getId()); - $this->database->purgeCachedCollection('database_' . $database->getSequence() . '_collection_' . $collection->getSequence()); + $this->database->purgeCachedDocument('database_' . $database->getSequence(), $table->getId()); + $this->database->purgeCachedCollection('database_' . $database->getSequence() . '_collection_' . $table->getSequence()); return true; } @@ -681,7 +692,7 @@ protected function createIndex(Index $resource): bool { $database = $this->database->getDocument( 'databases', - $resource->getCollection()->getDatabase()->getId(), + $resource->getTable()->getDatabase()->getId(), ); if ($database->isEmpty()) { throw new Exception( @@ -692,21 +703,21 @@ protected function createIndex(Index $resource): bool ); } - $collection = $this->database->getDocument( + $table = $this->database->getDocument( 'database_' . $database->getSequence(), - $resource->getCollection()->getId(), + $resource->getTable()->getId(), ); - if ($collection->isEmpty()) { + if ($table->isEmpty()) { throw new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), - message: 'Collection not found', + message: 'Table not found', ); } $count = $this->database->count('indexes', [ - Query::equal('collectionInternalId', [$collection->getSequence()]), + Query::equal('collectionInternalId', [$table->getSequence()]), Query::equal('databaseInternalId', [$database->getSequence()]) ], $this->database->getLimitForIndexes()); @@ -715,21 +726,21 @@ protected function createIndex(Index $resource): bool resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), - message: 'Index limit reached for collection', + message: 'Index limit reached for table', ); } /** - * @var array $collectionAttributes + * @var array $tableColumns */ - $collectionAttributes = $collection->getAttribute('attributes'); + $tableColumns = $table->getAttribute('attributes'); - $oldAttributes = \array_map( + $oldColumns = \array_map( fn ($attr) => $attr->getArrayCopy(), - $collectionAttributes + $tableColumns ); - $oldAttributes[] = [ + $oldColumns[] = [ 'key' => '$id', 'type' => UtopiaDatabase::VAR_STRING, 'status' => 'available', @@ -738,7 +749,8 @@ protected function createIndex(Index $resource): bool 'default' => null, 'size' => UtopiaDatabase::LENGTH_KEY ]; - $oldAttributes[] = [ + + $oldColumns[] = [ 'key' => '$createdAt', 'type' => UtopiaDatabase::VAR_DATETIME, 'status' => 'available', @@ -748,7 +760,8 @@ protected function createIndex(Index $resource): bool 'default' => null, 'size' => 0 ]; - $oldAttributes[] = [ + + $oldColumns[] = [ 'key' => '$updatedAt', 'type' => UtopiaDatabase::VAR_DATETIME, 'status' => 'available', @@ -762,63 +775,63 @@ protected function createIndex(Index $resource): bool // Lengths hidden by default $lengths = []; - foreach ($resource->getAttributes() as $i => $attribute) { + foreach ($resource->getColumns() as $i => $column) { // find attribute metadata in collection document - $attributeIndex = \array_search( - $attribute, - \array_column($oldAttributes, 'key') + $columnIndex = \array_search( + $column, + \array_column($oldColumns, 'key') ); - if ($attributeIndex === false) { + if ($columnIndex === false) { throw new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), - message: 'Attribute not found in collection: ' . $attribute, + message: 'Column not found in table: ' . $column, ); } - $attributeStatus = $oldAttributes[$attributeIndex]['status']; - $attributeType = $oldAttributes[$attributeIndex]['type']; - $attributeSize = $oldAttributes[$attributeIndex]['size']; - $attributeArray = $oldAttributes[$attributeIndex]['array'] ?? false; + $columnStatus = $oldColumns[$columnIndex]['status']; + $columnType = $oldColumns[$columnIndex]['type']; + $columnSize = $oldColumns[$columnIndex]['size']; + $columnArray = $oldColumns[$columnIndex]['array'] ?? false; - if ($attributeType === UtopiaDatabase::VAR_RELATIONSHIP) { + if ($columnType === UtopiaDatabase::VAR_RELATIONSHIP) { throw new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), - message: 'Relationship attributes are not supported in indexes', + message: 'Relationship columns are not supported in indexes', ); } // Ensure attribute is available - if ($attributeStatus !== 'available') { + if ($columnStatus !== 'available') { throw new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), - message: 'Attribute not available: ' . $attribute, + message: 'Column not available: ' . $column, ); } $lengths[$i] = null; - if ($attributeArray === true) { + if ($columnArray === true) { $lengths[$i] = UtopiaDatabase::ARRAY_INDEX_LENGTH; } } $index = new UtopiaDocument([ - '$id' => ID::custom($database->getSequence() . '_' . $collection->getSequence() . '_' . $resource->getKey()), + '$id' => ID::custom($database->getSequence() . '_' . $table->getSequence() . '_' . $resource->getKey()), 'key' => $resource->getKey(), 'status' => 'available', // processing, available, failed, deleting, stuck 'databaseInternalId' => $database->getSequence(), 'databaseId' => $database->getId(), - 'collectionInternalId' => $collection->getSequence(), - 'collectionId' => $collection->getId(), + 'collectionInternalId' => $table->getSequence(), + 'collectionId' => $table->getId(), 'type' => $resource->getType(), - 'attributes' => $resource->getAttributes(), + 'attributes' => $resource->getColumns(), 'lengths' => $lengths, 'orders' => $resource->getOrders(), '$createdAt' => $resource->getCreatedAt(), @@ -826,7 +839,7 @@ protected function createIndex(Index $resource): bool ]); $validator = new IndexValidator( - $collectionAttributes, + $tableColumns, $this->database->getAdapter()->getMaxIndexLength(), $this->database->getAdapter()->getInternalIndexesKeys() ); @@ -844,10 +857,10 @@ protected function createIndex(Index $resource): bool try { $result = $this->database->createIndex( - 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), + 'database_' . $database->getSequence() . '_collection_' . $table->getSequence(), $resource->getKey(), $resource->getType(), - $resource->getAttributes(), + $resource->getColumns(), $lengths, $resource->getOrders() ); @@ -873,7 +886,7 @@ protected function createIndex(Index $resource): bool $this->database->purgeCachedDocument( 'database_' . $database->getSequence(), - $collection->getId() + $table->getId() ); return true; @@ -885,7 +898,7 @@ protected function createIndex(Index $resource): bool * @throws StructureException * @throws Exception */ - protected function createDocument(Document $resource, bool $isLast): bool + protected function createRow(Row $resource, bool $isLast): bool { if ($resource->getId() == 'unique()') { $resource->setId(ID::unique()); @@ -905,18 +918,18 @@ protected function createDocument(Document $resource, bool $isLast): bool // Check if document has already been created $exists = \array_key_exists( $resource->getId(), - $this->cache->get(Resource::TYPE_DOCUMENT) + $this->cache->get(Resource::TYPE_ROW) ); if ($exists) { $resource->setStatus( Resource::STATUS_SKIPPED, - 'Document has already been created' + 'Row has already been created' ); return false; } - $this->documentBuffer[] = new UtopiaDocument(\array_merge([ + $this->rowBuffer[] = new UtopiaDocument(\array_merge([ '$id' => $resource->getId(), '$permissions' => $resource->getPermissions(), ], $resource->getData())); @@ -925,29 +938,30 @@ protected function createDocument(Document $resource, bool $isLast): bool try { $database = $this->database->getDocument( 'databases', - $resource->getCollection()->getDatabase()->getId(), + $resource->getTable()->getDatabase()->getId(), ); - $collection = $this->database->getDocument( + + $table = $this->database->getDocument( 'database_' . $database->getSequence(), - $resource->getCollection()->getId(), + $resource->getTable()->getId(), ); $databaseInternalId = $database->getSequence(); - $collectionInternalId = $collection->getSequence(); + $tableInternalId = $table->getSequence(); /** * This is in case an attribute was deleted from Appwrite attributes collection but was not deleted from the table * When creating an archive we select * which will include orphan attribute from the schema */ - foreach ($this->documentBuffer as $document) { - foreach ($document as $key => $value) { + foreach ($this->rowBuffer as $row) { + foreach ($row as $key => $value) { if (\str_starts_with($key, '$')) { continue; } /** @var \Utopia\Database\Document $attribute */ $found = false; - foreach ($collection->getAttribute('attributes', []) as $attribute) { + foreach ($table->getAttribute('attributes', []) as $attribute) { if ($attribute->getAttribute('key') == $key) { $found = true; break; @@ -955,18 +969,18 @@ protected function createDocument(Document $resource, bool $isLast): bool } if (! $found) { - $document->removeAttribute($key); + $row->removeAttribute($key); } } } $this->database->skipRelationshipsExistCheck(fn () => $this->database->createDocuments( - 'database_' . $databaseInternalId . '_collection_' . $collectionInternalId, - $this->documentBuffer + 'database_' . $databaseInternalId . '_collection_' . $tableInternalId, + $this->rowBuffer )); } finally { - $this->documentBuffer = []; + $this->rowBuffer = []; } } diff --git a/src/Migration/Destinations/Local.php b/src/Migration/Destinations/Local.php index 68ea59db..f195f1cb 100644 --- a/src/Migration/Destinations/Local.php +++ b/src/Migration/Destinations/Local.php @@ -52,10 +52,15 @@ public static function getSupportedResources(): array // Database Resource::TYPE_DATABASE, - Resource::TYPE_COLLECTION, - Resource::TYPE_ATTRIBUTE, + Resource::TYPE_TABLE, + Resource::TYPE_COLUMN, Resource::TYPE_INDEX, + Resource::TYPE_ROW, + + // legacy Resource::TYPE_DOCUMENT, + Resource::TYPE_ATTRIBUTE, + Resource::TYPE_COLLECTION, // Storage Resource::TYPE_BUCKET, diff --git a/src/Migration/Resource.php b/src/Migration/Resource.php index 32af0e79..9645cc6f 100644 --- a/src/Migration/Resource.php +++ b/src/Migration/Resource.php @@ -26,11 +26,11 @@ abstract class Resource implements \JsonSerializable // Master Resources public const TYPE_BUCKET = 'bucket'; - public const TYPE_COLLECTION = 'collection'; + public const TYPE_TABLE = 'table'; public const TYPE_DATABASE = 'database'; - public const TYPE_DOCUMENT = 'document'; + public const TYPE_ROW = 'row'; public const TYPE_FILE = 'file'; @@ -46,7 +46,7 @@ abstract class Resource implements \JsonSerializable // Children (Resources that are created by other resources) - public const TYPE_ATTRIBUTE = 'attribute'; + public const TYPE_COLUMN = 'column'; public const TYPE_DEPLOYMENT = 'deployment'; @@ -54,12 +54,23 @@ abstract class Resource implements \JsonSerializable public const TYPE_ENVIRONMENT_VARIABLE = 'environment-variable'; + // legacy terminologies + public const TYPE_DOCUMENT = 'document'; + public const TYPE_ATTRIBUTE = 'attribute'; + public const TYPE_COLLECTION = 'collection'; + + private const TYPE_MAP = [ + Resource::TYPE_ROW => Resource::TYPE_DOCUMENT, + Resource::TYPE_COLUMN => Resource::TYPE_ATTRIBUTE, + Resource::TYPE_TABLE => Resource::TYPE_COLLECTION, + ]; + public const ALL_RESOURCES = [ - self::TYPE_ATTRIBUTE, + self::TYPE_COLUMN, self::TYPE_BUCKET, - self::TYPE_COLLECTION, + self::TYPE_TABLE, self::TYPE_DATABASE, - self::TYPE_DOCUMENT, + self::TYPE_ROW, self::TYPE_FILE, self::TYPE_FUNCTION, self::TYPE_DEPLOYMENT, @@ -69,6 +80,11 @@ abstract class Resource implements \JsonSerializable self::TYPE_ENVIRONMENT_VARIABLE, self::TYPE_TEAM, self::TYPE_MEMBERSHIP, + + // legacy + self::TYPE_DOCUMENT, + self::TYPE_ATTRIBUTE, + self::TYPE_COLLECTION, ]; protected string $id = ''; @@ -93,6 +109,21 @@ abstract public static function getName(): string; abstract public function getGroup(): string; + public static function isSupported(string|array $types, array $resources): bool + { + $allTypes = []; + $types = (array) $types; + + foreach ($types as $type) { + $allTypes[] = $type; + if (isset(self::TYPE_MAP[$type])) { + $allTypes[] = self::TYPE_MAP[$type]; + } + } + + return (bool) \array_intersect($resources, $allTypes); + } + public function getId(): string { return $this->id; diff --git a/src/Migration/Resources/Database/Attribute.php b/src/Migration/Resources/Database/Column.php similarity index 79% rename from src/Migration/Resources/Database/Attribute.php rename to src/Migration/Resources/Database/Column.php index dab1d9a9..32279d3f 100644 --- a/src/Migration/Resources/Database/Attribute.php +++ b/src/Migration/Resources/Database/Column.php @@ -5,7 +5,7 @@ use Utopia\Migration\Resource; use Utopia\Migration\Transfer; -abstract class Attribute extends Resource +abstract class Column extends Resource { public const TYPE_STRING = 'string'; public const TYPE_INTEGER = 'integer'; @@ -20,7 +20,7 @@ abstract class Attribute extends Resource /** * @param string $key - * @param Collection $collection + * @param Table $table * @param int $size * @param bool $required * @param mixed|null $default @@ -30,20 +30,22 @@ abstract class Attribute extends Resource * @param array $formatOptions * @param array $filters * @param array $options + * @param string $createdAt + * @param string $updatedAt */ public function __construct( protected readonly string $key, - protected readonly Collection $collection, - protected readonly int $size = 0, - protected readonly bool $required = false, - protected readonly mixed $default = null, - protected readonly bool $array = false, - protected readonly bool $signed = false, + protected readonly Table $table, + protected readonly int $size = 0, + protected readonly bool $required = false, + protected readonly mixed $default = null, + protected readonly bool $array = false, + protected readonly bool $signed = false, protected readonly string $format = '', - protected readonly array $formatOptions = [], - protected readonly array $filters = [], - protected array $options = [], - protected string $createdAt = '', + protected readonly array $formatOptions = [], + protected readonly array $filters = [], + protected array $options = [], + protected string $createdAt = '', protected string $updatedAt = '', ) { } @@ -55,7 +57,7 @@ public function jsonSerialize(): array { return [ 'key' => $this->key, - 'collection' => $this->collection, + 'table' => $this->table, 'type' => $this->getType(), 'size' => $this->size, 'required' => $this->required, @@ -73,7 +75,7 @@ public function jsonSerialize(): array public static function getName(): string { - return Resource::TYPE_ATTRIBUTE; + return Resource::TYPE_COLUMN; } abstract public function getType(): string; @@ -88,9 +90,9 @@ public function getKey(): string return $this->key; } - public function getCollection(): Collection + public function getTable(): Table { - return $this->collection; + return $this->table; } public function getSize(): int diff --git a/src/Migration/Resources/Database/Attributes/Boolean.php b/src/Migration/Resources/Database/Columns/Boolean.php similarity index 62% rename from src/Migration/Resources/Database/Attributes/Boolean.php rename to src/Migration/Resources/Database/Columns/Boolean.php index 05ef1b00..6ae50e32 100644 --- a/src/Migration/Resources/Database/Attributes/Boolean.php +++ b/src/Migration/Resources/Database/Columns/Boolean.php @@ -1,24 +1,24 @@ * }, + * table?: array{ + * database: array{ + * id: string, + * name: string, + * }, + * name: string, + * id: string, + * rowSecurity: bool, + * permissions: ?array + * }, * required: bool, * array: bool, * default: ?bool, @@ -52,7 +62,7 @@ public static function fromArray(array $array): self { return new self( $array['key'], - Collection::fromArray($array['collection']), + Table::fromArray($array['table'] ?? $array['collection']), required: $array['required'], default: $array['default'], array: $array['array'], @@ -63,6 +73,6 @@ public static function fromArray(array $array): self public function getType(): string { - return Attribute::TYPE_BOOLEAN; + return Column::TYPE_BOOLEAN; } } diff --git a/src/Migration/Resources/Database/Attributes/DateTime.php b/src/Migration/Resources/Database/Columns/DateTime.php similarity index 60% rename from src/Migration/Resources/Database/Attributes/DateTime.php rename to src/Migration/Resources/Database/Columns/DateTime.php index e3defdae..a97385d8 100644 --- a/src/Migration/Resources/Database/Attributes/DateTime.php +++ b/src/Migration/Resources/Database/Columns/DateTime.php @@ -1,24 +1,24 @@ * }, + * table?: array{ + * database: array{ + * id: string, + * name: string, + * }, + * name: string, + * id: string, + * rowSecurity: bool, + * permissions: ?array + * }, * required: bool, * array: bool, * default: ?string, @@ -58,7 +68,7 @@ public static function fromArray(array $array): self { return new self( $array['key'], - Collection::fromArray($array['collection']), + Table::fromArray($array['table'] ?? $array['collection']), required: $array['required'], default: $array['default'], array: $array['array'], diff --git a/src/Migration/Resources/Database/Attributes/Decimal.php b/src/Migration/Resources/Database/Columns/Decimal.php similarity index 72% rename from src/Migration/Resources/Database/Attributes/Decimal.php rename to src/Migration/Resources/Database/Columns/Decimal.php index de51a2f2..000d46a0 100644 --- a/src/Migration/Resources/Database/Attributes/Decimal.php +++ b/src/Migration/Resources/Database/Columns/Decimal.php @@ -1,21 +1,21 @@ * }, + * table?: array{ + * database: array{ + * id: string, + * name: string, + * }, + * name: string, + * id: string, + * rowSecurity: bool, + * permissions: ?array + * }, * required: bool, * array: bool, * default: ?float, @@ -67,7 +77,7 @@ public static function fromArray(array $array): self { return new self( $array['key'], - Collection::fromArray($array['collection']), + Table::fromArray($array['table'] ?? $array['collection']), required: $array['required'], default: $array['default'], array: $array['array'], @@ -80,7 +90,7 @@ public static function fromArray(array $array): self public function getType(): string { - return Attribute::TYPE_FLOAT; + return Column::TYPE_FLOAT; } public function getMin(): ?float diff --git a/src/Migration/Resources/Database/Attributes/Email.php b/src/Migration/Resources/Database/Columns/Email.php similarity index 50% rename from src/Migration/Resources/Database/Attributes/Email.php rename to src/Migration/Resources/Database/Columns/Email.php index 69c24626..d5874a94 100644 --- a/src/Migration/Resources/Database/Attributes/Email.php +++ b/src/Migration/Resources/Database/Columns/Email.php @@ -1,25 +1,25 @@ $elements */ public function __construct( - string $key, - Collection $collection, - array $elements, - bool $required = false, + string $key, + Table $table, + array $elements, + bool $required = false, ?string $default = null, - bool $array = false, - int $size = 256, - string $createdAt = '', - string $updatedAt = '' + bool $array = false, + int $size = 256, + string $createdAt = '', + string $updatedAt = '' ) { parent::__construct( $key, - $collection, + $table, size: $size, required: $required, default: $default, @@ -40,7 +40,7 @@ public function __construct( /** * @param array{ * key: string, - * collection: array{ + * collection?: array{ * database: array{ * id: string, * name: string, @@ -50,6 +50,16 @@ public function __construct( * documentSecurity: bool, * permissions: ?array * }, + * table?: array{ + * database: array{ + * id: string, + * name: string, + * }, + * name: string, + * id: string, + * rowSecurity: bool, + * permissions: ?array + * }, * size: int, * required: bool, * default: ?string, @@ -66,7 +76,7 @@ public static function fromArray(array $array): self { return new self( $array['key'], - Collection::fromArray($array['collection']), + Table::fromArray($array['table'] ?? $array['collection']), elements: $array['formatOptions']['elements'], required: $array['required'], default: $array['default'], @@ -79,7 +89,7 @@ public static function fromArray(array $array): self public function getType(): string { - return Attribute::TYPE_ENUM; + return Column::TYPE_ENUM; } /** diff --git a/src/Migration/Resources/Database/Attributes/IP.php b/src/Migration/Resources/Database/Columns/IP.php similarity index 50% rename from src/Migration/Resources/Database/Attributes/IP.php rename to src/Migration/Resources/Database/Columns/IP.php index 88c7248b..06634d59 100644 --- a/src/Migration/Resources/Database/Attributes/IP.php +++ b/src/Migration/Resources/Database/Columns/IP.php @@ -1,25 +1,25 @@ * }, + * table?: array{ + * database: array{ + * id: string, + * name: string, + * }, + * name: string, + * id: string, + * rowSecurity: bool, + * permissions: ?array + * }, * required: bool, * array: bool, * default: ?int, @@ -69,7 +79,7 @@ public static function fromArray(array $array): self { return new self( $array['key'], - Collection::fromArray($array['collection']), + Table::fromArray($array['table'] ?? $array['collection']), required: $array['required'], default: $array['default'], array: $array['array'], @@ -82,7 +92,7 @@ public static function fromArray(array $array): self public function getType(): string { - return Attribute::TYPE_INTEGER; + return Column::TYPE_INTEGER; } public function getMin(): ?int diff --git a/src/Migration/Resources/Database/Attributes/Relationship.php b/src/Migration/Resources/Database/Columns/Relationship.php similarity index 63% rename from src/Migration/Resources/Database/Attributes/Relationship.php rename to src/Migration/Resources/Database/Columns/Relationship.php index 19b62106..d0d3951d 100644 --- a/src/Migration/Resources/Database/Attributes/Relationship.php +++ b/src/Migration/Resources/Database/Columns/Relationship.php @@ -1,30 +1,30 @@ $relatedCollection, + 'relatedTable' => $relatedTable, 'relationType' => $relationType, 'twoWay' => $twoWay, 'twoWayKey' => $twoWayKey, @@ -39,7 +39,7 @@ public function __construct( /** * @param array{ * key: string, - * collection: array{ + * collection?: array{ * database: array{ * id: string, * name: string, @@ -49,6 +49,16 @@ public function __construct( * documentSecurity: bool, * permissions: ?array * }, + * table?: array{ + * database: array{ + * id: string, + * name: string, + * }, + * name: string, + * id: string, + * rowSecurity: bool, + * permissions: ?array + * }, * options: array{ * relatedCollection: string, * relationType: string, @@ -66,8 +76,8 @@ public static function fromArray(array $array): self { return new self( $array['key'], - Collection::fromArray($array['collection']), - relatedCollection: $array['options']['relatedCollection'], + Table::fromArray($array['table'] ?? $array['collection']), + relatedTable: $array['options']['relatedTable'] ?? $array['options']['relatedCollection'], relationType: $array['options']['relationType'], twoWay: $array['options']['twoWay'], twoWayKey: $array['options']['twoWayKey'], @@ -80,12 +90,12 @@ public static function fromArray(array $array): self public function getType(): string { - return Attribute::TYPE_RELATIONSHIP; + return Column::TYPE_RELATIONSHIP; } - public function getRelatedCollection(): string + public function getRelatedTable(): string { - return $this->options['relatedCollection']; + return $this->options['relatedTable'] ?? $this->options['relatedCollection']; } public function getRelationType(): string diff --git a/src/Migration/Resources/Database/Attributes/Text.php b/src/Migration/Resources/Database/Columns/Text.php similarity index 64% rename from src/Migration/Resources/Database/Attributes/Text.php rename to src/Migration/Resources/Database/Columns/Text.php index 1f8381de..452b92d0 100644 --- a/src/Migration/Resources/Database/Attributes/Text.php +++ b/src/Migration/Resources/Database/Columns/Text.php @@ -1,27 +1,27 @@ * }, + * table?: array{ + * database: array{ + * id: string, + * name: string, + * }, + * name: string, + * id: string, + * rowSecurity: bool, + * permissions: ?array + * }, * required: bool, * default: ?string, * array: bool, @@ -59,7 +69,7 @@ public static function fromArray(array $array): self { return new self( $array['key'], - Collection::fromArray($array['collection']), + Table::fromArray($array['table'] ?? $array['collection']), required: $array['required'], default: $array['default'] ?? null, array: $array['array'], @@ -72,7 +82,7 @@ public static function fromArray(array $array): self public function getType(): string { - return Attribute::TYPE_STRING; + return Column::TYPE_STRING; } public function getSize(): int diff --git a/src/Migration/Resources/Database/Attributes/URL.php b/src/Migration/Resources/Database/Columns/URL.php similarity index 50% rename from src/Migration/Resources/Database/Attributes/URL.php rename to src/Migration/Resources/Database/Columns/URL.php index d769a7d3..643fe43b 100644 --- a/src/Migration/Resources/Database/Attributes/URL.php +++ b/src/Migration/Resources/Database/Columns/URL.php @@ -1,25 +1,25 @@ $attributes + * @param array $columns * @param array $lengths * @param array $orders * @param string $createdAt * @param string $updatedAt */ public function __construct( - string $id, + string $id, private readonly string $key, - private readonly Collection $collection, + private readonly Table $table, private readonly string $type = '', - private readonly array $attributes = [], - private readonly array $lengths = [], - private readonly array $orders = [], - protected string $createdAt = '', - protected string $updatedAt = '', + private readonly array $columns = [], + private readonly array $lengths = [], + private readonly array $orders = [], + protected string $createdAt = '', + protected string $updatedAt = '', ) { $this->id = $id; } @@ -42,7 +42,7 @@ public function __construct( * @param array{ * id: string, * key: string, - * collection: array{ + * collection?: array{ * database: array{ * id: string, * name: string, @@ -52,8 +52,19 @@ public function __construct( * documentSecurity: bool, * permissions: ?array * }, + * table?: array{ + * database: array{ + * id: string, + * name: string, + * }, + * name: string, + * id: string, + * rowSecurity: bool, + * permissions: ?array + * }, * type: string, - * attributes: array, + * columns?: array, + * attributes?: array, * lengths: ?array, * orders: ?array, * createdAt: string, @@ -65,9 +76,9 @@ public static function fromArray(array $array): self return new self( $array['id'], $array['key'], - Collection::fromArray($array['collection']), + Table::fromArray($array['table'] ?? $array['collection']), $array['type'], - $array['attributes'], + $array['columns'] ?? $array['attributes'], $array['lengths'] ?? [], $array['orders'] ?? [], createdAt: $array['createdAt'] ?? '', @@ -83,9 +94,9 @@ public function jsonSerialize(): array return [ 'id' => $this->getId(), 'key' => $this->key, - 'collection' => $this->collection, + 'table' => $this->table, 'type' => $this->type, - 'attributes' => $this->attributes, + 'columns' => $this->columns, 'lengths' => $this->lengths, 'orders' => $this->orders, 'createdAt' => $this->createdAt, @@ -108,9 +119,9 @@ public function getKey(): string return $this->key; } - public function getCollection(): Collection + public function getTable(): Table { - return $this->collection; + return $this->table; } public function getType(): string @@ -121,9 +132,9 @@ public function getType(): string /** * @return array */ - public function getAttributes(): array + public function getColumns(): array { - return $this->attributes; + return $this->columns; } /** diff --git a/src/Migration/Resources/Database/Document.php b/src/Migration/Resources/Database/Row.php similarity index 68% rename from src/Migration/Resources/Database/Document.php rename to src/Migration/Resources/Database/Row.php index db3d41ac..cebd88e7 100644 --- a/src/Migration/Resources/Database/Document.php +++ b/src/Migration/Resources/Database/Row.php @@ -5,19 +5,19 @@ use Utopia\Migration\Resource; use Utopia\Migration\Transfer; -class Document extends Resource +class Row extends Resource { /** * @param string $id - * @param Collection $collection + * @param Table $table * @param array $data * @param array $permissions */ public function __construct( - string $id, - private readonly Collection $collection, + string $id, + private readonly Table $table, private readonly array $data = [], - array $permissions = [] + array $permissions = [] ) { $this->id = $id; $this->permissions = $permissions; @@ -26,7 +26,7 @@ public function __construct( /** * @param array{ * id: string, - * collection: array{ + * collection?: array{ * database: array{ * id: string, * name: string, @@ -36,6 +36,16 @@ public function __construct( * documentSecurity: bool, * permissions: ?array * }, + * table?: array{ + * database: array{ + * id: string, + * name: string, + * }, + * name: string, + * id: string, + * rowSecurity: bool, + * permissions: ?array + * }, * data: array, * permissions: ?array * } $array @@ -44,7 +54,7 @@ public static function fromArray(array $array): self { return new self( $array['id'], - Collection::fromArray($array['collection']), + Table::fromArray($array['table'] ?? $array['collection']), $array['data'], $array['permissions'] ?? [] ); @@ -57,7 +67,7 @@ public function jsonSerialize(): array { return [ 'id' => $this->id, - 'collection' => $this->collection, + 'table' => $this->table, 'data' => $this->data, 'permissions' => $this->permissions, ]; @@ -65,7 +75,7 @@ public function jsonSerialize(): array public static function getName(): string { - return Resource::TYPE_DOCUMENT; + return Resource::TYPE_ROW; } public function getGroup(): string @@ -73,9 +83,9 @@ public function getGroup(): string return Transfer::GROUP_DATABASES; } - public function getCollection(): Collection + public function getTable(): Table { - return $this->collection; + return $this->table; } /** diff --git a/src/Migration/Resources/Database/Collection.php b/src/Migration/Resources/Database/Table.php similarity index 79% rename from src/Migration/Resources/Database/Collection.php rename to src/Migration/Resources/Database/Table.php index b928167f..889d90fe 100644 --- a/src/Migration/Resources/Database/Collection.php +++ b/src/Migration/Resources/Database/Table.php @@ -5,22 +5,23 @@ use Utopia\Migration\Resource; use Utopia\Migration\Transfer; -class Collection extends Resource +class Table extends Resource { /** * @param Database $database * @param string $name * @param string $id - * @param bool $documentSecurity + * @param bool $rowSecurity * @param array $permissions * @param string $createdAt * @param string $updatedAt + * @param bool $enabled */ public function __construct( private readonly Database $database, private readonly string $name, string $id, - private readonly bool $documentSecurity = false, + private readonly bool $rowSecurity = false, array $permissions = [], protected string $createdAt = '', protected string $updatedAt = '', @@ -35,13 +36,15 @@ public function __construct( * database: array{ * id: string, * name: string, - * }, + * }, * name: string, * id: string, - * documentSecurity: bool, + * documentSecurity?: bool, + * rowSecurity?: bool, * permissions: ?array, * createdAt: string, - * updatedAt: string + * updatedAt: string, + * enabled: bool * } $array */ public static function fromArray(array $array): self @@ -50,7 +53,7 @@ public static function fromArray(array $array): self Database::fromArray($array['database']), name: $array['name'], id: $array['id'], - documentSecurity: $array['documentSecurity'], + rowSecurity: $array['rowSecurity'] ?? $array['documentSecurity'], permissions: $array['permissions'] ?? [], createdAt: $array['createdAt'] ?? '', updatedAt: $array['updatedAt'] ?? '', @@ -67,7 +70,7 @@ public function jsonSerialize(): array 'database' => $this->database, 'id' => $this->id, 'name' => $this->name, - 'documentSecurity' => $this->documentSecurity, + 'rowSecurity' => $this->rowSecurity, 'permissions' => $this->permissions, 'createdAt' => $this->createdAt, 'updatedAt' => $this->updatedAt, @@ -77,7 +80,7 @@ public function jsonSerialize(): array public static function getName(): string { - return Resource::TYPE_COLLECTION; + return Resource::TYPE_TABLE; } public function getGroup(): string @@ -90,14 +93,14 @@ public function getDatabase(): Database return $this->database; } - public function getCollectionName(): string + public function getTableName(): string { return $this->name; } - public function getDocumentSecurity(): bool + public function getRowSecurity(): bool { - return $this->documentSecurity; + return $this->rowSecurity; } public function getEnabled(): bool diff --git a/src/Migration/Sources/Appwrite.php b/src/Migration/Sources/Appwrite.php index f526b922..06f888bb 100644 --- a/src/Migration/Sources/Appwrite.php +++ b/src/Migration/Sources/Appwrite.php @@ -18,21 +18,21 @@ use Utopia\Migration\Resources\Auth\Membership; use Utopia\Migration\Resources\Auth\Team; use Utopia\Migration\Resources\Auth\User; -use Utopia\Migration\Resources\Database\Attribute; -use Utopia\Migration\Resources\Database\Attributes\Boolean; -use Utopia\Migration\Resources\Database\Attributes\DateTime; -use Utopia\Migration\Resources\Database\Attributes\Decimal; -use Utopia\Migration\Resources\Database\Attributes\Email; -use Utopia\Migration\Resources\Database\Attributes\Enum; -use Utopia\Migration\Resources\Database\Attributes\Integer; -use Utopia\Migration\Resources\Database\Attributes\IP; -use Utopia\Migration\Resources\Database\Attributes\Relationship; -use Utopia\Migration\Resources\Database\Attributes\Text; -use Utopia\Migration\Resources\Database\Attributes\URL; -use Utopia\Migration\Resources\Database\Collection; +use Utopia\Migration\Resources\Database\Column; +use Utopia\Migration\Resources\Database\Columns\Boolean; +use Utopia\Migration\Resources\Database\Columns\DateTime; +use Utopia\Migration\Resources\Database\Columns\Decimal; +use Utopia\Migration\Resources\Database\Columns\Email; +use Utopia\Migration\Resources\Database\Columns\Enum; +use Utopia\Migration\Resources\Database\Columns\Integer; +use Utopia\Migration\Resources\Database\Columns\IP; +use Utopia\Migration\Resources\Database\Columns\Relationship; +use Utopia\Migration\Resources\Database\Columns\Text; +use Utopia\Migration\Resources\Database\Columns\URL; use Utopia\Migration\Resources\Database\Database; -use Utopia\Migration\Resources\Database\Document; use Utopia\Migration\Resources\Database\Index; +use Utopia\Migration\Resources\Database\Row; +use Utopia\Migration\Resources\Database\Table; use Utopia\Migration\Resources\Functions\Deployment; use Utopia\Migration\Resources\Functions\EnvVar; use Utopia\Migration\Resources\Functions\Func; @@ -117,10 +117,15 @@ public static function getSupportedResources(): array // Database Resource::TYPE_DATABASE, - Resource::TYPE_COLLECTION, - Resource::TYPE_ATTRIBUTE, + Resource::TYPE_TABLE, + Resource::TYPE_COLUMN, Resource::TYPE_INDEX, + Resource::TYPE_ROW, + + // legacy Resource::TYPE_DOCUMENT, + Resource::TYPE_ATTRIBUTE, + Resource::TYPE_COLLECTION, // Storage Resource::TYPE_BUCKET, @@ -541,13 +546,13 @@ protected function exportGroupDatabases(int $batchSize, array $resources): void } try { - if (\in_array(Resource::TYPE_COLLECTION, $resources)) { - $this->exportCollections($batchSize); + if (Resource::isSupported(Resource::TYPE_TABLE, $resources)) { + $this->exportTables($batchSize); } } catch (\Throwable $e) { $this->addError( new Exception( - Resource::TYPE_COLLECTION, + Resource::TYPE_TABLE, Transfer::GROUP_DATABASES, message: $e->getMessage(), code: $e->getCode(), @@ -559,13 +564,13 @@ protected function exportGroupDatabases(int $batchSize, array $resources): void } try { - if (\in_array(Resource::TYPE_ATTRIBUTE, $resources)) { - $this->exportAttributes($batchSize); + if (Resource::isSupported(Resource::TYPE_COLUMN, $resources)) { + $this->exportColumns($batchSize); } } catch (\Throwable $e) { $this->addError( new Exception( - Resource::TYPE_ATTRIBUTE, + Resource::TYPE_COLUMN, Transfer::GROUP_DATABASES, message: $e->getMessage(), code: $e->getCode(), @@ -595,13 +600,13 @@ protected function exportGroupDatabases(int $batchSize, array $resources): void } try { - if (\in_array(Resource::TYPE_DOCUMENT, $resources)) { - $this->exportDocuments($batchSize); + if (Resource::isSupported(Resource::TYPE_ROW, $resources)) { + $this->exportRows($batchSize); } } catch (\Throwable $e) { $this->addError( new Exception( - Resource::TYPE_DOCUMENT, + Resource::TYPE_ROW, Transfer::GROUP_DATABASES, message: $e->getMessage(), code: $e->getCode(), @@ -666,47 +671,47 @@ private function exportDatabases(int $batchSize): void * @param int $batchSize * @throws Exception */ - private function exportCollections(int $batchSize): void + private function exportTables(int $batchSize): void { $databases = $this->cache->get(Database::getName()); foreach ($databases as $database) { - $lastCollection = null; + $lastTable = null; /** @var Database $database */ while (true) { $queries = [$this->database->queryLimit($batchSize)]; - $collections = []; + $tables = []; - if ($lastCollection) { - $queries[] = $this->database->queryCursorAfter($lastCollection); + if ($lastTable) { + $queries[] = $this->database->queryCursorAfter($lastTable); } - $response = $this->database->listCollections($database, $queries); + $response = $this->database->listTables($database, $queries); - foreach ($response as $collection) { - $newCollection = new Collection( + foreach ($response as $table) { + $newTable = new Table( $database, - $collection['name'], - $collection['$id'], - $collection['documentSecurity'], - $collection['$permissions'], - $collection['$createdAt'], - $collection['$updatedAt'], + $table['name'], + $table['$id'], + $table['documentSecurity'], + $table['$permissions'], + $table['$createdAt'], + $table['$updatedAt'], ); - $collections[] = $newCollection; + $tables[] = $newTable; } - if (empty($collections)) { + if (empty($tables)) { break; } - $this->callback($collections); + $this->callback($tables); - $lastCollection = $collections[count($collections) - 1]; + $lastTable = $tables[count($tables) - 1]; - if (count($collections) < $batchSize) { + if (count($tables) < $batchSize) { break; } } @@ -717,173 +722,174 @@ private function exportCollections(int $batchSize): void * @param int $batchSize * @throws Exception */ - private function exportAttributes(int $batchSize): void + private function exportColumns(int $batchSize): void { - $collections = $this->cache->get(Collection::getName()); - /** @var Collection[] $collections */ - foreach ($collections as $collection) { - $lastAttribute = null; + $tables = $this->cache->get(Table::getName()); + + /** @var Table[] $tables */ + foreach ($tables as $table) { + $lastColumn = null; while (true) { $queries = [$this->database->queryLimit($batchSize)]; - $attributes = []; + $columns = []; - if ($lastAttribute) { - $queries[] = $this->database->queryCursorAfter($lastAttribute); + if ($lastColumn) { + $queries[] = $this->database->queryCursorAfter($lastColumn); } - $response = $this->database->listAttributes($collection, $queries); + $response = $this->database->listColumns($table, $queries); - foreach ($response as $attribute) { + foreach ($response as $column) { if ( - $attribute['type'] === UtopiaDatabase::VAR_RELATIONSHIP - && $attribute['side'] === UtopiaDatabase::RELATION_SIDE_CHILD + $column['type'] === UtopiaDatabase::VAR_RELATIONSHIP + && $column['side'] === UtopiaDatabase::RELATION_SIDE_CHILD ) { continue; } - switch ($attribute['type']) { - case Attribute::TYPE_STRING: - $attr = match ($attribute['format'] ?? '') { - Attribute::TYPE_EMAIL => new Email( - $attribute['key'], - $collection, - required: $attribute['required'], - default: $attribute['default'], - array: $attribute['array'], - size: $attribute['size'] ?? 254, - createdAt: $attribute['$createdAt'] ?? '', - updatedAt: $attribute['$updatedAt'] ?? '', + switch ($column['type']) { + case Column::TYPE_STRING: + $col = match ($column['format'] ?? '') { + Column::TYPE_EMAIL => new Email( + $column['key'], + $table, + required: $column['required'], + default: $column['default'], + array: $column['array'], + size: $column['size'] ?? 254, + createdAt: $column['$createdAt'] ?? '', + updatedAt: $column['$updatedAt'] ?? '', ), - Attribute::TYPE_ENUM => new Enum( - $attribute['key'], - $collection, - elements: $attribute['elements'], - required: $attribute['required'], - default: $attribute['default'], - array: $attribute['array'], - size: $attribute['size'] ?? UtopiaDatabase::LENGTH_KEY, - createdAt: $attribute['$createdAt'] ?? '', - updatedAt: $attribute['$updatedAt'] ?? '', + Column::TYPE_ENUM => new Enum( + $column['key'], + $table, + elements: $column['elements'], + required: $column['required'], + default: $column['default'], + array: $column['array'], + size: $column['size'] ?? UtopiaDatabase::LENGTH_KEY, + createdAt: $column['$createdAt'] ?? '', + updatedAt: $column['$updatedAt'] ?? '', ), - Attribute::TYPE_URL => new URL( - $attribute['key'], - $collection, - required: $attribute['required'], - default: $attribute['default'], - array: $attribute['array'], - size: $attribute['size'] ?? 2000, - createdAt: $attribute['$createdAt'] ?? '', - updatedAt: $attribute['$updatedAt'] ?? '', + Column::TYPE_URL => new URL( + $column['key'], + $table, + required: $column['required'], + default: $column['default'], + array: $column['array'], + size: $column['size'] ?? 2000, + createdAt: $column['$createdAt'] ?? '', + updatedAt: $column['$updatedAt'] ?? '', ), - Attribute::TYPE_IP => new IP( - $attribute['key'], - $collection, - required: $attribute['required'], - default: $attribute['default'], - array: $attribute['array'], - size: $attribute['size'] ?? 39, - createdAt: $attribute['$createdAt'] ?? '', - updatedAt: $attribute['$updatedAt'] ?? '', + Column::TYPE_IP => new IP( + $column['key'], + $table, + required: $column['required'], + default: $column['default'], + array: $column['array'], + size: $column['size'] ?? 39, + createdAt: $column['$createdAt'] ?? '', + updatedAt: $column['$updatedAt'] ?? '', ), default => new Text( - $attribute['key'], - $collection, - required: $attribute['required'], - default: $attribute['default'], - array: $attribute['array'], - size: $attribute['size'] ?? 0, - createdAt: $attribute['$createdAt'] ?? '', - updatedAt: $attribute['$updatedAt'] ?? '', + $column['key'], + $table, + required: $column['required'], + default: $column['default'], + array: $column['array'], + size: $column['size'] ?? 0, + createdAt: $column['$createdAt'] ?? '', + updatedAt: $column['$updatedAt'] ?? '', ), }; break; - case Attribute::TYPE_BOOLEAN: - $attr = new Boolean( - $attribute['key'], - $collection, - required: $attribute['required'], - default: $attribute['default'], - array: $attribute['array'], - createdAt: $attribute['$createdAt'] ?? '', - updatedAt: $attribute['$updatedAt'] ?? '', + case Column::TYPE_BOOLEAN: + $col = new Boolean( + $column['key'], + $table, + required: $column['required'], + default: $column['default'], + array: $column['array'], + createdAt: $column['$createdAt'] ?? '', + updatedAt: $column['$updatedAt'] ?? '', ); break; - case Attribute::TYPE_INTEGER: - $attr = new Integer( - $attribute['key'], - $collection, - required: $attribute['required'], - default: $attribute['default'], - array: $attribute['array'], - min: $attribute['min'] ?? null, - max: $attribute['max'] ?? null, - createdAt: $attribute['$createdAt'] ?? '', - updatedAt: $attribute['$updatedAt'] ?? '', + case Column::TYPE_INTEGER: + $col = new Integer( + $column['key'], + $table, + required: $column['required'], + default: $column['default'], + array: $column['array'], + min: $column['min'] ?? null, + max: $column['max'] ?? null, + createdAt: $column['$createdAt'] ?? '', + updatedAt: $column['$updatedAt'] ?? '', ); break; - case Attribute::TYPE_FLOAT: - $attr = new Decimal( - $attribute['key'], - $collection, - required: $attribute['required'], - default: $attribute['default'], - array: $attribute['array'], - min: $attribute['min'] ?? null, - max: $attribute['max'] ?? null, - createdAt: $attribute['$createdAt'] ?? '', - updatedAt: $attribute['$updatedAt'] ?? '', + case Column::TYPE_FLOAT: + $col = new Decimal( + $column['key'], + $table, + required: $column['required'], + default: $column['default'], + array: $column['array'], + min: $column['min'] ?? null, + max: $column['max'] ?? null, + createdAt: $column['$createdAt'] ?? '', + updatedAt: $column['$updatedAt'] ?? '', ); break; - case Attribute::TYPE_RELATIONSHIP: - $attr = new Relationship( - $attribute['key'], - $collection, - relatedCollection: $attribute['relatedCollection'], - relationType: $attribute['relationType'], - twoWay: $attribute['twoWay'], - twoWayKey: $attribute['twoWayKey'], - onDelete: $attribute['onDelete'], - side: $attribute['side'], - createdAt: $attribute['$createdAt'] ?? '', - updatedAt: $attribute['$updatedAt'] ?? '', + case Column::TYPE_RELATIONSHIP: + $col = new Relationship( + $column['key'], + $table, + relatedTable: $column['relatedCollection'], + relationType: $column['relationType'], + twoWay: $column['twoWay'], + twoWayKey: $column['twoWayKey'], + onDelete: $column['onDelete'], + side: $column['side'], + createdAt: $column['$createdAt'] ?? '', + updatedAt: $column['$updatedAt'] ?? '', ); break; - case Attribute::TYPE_DATETIME: - $attr = new DateTime( - $attribute['key'], - $collection, - required: $attribute['required'], - default: $attribute['default'], - array: $attribute['array'], - createdAt: $attribute['$createdAt'] ?? '', - updatedAt: $attribute['$updatedAt'] ?? '', + case Column::TYPE_DATETIME: + $col = new DateTime( + $column['key'], + $table, + required: $column['required'], + default: $column['default'], + array: $column['array'], + createdAt: $column['$createdAt'] ?? '', + updatedAt: $column['$updatedAt'] ?? '', ); break; } - if (!isset($attr)) { + if (!isset($col)) { throw new Exception( - resourceName: Resource::TYPE_ATTRIBUTE, + resourceName: Resource::TYPE_COLUMN, resourceGroup: Transfer::GROUP_DATABASES, - resourceId: $attribute['$id'], - message: 'Unknown attribute type: ' . $attribute['type'] + resourceId: $column['$id'], + message: 'Unknown column type: ' . $column['type'] ); } - $attributes[] = $attr; + $columns[] = $col; } - if (empty($attributes)) { + if (empty($columns)) { break; } - $this->callback($attributes); + $this->callback($columns); - $lastAttribute = $attributes[count($attributes) - 1]; + $lastColumn = $columns[count($columns) - 1]; - if (count($attributes) < $batchSize) { + if (count($columns) < $batchSize) { break; } } @@ -896,11 +902,11 @@ private function exportAttributes(int $batchSize): void */ private function exportIndexes(int $batchSize): void { - $collections = $this->cache->get(Resource::TYPE_COLLECTION); + $tables = $this->cache->get(Resource::TYPE_TABLE); // Transfer Indexes - foreach ($collections as $collection) { - /** @var Collection $collection */ + foreach ($tables as $table) { + /** @var Table $table */ $lastIndex = null; while (true) { @@ -911,15 +917,15 @@ private function exportIndexes(int $batchSize): void $queries[] = $this->database->queryCursorAfter($lastIndex); } - $response = $this->database->listIndexes($collection, $queries); + $response = $this->database->listIndexes($table, $queries); foreach ($response as $index) { $indexes[] = new Index( 'unique()', $index['key'], - $collection, + $table, $index['type'], - $index['attributes'], + $index['columns'], [], $index['orders'], $index['$createdAt'] = empty($index['$createdAt']) ? UtopiaDateTime::now() : $index['$createdAt'], @@ -945,32 +951,32 @@ private function exportIndexes(int $batchSize): void /** * @throws Exception */ - private function exportDocuments(int $batchSize): void + private function exportRows(int $batchSize): void { - $collections = $this->cache->get(Collection::getName()); + $tables = $this->cache->get(Table::getName()); - foreach ($collections as $collection) { - /** @var Collection $collection */ - $lastDocument = null; + foreach ($tables as $table) { + /** @var Table $table */ + $lastRow = null; while (true) { $queries = [$this->database->queryLimit($batchSize)]; - $documents = []; + $rows = []; - if ($lastDocument) { - $queries[] = $this->database->queryCursorAfter($lastDocument); + if ($lastRow) { + $queries[] = $this->database->queryCursorAfter($lastRow); } $selects = ['*', '$id', '$permissions', '$updatedAt', '$createdAt']; // We want relations flat! $manyToMany = []; - $attributes = $this->cache->get(Attribute::getName()); + $attributes = $this->cache->get(Column::getName()); foreach ($attributes as $attribute) { /** @var Relationship $attribute */ if ( - $attribute->getCollection()->getId() === $collection->getId() && - $attribute->getType() === Attribute::TYPE_RELATIONSHIP && + $attribute->getTable()->getId() === $table->getId() && + $attribute->getType() === Column::TYPE_RELATIONSHIP && $attribute->getSide() === 'parent' && $attribute->getRelationType() == 'manyToMany' ) { @@ -984,13 +990,13 @@ private function exportDocuments(int $batchSize): void $manyToMany[] = $attribute->getKey(); } } - /** @var Attribute|Relationship $attribute */ + /** @var Column|Relationship $attribute */ $queries[] = $this->database->querySelect($selects); - $response = $this->database->listDocuments($collection, $queries); + $response = $this->database->listRows($table, $queries); - foreach ($response as $document) { + foreach ($response as $row) { // HACK: Handle many to many if (!empty($manyToMany)) { $stack = ['$id']; // Adding $id because we can't select only relations @@ -998,42 +1004,42 @@ private function exportDocuments(int $batchSize): void $stack[] = $relation . '.$id'; } - $doc = $this->database->getDocument( - $collection, - $document['$id'], + $rowItem = $this->database->getRow( + $table, + $row['$id'], [$this->database->querySelect($stack)] ); foreach ($manyToMany as $key) { - $document[$key] = []; - foreach ($doc[$key] as $relationDocument) { - $document[$key][] = $relationDocument['$id']; + $row[$key] = []; + foreach ($rowItem[$key] as $relatedRowItem) { + $row[$key][] = $relatedRowItem['$id']; } } } - $id = $document['$id']; - $permissions = $document['$permissions']; + $id = $row['$id']; + $permissions = $row['$permissions']; - unset($document['$id']); - unset($document['$permissions']); - unset($document['$collectionId']); - unset($document['$databaseId']); - unset($document['$sequence']); - unset($document['$collection']); + unset($row['$id']); + unset($row['$permissions']); + unset($row['$collectionId']); + unset($row['$databaseId']); + unset($row['$sequence']); + unset($row['$collection']); - $document = new Document( + $row = new Row( $id, - $collection, - $document, + $table, + $row, $permissions ); - $documents[] = $document; - $lastDocument = $document; + $rows[] = $row; + $lastRow = $row; } - $this->callback($documents); + $this->callback($rows); if (count($response) < $batchSize) { break; diff --git a/src/Migration/Sources/Appwrite/Reader.php b/src/Migration/Sources/Appwrite/Reader.php index e03a6981..b45f2bdc 100644 --- a/src/Migration/Sources/Appwrite/Reader.php +++ b/src/Migration/Sources/Appwrite/Reader.php @@ -3,8 +3,8 @@ namespace Utopia\Migration\Sources\Appwrite; use Utopia\Migration\Resource; -use Utopia\Migration\Resources\Database\Collection; use Utopia\Migration\Resources\Database\Database; +use Utopia\Migration\Resources\Database\Table; /** * @template QueryType @@ -18,7 +18,7 @@ interface Reader * @param array $report * @return mixed */ - public function report(array $resources, array &$report); + public function report(array $resources, array &$report): mixed; /** * List databases that match the given queries @@ -35,61 +35,61 @@ public function listDatabases(array $queries = []): array; * @param array $queries * @return array */ - public function listCollections(Database $resource, array $queries = []): array; + public function listTables(Database $resource, array $queries = []): array; /** * List attributes that match the given queries * - * @param Collection $resource + * @param Table $resource * @param array $queries * @return array */ - public function listAttributes(Collection $resource, array $queries = []): array; + public function listColumns(Table $resource, array $queries = []): array; /** * List indexes that match the given queries * - * @param Collection $resource + * @param Table $resource * @param array $queries * @return array */ - public function listIndexes(Collection $resource, array $queries = []): array; + public function listIndexes(Table $resource, array $queries = []): array; /** * List documents that match the given queries * - * @param Collection $resource + * @param Table $resource * @param array $queries * @return array */ - public function listDocuments(Collection $resource, array $queries = []): array; + public function listRows(Table $resource, array $queries = []): array; /** * Get a document by its ID in the given collection * - * @param Collection $resource - * @param string $documentId + * @param Table $resource + * @param string $rowId * @param array $queries * @return array */ - public function getDocument(Collection $resource, string $documentId, array $queries = []): array; + public function getRow(Table $resource, string $rowId, array $queries = []): array; /** * Return a query to select the given attributes * - * @param array $attributes + * @param array $columns * @return QueryType|string */ - public function querySelect(array $attributes): mixed; + public function querySelect(array $columns): mixed; /** * Return a query to filter the given attributes * - * @param string $attribute + * @param string $column * @param array $values * @return QueryType|string */ - public function queryEqual(string $attribute, array $values): mixed; + public function queryEqual(string $column, array $values): mixed; /** * Return a query to paginate after the given resource diff --git a/src/Migration/Sources/Appwrite/Reader/API.php b/src/Migration/Sources/Appwrite/Reader/API.php index 05d20f0c..1708d416 100644 --- a/src/Migration/Sources/Appwrite/Reader/API.php +++ b/src/Migration/Sources/Appwrite/Reader/API.php @@ -5,9 +5,10 @@ use Appwrite\AppwriteException; use Appwrite\Query; use Appwrite\Services\Databases; +/* use Appwrite\Services\Tables; */ use Utopia\Migration\Resource; -use Utopia\Migration\Resources\Database\Collection; use Utopia\Migration\Resources\Database\Database; +use Utopia\Migration\Resources\Database\Table; use Utopia\Migration\Sources\Appwrite\Reader; /** @@ -15,72 +16,89 @@ */ class API implements Reader { - public function __construct(private readonly Databases $database) - { + public function __construct( + private readonly Databases $database, + /* private readonly Tables $table, */ + ) { } /** * @throws AppwriteException */ - public function report(array $resources, array &$report): void + public function report(array $resources, array &$report): mixed { - if (\in_array(Resource::TYPE_DATABASE, $resources)) { - $report[Resource::TYPE_DATABASE] = $this->database->list()['total']; + $relevantResources = [ + Resource::TYPE_DATABASE, + Resource::TYPE_TABLE, + Resource::TYPE_ROW, + Resource::TYPE_COLUMN, + Resource::TYPE_INDEX + ]; + + if (!Resource::isSupported($relevantResources, $resources)) { + return null; } - if (\in_array(Resource::TYPE_COLLECTION, $resources)) { - $report[Resource::TYPE_COLLECTION] = 0; - $databases = $this->database->list()['databases']; - foreach ($databases as $database) { - $report[Resource::TYPE_COLLECTION] += $this->database->listCollections( - $database['$id'], - [Query::limit(1)] - )['total']; + foreach ($relevantResources as $resourceType) { + if (Resource::isSupported($resourceType, $resources)) { + $report[$resourceType] = 0; } } - if (\in_array(Resource::TYPE_DOCUMENT, $resources)) { - $report[Resource::TYPE_DOCUMENT] = 0; - $databases = $this->database->list()['databases']; - foreach ($databases as $database) { - $collections = $this->database->listCollections($database['$id'])['collections']; - foreach ($collections as $collection) { - $report[Resource::TYPE_DOCUMENT] += $this->database->listDocuments( - $database['$id'], - $collection['$id'], - [Query::limit(1)] - )['total']; - } - } + $databasesResponse = $this->database->list(); + $databases = $databasesResponse['databases']; + + if (in_array(Resource::TYPE_DATABASE, $resources)) { + $report[Resource::TYPE_DATABASE] = $databasesResponse['total']; } - if (\in_array(Resource::TYPE_ATTRIBUTE, $resources)) { - $report[Resource::TYPE_ATTRIBUTE] = 0; - $databases = $this->database->list()['databases']; - foreach ($databases as $database) { - $collections = $this->database->listCollections($database['$id'])['collections']; - foreach ($collections as $collection) { - $report[Resource::TYPE_ATTRIBUTE] += $this->database->listAttributes( - $database['$id'], - $collection['$id'] - )['total']; - } - } + if (count(array_intersect($resources, $relevantResources)) === 1 && + in_array(Resource::TYPE_DATABASE, $resources)) { + return null; } - if (\in_array(Resource::TYPE_INDEX, $resources)) { - $report[Resource::TYPE_INDEX] = 0; - $databases = $this->database->list()['databases']; - foreach ($databases as $database) { - $collections = $this->database->listCollections($database['$id'])['collections']; - foreach ($collections as $collection) { - $report[Resource::TYPE_INDEX] += $this->database->listIndexes( - $database['$id'], - $collection['$id'] - )['total']; + // Process each database + foreach ($databases as $database) { + $databaseId = $database['$id']; + + /* $tablesResponse = $this->tables->list(...); */ + $tablesResponse = $this->database->listCollections($databaseId); + $tables = $tablesResponse['collections']; + + if (Resource::isSupported(Resource::TYPE_TABLE, $resources)) { + $report[Resource::TYPE_TABLE] += $tablesResponse['total']; + } + + if (Resource::isSupported([Resource::TYPE_ROW, Resource::TYPE_COLUMN, Resource::TYPE_INDEX], $resources)) { + foreach ($tables as $table) { + $tableId = $table['$id']; + + if (Resource::isSupported(Resource::TYPE_ROW, $resources)) { + /* $rowsResponse = $this->tables->listRows(...) */ + $rowsResponse = $this->database->listDocuments( + $databaseId, + $tableId, + [Query::limit(1)] + ); + $report[Resource::TYPE_ROW] += $rowsResponse['total']; + } + + if (Resource::isSupported(Resource::TYPE_COLUMN, $resources)) { + /* $columnsResponse = $this->tables->listColumns(...); */ + $columnsResponse = $this->database->listAttributes($databaseId, $tableId); + $report[Resource::TYPE_COLUMN] += $columnsResponse['total']; + } + + if (in_array(Resource::TYPE_INDEX, $resources)) { + /* $indexesResponse = $this->tables->listIndexes(...); */ + $indexesResponse = $this->database->listIndexes($databaseId, $tableId); + $report[Resource::TYPE_INDEX] += $indexesResponse['total']; + } } } } + + return null; } /** @@ -94,8 +112,9 @@ public function listDatabases(array $queries = []): array /** * @throws AppwriteException */ - public function listCollections(Database $resource, array $queries = []): array + public function listTables(Database $resource, array $queries = []): array { + /* $this->tables->list(...)['tables'] */ return $this->database->listCollections( $resource->getId(), $queries @@ -103,13 +122,14 @@ public function listCollections(Database $resource, array $queries = []): array } /** - * @param Collection $resource + * @param Table $resource * @param array $queries * @return array * @throws AppwriteException */ - public function listAttributes(Collection $resource, array $queries = []): array + public function listColumns(Table $resource, array $queries = []): array { + /* $this->tables->listColumns(...)['columns'] */ return $this->database->listAttributes( $resource->getDatabase()->getId(), $resource->getId(), @@ -118,13 +138,14 @@ public function listAttributes(Collection $resource, array $queries = []): array } /** - * @param Collection $resource + * @param Table $resource * @param array $queries * @return array * @throws AppwriteException */ - public function listIndexes(Collection $resource, array $queries = []): array + public function listIndexes(Table $resource, array $queries = []): array { + /* $this->tables->listIndexes(...)['indexes'] */ return $this->database->listIndexes( $resource->getDatabase()->getId(), $resource->getId(), @@ -134,13 +155,14 @@ public function listIndexes(Collection $resource, array $queries = []): array /** - * @param Collection $resource + * @param Table $resource * @param array $queries * @return array * @throws AppwriteException */ - public function listDocuments(Collection $resource, array $queries = []): array + public function listRows(Table $resource, array $queries = []): array { + /* $this->tables->listRows(...)['rows'] */ return $this->database->listDocuments( $resource->getDatabase()->getId(), $resource->getId(), @@ -149,39 +171,40 @@ public function listDocuments(Collection $resource, array $queries = []): array } /** - * @param Collection $resource - * @param string $documentId + * @param Table $resource + * @param string $rowId * @param array $queries * @return array * @throws AppwriteException */ - public function getDocument(Collection $resource, string $documentId, array $queries = []): array + public function getRow(Table $resource, string $rowId, array $queries = []): array { + /* $this->tables->getRow(...) */ return $this->database->getDocument( $resource->getDatabase()->getId(), $resource->getId(), - $documentId, + $rowId, $queries ); } /** - * @param array $attributes + * @param array $columns * @return string */ - public function querySelect(array $attributes): string + public function querySelect(array $columns): string { - return Query::select($attributes); + return Query::select($columns); } /** - * @param string $attribute + * @param string $column * @param array $values * @return string */ - public function queryEqual(string $attribute, array $values): string + public function queryEqual(string $column, array $values): string { - return Query::equal($attribute, $values); + return Query::equal($column, $values); } /** diff --git a/src/Migration/Sources/Appwrite/Reader/Database.php b/src/Migration/Sources/Appwrite/Reader/Database.php index c7634b3c..c43e246c 100644 --- a/src/Migration/Sources/Appwrite/Reader/Database.php +++ b/src/Migration/Sources/Appwrite/Reader/Database.php @@ -8,11 +8,11 @@ use Utopia\Database\Query; use Utopia\Migration\Exception; use Utopia\Migration\Resource; -use Utopia\Migration\Resources\Database\Attribute as AttributeResource; -use Utopia\Migration\Resources\Database\Collection as CollectionResource; +use Utopia\Migration\Resources\Database\Column as ColumnResource; use Utopia\Migration\Resources\Database\Database as DatabaseResource; -use Utopia\Migration\Resources\Database\Document as DocumentResource; use Utopia\Migration\Resources\Database\Index as IndexResource; +use Utopia\Migration\Resources\Database\Row as RowResource; +use Utopia\Migration\Resources\Database\Table as TableResource; use Utopia\Migration\Sources\Appwrite\Reader; /** @@ -24,86 +24,88 @@ public function __construct(private readonly UtopiaDatabase $dbForProject) { } - public function report(array $resources, array &$report): void + public function report(array $resources, array &$report): mixed { - if (\in_array(Resource::TYPE_DATABASE, $resources)) { - $report[Resource::TYPE_DATABASE] = $this->countResources('databases'); + $relevantResources = [ + Resource::TYPE_DATABASE, + Resource::TYPE_TABLE, + Resource::TYPE_ROW, + Resource::TYPE_COLUMN, + Resource::TYPE_INDEX, + ]; + + if (!Resource::isSupported($relevantResources, $resources)) { + return null; } - if (\in_array(Resource::TYPE_COLLECTION, $resources)) { - $report[Resource::TYPE_COLLECTION] = 0; - $databases = $this->listDatabases(); - foreach ($databases as $database) { - $collectionId = "database_{$database->getSequence()}"; - - $report[Resource::TYPE_COLLECTION] += $this->countResources($collectionId); + foreach ($relevantResources as $resourceType) { + if (Resource::isSupported($resourceType, $resources)) { + $report[$resourceType] = 0; } } - if (\in_array(Resource::TYPE_DOCUMENT, $resources)) { - $report[Resource::TYPE_DOCUMENT] = 0; - $databases = $this->listDatabases(); - foreach ($databases as $database) { - $dbResource = new DatabaseResource( - $database->getId(), - $database->getAttribute('name'), - $database->getCreatedAt(), - $database->getUpdatedAt(), - ); + if (in_array(Resource::TYPE_DATABASE, $resources)) { + $report[Resource::TYPE_DATABASE] = $this->countResources('databases'); + } - $collections = $this->listCollections($dbResource); + if (count(array_intersect($resources, $relevantResources)) === 1 && + in_array(Resource::TYPE_DATABASE, $resources)) { + return null; + } - foreach ($collections as $collection) { - $collectionId = "database_{$database->getSequence()}_collection_{$collection->getSequence()}"; + $dbResources = []; + $databases = $this->listDatabases(); - $report[Resource::TYPE_DOCUMENT] += $this->countResources($collectionId); - } + // Process each database + foreach ($databases as $database) { + $databaseSequence = $database->getSequence(); + $tableId = "database_{$databaseSequence}"; + + if (Resource::isSupported(Resource::TYPE_TABLE, $resources)) { + $report[Resource::TYPE_TABLE] += $this->countResources($tableId); + } + + if (!Resource::isSupported([Resource::TYPE_ROW, Resource::TYPE_COLUMN, Resource::TYPE_INDEX], $resources)) { + continue; } - } - if (\in_array(Resource::TYPE_ATTRIBUTE, $resources)) { - $report[Resource::TYPE_ATTRIBUTE] = 0; - $databases = $this->listDatabases(); - foreach ($databases as $database) { - $dbResource = new DatabaseResource( + if (!isset($dbResources[$database->getId()])) { + $dbResources[$database->getId()] = new DatabaseResource( $database->getId(), $database->getAttribute('name'), $database->getCreatedAt(), $database->getUpdatedAt(), ); + } + + $dbResource = $dbResources[$database->getId()]; - $collections = $this->listCollections($dbResource); + $tables = $this->listTables($dbResource); - foreach ($collections as $collection) { - $report[Resource::TYPE_ATTRIBUTE] += $this->countResources('attributes', [ - Query::equal('databaseInternalId', [$database->getSequence()]), - Query::equal('collectionInternalId', [$collection->getSequence()]), - ]); + foreach ($tables as $table) { + $tableSequence = $table->getSequence(); + + if (Resource::isSupported(Resource::TYPE_ROW, $resources)) { + $rowTableId = "database_{$databaseSequence}_collection_{$tableSequence}"; + $report[Resource::TYPE_ROW] += $this->countResources($rowTableId); } - } - } - if (\in_array(Resource::TYPE_INDEX, $resources)) { - $report[Resource::TYPE_INDEX] = 0; - $databases = $this->listDatabases(); - foreach ($databases as $database) { - $dbResource = new DatabaseResource( - $database->getId(), - $database->getAttribute('name'), - $database->getCreatedAt(), - $database->getUpdatedAt(), - ); + $commonQueries = [ + Query::equal('databaseInternalId', [$databaseSequence]), + Query::equal('collectionInternalId', [$tableSequence]), + ]; - $collections = $this->listCollections($dbResource); + if (Resource::isSupported(Resource::TYPE_COLUMN, $resources)) { + $report[Resource::TYPE_COLUMN] += $this->countResources('attributes', $commonQueries); + } - foreach ($collections as $collection) { - $report[Resource::TYPE_INDEX] += $this->countResources('indexes', [ - Query::equal('databaseInternalId', [$database->getSequence()]), - Query::equal('collectionInternalId', [$collection->getSequence()]), - ]); + if (in_array(Resource::TYPE_INDEX, $resources)) { + $report[Resource::TYPE_INDEX] += $this->countResources('indexes', $commonQueries); } } } + + return null; } public function listDatabases(array $queries = []): array @@ -111,7 +113,7 @@ public function listDatabases(array $queries = []): array return $this->dbForProject->find('databases', $queries); } - public function listCollections(DatabaseResource $resource, array $queries = []): array + public function listTables(DatabaseResource $resource, array $queries = []): array { try { $database = $this->dbForProject->getDocument('databases', $resource->getId()); @@ -152,7 +154,7 @@ public function listCollections(DatabaseResource $resource, array $queries = []) } } - public function listAttributes(CollectionResource $resource, array $queries = []): array + public function listColumns(TableResource $resource, array $queries = []): array { $database = $this->dbForProject->getDocument( 'databases', @@ -168,25 +170,25 @@ public function listAttributes(CollectionResource $resource, array $queries = [] ); } - $collection = $this->dbForProject->getDocument( + $table = $this->dbForProject->getDocument( 'database_' . $database->getSequence(), $resource->getId(), ); - if ($collection->isEmpty()) { + if ($table->isEmpty()) { throw new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), - message: 'Collection not found', + message: 'Table not found', ); } $queries[] = Query::equal('databaseInternalId', [$database->getSequence()]); - $queries[] = Query::equal('collectionInternalId', [$collection->getSequence()]); + $queries[] = Query::equal('collectionInternalId', [$table->getSequence()]); try { - $attributes = $this->dbForProject->find('attributes', $queries); + $columns = $this->dbForProject->find('attributes', $queries); } catch (DatabaseException $e) { throw new Exception( resourceName: $resource->getName(), @@ -198,23 +200,23 @@ public function listAttributes(CollectionResource $resource, array $queries = [] ); } - foreach ($attributes as $attribute) { - if ($attribute['type'] !== UtopiaDatabase::VAR_RELATIONSHIP) { + foreach ($columns as $column) { + if ($column['type'] !== UtopiaDatabase::VAR_RELATIONSHIP) { continue; } - $options = $attribute['options']; + $options = $column['options']; foreach ($options as $key => $value) { - $attribute[$key] = $value; + $column[$key] = $value; } - unset($attribute['options']); + unset($column['options']); } - return $attributes; + return $columns; } - public function listIndexes(CollectionResource $resource, array $queries = []): array + public function listIndexes(TableResource $resource, array $queries = []): array { $database = $this->dbForProject->getDocument( 'databases', @@ -230,22 +232,22 @@ public function listIndexes(CollectionResource $resource, array $queries = []): ); } - $collection = $this->dbForProject->getDocument( + $table = $this->dbForProject->getDocument( 'database_' . $database->getSequence(), $resource->getId(), ); - if ($collection->isEmpty()) { + if ($table->isEmpty()) { throw new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), - message: 'Collection not found', + message: 'Table not found', ); } $queries[] = Query::equal('databaseInternalId', [$database->getSequence()]); - $queries[] = Query::equal('collectionInternalId', [$collection->getSequence()]); + $queries[] = Query::equal('collectionInternalId', [$table->getSequence()]); try { return $this->dbForProject->find('indexes', $queries); @@ -261,7 +263,7 @@ public function listIndexes(CollectionResource $resource, array $queries = []): } } - public function listDocuments(CollectionResource $resource, array $queries = []): array + public function listRows(TableResource $resource, array $queries = []): array { $database = $this->dbForProject->getDocument( 'databases', @@ -277,24 +279,24 @@ public function listDocuments(CollectionResource $resource, array $queries = []) ); } - $collection = $this->dbForProject->getDocument( + $table = $this->dbForProject->getDocument( 'database_' . $database->getSequence(), $resource->getId(), ); - if ($collection->isEmpty()) { + if ($table->isEmpty()) { throw new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), - message: 'Collection not found', + message: 'Table not found', ); } - $collectionId = "database_{$database->getSequence()}_collection_{$collection->getSequence()}"; + $tableId = "database_{$database->getSequence()}_collection_{$table->getSequence()}"; try { - $documents = $this->dbForProject->find($collectionId, $queries); + $rows = $this->dbForProject->find($tableId, $queries); } catch (DatabaseException $e) { throw new Exception( resourceName: $resource->getName(), @@ -306,12 +308,12 @@ public function listDocuments(CollectionResource $resource, array $queries = []) ); } - return \array_map(function ($document) { - return $document->getArrayCopy(); - }, $documents); + return \array_map(function ($row) { + return $row->getArrayCopy(); + }, $rows); } - public function getDocument(CollectionResource $resource, string $documentId, array $queries = []): array + public function getRow(TableResource $resource, string $rowId, array $queries = []): array { $database = $this->dbForProject->getDocument( 'databases', @@ -327,46 +329,46 @@ public function getDocument(CollectionResource $resource, string $documentId, ar ); } - $collection = $this->dbForProject->getDocument( + $table = $this->dbForProject->getDocument( 'database_' . $database->getSequence(), $resource->getId(), ); - if ($collection->isEmpty()) { + if ($table->isEmpty()) { throw new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), - message: 'Collection not found', + message: 'Table not found', ); } - $collectionId = "database_{$database->getSequence()}_collection_{$collection->getSequence()}"; + $tableId = "database_{$database->getSequence()}_collection_{$table->getSequence()}"; return $this->dbForProject->getDocument( - $collectionId, - $documentId, + $tableId, + $rowId, $queries )->getArrayCopy(); } /** - * @param array $attributes + * @param array $columns * @return Query */ - public function querySelect(array $attributes): Query + public function querySelect(array $columns): Query { - return Query::select($attributes); + return Query::select($columns); } /** - * @param string $attribute + * @param string $column * @param array $values * @return Query */ - public function queryEqual(string $attribute, array $values): Query + public function queryEqual(string $column, array $values): Query { - return Query::equal($attribute, $values); + return Query::equal($column, $values); } /** @@ -385,18 +387,18 @@ public function queryCursorAfter(mixed $resource): Query case DatabaseResource::class: $document = $this->dbForProject->getDocument('databases', $resource->getId()); break; - case CollectionResource::class: + case TableResource::class: $database = $this->dbForProject->getDocument('databases', $resource->getDatabase()->getId()); $document = $this->dbForProject->getDocument('database_' . $database->getSequence(), $resource->getId()); break; - case AttributeResource::class: + case ColumnResource::class: $document = $this->dbForProject->getDocument('attributes', $resource->getId()); break; case IndexResource::class: $document = $this->dbForProject->getDocument('indexes', $resource->getId()); break; - case DocumentResource::class: - $document = $this->getDocument($resource->getCollection(), $resource->getId()); + case RowResource::class: + $document = $this->getRow($resource->getTable(), $resource->getId()); $document = new UtopiaDocument($document); break; default: @@ -412,13 +414,13 @@ public function queryLimit(int $limit): Query } /** - * @param string $collection + * @param string $table * @param array $queries * @return int * @throws DatabaseException */ - private function countResources(string $collection, array $queries = []): int + private function countResources(string $table, array $queries = []): int { - return $this->dbForProject->count($collection, $queries); + return $this->dbForProject->count($table, $queries); } } diff --git a/src/Migration/Sources/CSV.php b/src/Migration/Sources/CSV.php index 459f9053..8bab5c04 100644 --- a/src/Migration/Sources/CSV.php +++ b/src/Migration/Sources/CSV.php @@ -5,10 +5,10 @@ use Utopia\Database\Database as UtopiaDatabase; use Utopia\Migration\Exception; use Utopia\Migration\Resource as UtopiaResource; -use Utopia\Migration\Resources\Database\Attribute; -use Utopia\Migration\Resources\Database\Collection; +use Utopia\Migration\Resources\Database\Column; use Utopia\Migration\Resources\Database\Database; -use Utopia\Migration\Resources\Database\Document; +use Utopia\Migration\Resources\Database\Row; +use Utopia\Migration\Resources\Database\Table; use Utopia\Migration\Resources\Storage\File; use Utopia\Migration\Source; use Utopia\Migration\Sources\Appwrite\Reader; @@ -52,7 +52,7 @@ public static function getName(): string public static function getSupportedResources(): array { return [ - UtopiaResource::TYPE_DOCUMENT, + UtopiaResource::TYPE_ROW, ]; } @@ -80,7 +80,7 @@ public function report(array $resources = []): array $file->seek(PHP_INT_MAX); $rowCount = max(0, $file->key()); - $report[UtopiaResource::TYPE_DOCUMENT] = $rowCount; + $report[UtopiaResource::TYPE_ROW] = $rowCount; return $report; } @@ -96,13 +96,13 @@ protected function exportGroupAuth(int $batchSize, array $resources): void protected function exportGroupDatabases(int $batchSize, array $resources): void { try { - if (\in_array(UtopiaResource::TYPE_DOCUMENT, $resources)) { - $this->exportDocuments($batchSize); + if (UtopiaResource::isSupported(UtopiaResource::TYPE_ROW, $resources)) { + $this->exportRows($batchSize); } } catch (\Throwable $e) { $this->addError( new Exception( - UtopiaResource::TYPE_DOCUMENT, + UtopiaResource::TYPE_ROW, Transfer::GROUP_DATABASES, message: $e->getMessage(), code: $e->getCode(), @@ -118,29 +118,29 @@ protected function exportGroupDatabases(int $batchSize, array $resources): void /** * @throws \Exception */ - private function exportDocuments(int $batchSize): void + private function exportRows(int $batchSize): void { - $attributes = []; - $lastAttribute = null; + $columns = []; + $lastColumn = null; - [$databaseId, $collectionId] = explode(':', $this->resourceId); + [$databaseId, $tableId] = explode(':', $this->resourceId); $database = new Database($databaseId, ''); - $collection = new Collection($database, '', $collectionId); + $table = new Table($database, '', $tableId); while (true) { $queries = [$this->database->queryLimit($batchSize)]; - if ($lastAttribute) { - $queries[] = $this->database->queryCursorAfter($lastAttribute); + if ($lastColumn) { + $queries[] = $this->database->queryCursorAfter($lastColumn); } - $fetched = $this->database->listAttributes($collection, $queries); + $fetched = $this->database->listColumns($table, $queries); if (empty($fetched)) { break; } - array_push($attributes, ...$fetched); - $lastAttribute = $fetched[count($fetched) - 1]; + array_push($columns, ...$fetched); + $lastColumn = $fetched[count($fetched) - 1]; if (count($fetched) < $batchSize) { break; @@ -148,54 +148,54 @@ private function exportDocuments(int $batchSize): void } $arrayKeys = []; - $attributeTypes = []; + $columnTypes = []; $manyToManyKeys = []; - foreach ($attributes as $attribute) { - $key = $attribute['key']; - $type = $attribute['type']; - $isArray = $attribute['array'] ?? false; - $relationSide = $attribute['side'] ?? ''; - $relationType = $attribute['relationType'] ?? ''; + foreach ($columns as $column) { + $key = $column['key']; + $type = $column['type']; + $isArray = $column['array'] ?? false; + $relationSide = $column['side'] ?? ''; + $relationType = $column['relationType'] ?? ''; if ( - $type === Attribute::TYPE_RELATIONSHIP && + $type === Column::TYPE_RELATIONSHIP && $relationSide === UtopiaDatabase::RELATION_SIDE_CHILD ) { continue; } - $attributeTypes[$key] = $type; + $columnTypes[$key] = $type; if ( - $type === Attribute::TYPE_RELATIONSHIP && + $type === Column::TYPE_RELATIONSHIP && $relationType === 'manyToMany' && $relationSide === 'parent' ) { $manyToManyKeys[] = $key; } - if ($isArray && $type !== Attribute::TYPE_RELATIONSHIP) { + if ($isArray && $type !== Column::TYPE_RELATIONSHIP) { $arrayKeys[] = $key; } } - $this->withCSVStream(function ($stream) use ($attributeTypes, $manyToManyKeys, $arrayKeys, $collection, $batchSize) { + $this->withCSVStream(function ($stream) use ($columnTypes, $manyToManyKeys, $arrayKeys, $table, $batchSize) { $headers = fgetcsv($stream); if (! is_array($headers) || count($headers) === 0) { return; } - $this->validateCSVHeaders($headers, $attributeTypes); + $this->validateCSVHeaders($headers, $columnTypes); $buffer = []; - while (($row = fgetcsv($stream)) !== false) { - if (count($row) !== count($headers)) { + while (($csvRowItem = fgetcsv($stream)) !== false) { + if (count($csvRowItem) !== count($headers)) { throw new \Exception('CSV row does not match the number of header columns.'); } - $data = array_combine($headers, $row); + $data = array_combine($headers, $csvRowItem); if ($data === false) { continue; } @@ -204,7 +204,7 @@ private function exportDocuments(int $batchSize): void foreach ($data as $key => $value) { $parsedValue = trim($value); - $type = $attributeTypes[$key] ?? null; + $type = $columnTypes[$key] ?? null; if (! isset($type)) { continue; @@ -233,9 +233,9 @@ private function exportDocuments(int $batchSize): void $parsedData[$key] = array_map(function ($item) use ($type) { return match ($type) { - Attribute::TYPE_INTEGER => is_numeric($item) ? (int) $item : null, - Attribute::TYPE_FLOAT => is_numeric($item) ? (float) $item : null, - Attribute::TYPE_BOOLEAN => filter_var($item, FILTER_VALIDATE_BOOLEAN), + Column::TYPE_INTEGER => is_numeric($item) ? (int) $item : null, + Column::TYPE_FLOAT => is_numeric($item) ? (float) $item : null, + Column::TYPE_BOOLEAN => filter_var($item, FILTER_VALIDATE_BOOLEAN), default => $item, }; }, $arrayValues); @@ -245,21 +245,21 @@ private function exportDocuments(int $batchSize): void if ($parsedValue !== '') { $parsedData[$key] = match ($type) { - Attribute::TYPE_INTEGER => is_numeric($parsedValue) ? (int) $parsedValue : null, - Attribute::TYPE_FLOAT => is_numeric($parsedValue) ? (float) $parsedValue : null, - Attribute::TYPE_BOOLEAN => filter_var($parsedValue, FILTER_VALIDATE_BOOLEAN), + Column::TYPE_INTEGER => is_numeric($parsedValue) ? (int) $parsedValue : null, + Column::TYPE_FLOAT => is_numeric($parsedValue) ? (float) $parsedValue : null, + Column::TYPE_BOOLEAN => filter_var($parsedValue, FILTER_VALIDATE_BOOLEAN), default => $parsedValue, }; } } - $documentId = $parsedData['$id'] ?? 'unique()'; + $rowId = $parsedData['$id'] ?? 'unique()'; // `$id`, `$permissions` in the doc can cause issues! unset($parsedData['$id'], $parsedData['$permissions']); - $document = new Document($documentId, $collection, $parsedData); - $buffer[] = $document; + $row = new Row($rowId, $table, $parsedData); + $buffer[] = $row; if (count($buffer) === $batchSize) { $this->callback($buffer); @@ -346,27 +346,27 @@ private function withCsvStream(callable $callback): void /** * @throws \Exception */ - private function validateCSVHeaders(array $headers, array $attributeTypes): void + private function validateCSVHeaders(array $headers, array $columnTypes): void { - $expectedAttributes = array_keys($attributeTypes); + $expectedColumns = array_keys($columnTypes); // Ignore keys like $id, $permissions, etc. $filteredHeaders = array_filter($headers, fn ($key) => ! str_starts_with($key, '$')); - $extraAttributes = array_diff($filteredHeaders, $expectedAttributes); - $missingAttributes = array_diff($expectedAttributes, $filteredHeaders); + $extraColumns = array_diff($filteredHeaders, $expectedColumns); + $missingColumns = array_diff($expectedColumns, $filteredHeaders); - if (! empty($missingAttributes) || ! empty($extraAttributes)) { + if (! empty($missingColumns) || ! empty($extraColumns)) { $messages = []; - if (! empty($missingAttributes)) { - $label = count($missingAttributes) === 1 ? 'Missing attribute' : 'Missing attributes'; - $messages[] = "{$label}: '".implode("', '", $missingAttributes)."'"; + if (! empty($missingColumns)) { + $label = count($missingColumns) === 1 ? 'Missing column' : 'Missing columns'; + $messages[] = "{$label}: '".implode("', '", $missingColumns)."'"; } - if (! empty($extraAttributes)) { - $label = count($extraAttributes) === 1 ? 'Unexpected attribute' : 'Unexpected attributes'; - $messages[] = "{$label}: '".implode("', '", $extraAttributes)."'"; + if (! empty($extraColumns)) { + $label = count($extraColumns) === 1 ? 'Unexpected column' : 'Unexpected columns'; + $messages[] = "{$label}: '".implode("', '", $extraColumns)."'"; } throw new \Exception('CSV header mismatch. '.implode(' | ', $messages)); diff --git a/src/Migration/Sources/Firebase.php b/src/Migration/Sources/Firebase.php index 376b337b..917cc0bc 100644 --- a/src/Migration/Sources/Firebase.php +++ b/src/Migration/Sources/Firebase.php @@ -6,15 +6,15 @@ use Utopia\Migration\Resource; use Utopia\Migration\Resources\Auth\Hash; use Utopia\Migration\Resources\Auth\User; -use Utopia\Migration\Resources\Database\Attribute; -use Utopia\Migration\Resources\Database\Attributes\Boolean; -use Utopia\Migration\Resources\Database\Attributes\DateTime; -use Utopia\Migration\Resources\Database\Attributes\Decimal; -use Utopia\Migration\Resources\Database\Attributes\Integer; -use Utopia\Migration\Resources\Database\Attributes\Text; -use Utopia\Migration\Resources\Database\Collection; +use Utopia\Migration\Resources\Database\Column; +use Utopia\Migration\Resources\Database\Columns\Boolean; +use Utopia\Migration\Resources\Database\Columns\DateTime; +use Utopia\Migration\Resources\Database\Columns\Decimal; +use Utopia\Migration\Resources\Database\Columns\Integer; +use Utopia\Migration\Resources\Database\Columns\Text; use Utopia\Migration\Resources\Database\Database; -use Utopia\Migration\Resources\Database\Document; +use Utopia\Migration\Resources\Database\Row; +use Utopia\Migration\Resources\Database\Table; use Utopia\Migration\Resources\Storage\Bucket; use Utopia\Migration\Resources\Storage\File; use Utopia\Migration\Source; @@ -126,9 +126,14 @@ public static function getSupportedResources(): array // Database Resource::TYPE_DATABASE, - Resource::TYPE_COLLECTION, - Resource::TYPE_ATTRIBUTE, + Resource::TYPE_TABLE, + Resource::TYPE_COLUMN, + Resource::TYPE_ROW, + + // legacy Resource::TYPE_DOCUMENT, + Resource::TYPE_ATTRIBUTE, + Resource::TYPE_COLLECTION, // Storage Resource::TYPE_BUCKET, @@ -295,13 +300,14 @@ protected function exportGroupDatabases(int $batchSize, array $resources): void } try { - if (\in_array(Resource::TYPE_COLLECTION, $resources)) { - $this->exportDB($batchSize, in_array(Resource::TYPE_DOCUMENT, $resources), $database); + if (Resource::isSupported(Resource::TYPE_TABLE, $resources)) { + $hasInResources = Resource::isSupported(Resource::TYPE_ROW, $resources); + $this->exportDB($batchSize, $hasInResources, $database); } } catch (\Throwable $e) { $this->addError( new Exception( - Resource::TYPE_COLLECTION, + Resource::TYPE_TABLE, Transfer::GROUP_DATABASES, message: $e->getMessage(), code: $e->getCode(), @@ -311,14 +317,14 @@ protected function exportGroupDatabases(int $batchSize, array $resources): void } } - private function exportDB(int $batchSize, bool $pushDocuments, Database $database): void + private function exportDB(int $batchSize, bool $pushRows, Database $database): void { $baseURL = "https://firestore.googleapis.com/v1/projects/{$this->projectID}/databases/(default)/documents"; + $allTables = []; $nextPageToken = null; - $allCollections = []; while (true) { - $collections = []; + $tables = []; try { $result = $this->call('POST', $baseURL.':listCollectionIds', [ @@ -333,7 +339,7 @@ private function exportDB(int $batchSize, bool $pushDocuments, Database $databas } } catch (\Exception $e) { if ($e->getCode() == 403) { - $errorMessage = new Collection($database, 'firestore', 'firestore'); + $errorMessage = new Table($database, 'firestore', 'firestore'); $errorMessage->setStatus(Resource::STATUS_ERROR); $errorMessage->setMessage($e->getMessage()); @@ -345,20 +351,20 @@ private function exportDB(int $batchSize, bool $pushDocuments, Database $databas } // Transfer Collections - foreach ($result['collectionIds'] as $collection) { - $collections[] = new Collection($database, $collection, $collection); + foreach ($result['collectionIds'] as $table) { + $tables[] = new Table($database, $table, $table); } - if (count($collections) !== 0) { - $allCollections = array_merge($allCollections, $collections); - $this->callback($collections); + if (count($tables) !== 0) { + $allTables = array_merge($allTables, $tables); + $this->callback($tables); } else { return; } - // Transfer Documents and Calculate Schema - foreach ($collections as $collection) { - $this->exportCollection($collection, $batchSize, $pushDocuments); + // Transfer Row and Calculate Schema + foreach ($tables as $table) { + $this->exportTable($table, $batchSize, $pushRows); } if (count($result['collectionIds']) < $batchSize) { @@ -372,12 +378,12 @@ private function exportDB(int $batchSize, bool $pushDocuments, Database $databas /** * @throws \Exception */ - private function convertAttribute(Collection $collection, string $key, array $field, bool $array = false): Attribute + private function convertColumn(Table $column, string $key, array $field, bool $array = false): Column { if (array_key_exists('booleanValue', $field)) { return new Boolean( $key, - $collection, + $column, required:false, default: null, array: $array, @@ -385,7 +391,7 @@ private function convertAttribute(Collection $collection, string $key, array $fi } elseif (array_key_exists('bytesValue', $field)) { return new Text( $key, - $collection, + $column, required: false, default: null, array: $array, @@ -394,7 +400,7 @@ private function convertAttribute(Collection $collection, string $key, array $fi } elseif (array_key_exists('doubleValue', $field)) { return new Decimal( $key, - $collection, + $column, required: false, default: null, array: $array, @@ -402,7 +408,7 @@ private function convertAttribute(Collection $collection, string $key, array $fi } elseif (array_key_exists('integerValue', $field)) { return new Integer( $key, - $collection, + $column, required: false, default: null, array: $array, @@ -410,7 +416,7 @@ private function convertAttribute(Collection $collection, string $key, array $fi } elseif (array_key_exists('mapValue', $field)) { return new Text( $key, - $collection, + $column, required: false, default: null, array: $array, @@ -419,7 +425,7 @@ private function convertAttribute(Collection $collection, string $key, array $fi } elseif (array_key_exists('nullValue', $field)) { return new Text( $key, - $collection, + $column, required: false, default: null, array: $array, @@ -428,7 +434,7 @@ private function convertAttribute(Collection $collection, string $key, array $fi } elseif (array_key_exists('referenceValue', $field)) { return new Text( $key, - $collection, + $column, required: false, default: null, array: $array, @@ -437,7 +443,7 @@ private function convertAttribute(Collection $collection, string $key, array $fi } elseif (array_key_exists('stringValue', $field)) { return new Text( $key, - $collection, + $column, required: false, default: null, array: $array, @@ -446,7 +452,7 @@ private function convertAttribute(Collection $collection, string $key, array $fi } elseif (array_key_exists('timestampValue', $field)) { return new DateTime( $key, - $collection, + $column, required: false, default: null, array: $array, @@ -454,28 +460,28 @@ private function convertAttribute(Collection $collection, string $key, array $fi } elseif (array_key_exists('geoPointValue', $field)) { return new Text( $key, - $collection, + $column, required: false, default: null, array: $array, size: 1000000, ); } elseif (array_key_exists('arrayValue', $field)) { - return $this->calculateArrayType($collection, $key, $field['arrayValue']); + return $this->calculateArrayType($column, $key, $field['arrayValue']); } else { throw new \Exception('Unknown field type'); } } - private function calculateArrayType(Collection $collection, string $key, array $data): Attribute + private function calculateArrayType(Table $table, string $key, array $data): Column { $isSameType = true; $previousType = null; foreach ($data['values'] as $field) { if (! $previousType) { - $previousType = $this->convertAttribute($collection, $key, $field, true); - } elseif ($previousType->getName() != ($this->convertAttribute($collection, $key, $field))->getName()) { + $previousType = $this->convertColumn($table, $key, $field, true); + } elseif ($previousType->getName() != ($this->convertColumn($table, $key, $field))->getName()) { $isSameType = false; break; } @@ -484,22 +490,21 @@ private function calculateArrayType(Collection $collection, string $key, array $ if ($isSameType) { return $previousType; } else { - return new Text($key, $collection, false, true, true, 1000000); + return new Text($key, $table, false, true, true, 1000000); } } - private function exportCollection(Collection $collection, int $batchSize, bool $transferDocuments): void + private function exportTable(Table $table, int $batchSize, bool $transferRows): void { - $resourceURL = 'https://firestore.googleapis.com/v1/projects/'.$this->projectID.'/databases/'.$collection->getDatabase()->getOriginalId().'/documents/'.$collection->getId(); + $resourceURL = 'https://firestore.googleapis.com/v1/projects/'.$this->projectID.'/databases/'.$table->getDatabase()->getOriginalId().'/documents/'.$table->getId(); $nextPageToken = null; - $documentSchema = []; - $createdSchema = []; + $rowSchema = []; // Transfer Documents and Calculate Schemas while (true) { - $documents = []; + $rows = []; $result = $this->call('GET', $resourceURL, [ 'Content-Type' => 'application/json', @@ -512,40 +517,40 @@ private function exportCollection(Collection $collection, int $batchSize, bool $ break; } - foreach ($result['documents'] as $document) { - if (! isset($document['fields'])) { + foreach ($result['documents'] as $row) { + if (! isset($row['fields'])) { continue; //TODO: Transfer Empty Documents } - foreach ($document['fields'] as $key => $field) { - if (! isset($documentSchema[$key])) { - $documentSchema[$key] = $this->convertAttribute($collection, $key, $field); + foreach ($row['fields'] as $key => $field) { + if (! isset($rowSchema[$key])) { + $rowSchema[$key] = $this->convertColumn($table, $key, $field); } } - $documents[] = $this->convertDocument($collection, $document, $documentSchema); + $rows[] = $this->convertRow($table, $row, $rowSchema); } - // Transfer Documents - if ($transferDocuments) { - $cachedAtrributes = $this->cache->get(Attribute::getName()); + // Transfer Rows + if ($transferRows) { + $cachedColumns = $this->cache->get(Column::getName()); - $attributesToCreate = $documentSchema; + $columnsToCreate = $rowSchema; - foreach ($documentSchema as $key => $attribute) { - foreach ($cachedAtrributes as $cachedAttribute) { - /** @var Attribute $cachedAttribute */ - if ($cachedAttribute->getKey() == $attribute->getKey() && $cachedAttribute->getCollection()->getId() == $attribute->getCollection()->getId()) { - unset($attributesToCreate[$key]); + foreach ($rowSchema as $key => $column) { + foreach ($cachedColumns as $cachedColumn) { + /** @var Column $cachedColumn */ + if ($cachedColumn->getKey() == $column->getKey() && $cachedColumn->getTable()->getId() == $column->getTable()->getId()) { + unset($columnsToCreate[$key]); } } } - if (count($attributesToCreate) > 0) { - $this->callback(array_values($attributesToCreate)); + if (count($columnsToCreate) > 0) { + $this->callback(array_values($columnsToCreate)); } - $this->callback($documents); + $this->callback($rows); } if (count($result['documents']) < $batchSize) { @@ -591,13 +596,13 @@ private function calculateValue(array $field) } } - private function convertDocument(Collection $collection, array $document, array $documentSchema): Document + private function convertRow(Table $table, array $row, array $rowSchema): Row { $data = []; - foreach ($document['fields'] as $key => $field) { + foreach ($row['fields'] as $key => $field) { $value = $this->calculateValue($field); - if ($documentSchema[$key]->getType() === Attribute::TYPE_STRING && is_array($value)) { + if ($rowSchema[$key]->getType() === Column::TYPE_STRING && is_array($value)) { $value = array_map(function ($item) { return strval($item); }, $value); @@ -606,13 +611,13 @@ private function convertDocument(Collection $collection, array $document, array $data[$key] = $value; } - $documentId = explode('/', $document['name']); - $documentId = end($documentId); + $rowId = explode('/', $row['name']); + $rowId = end($rowId); // Strip non-alphanumeric except underscore and hyphen - $documentId = preg_replace("/[^A-Za-z0-9\_\-]/", '', $documentId); - $documentId = strtolower($documentId); + $rowId = preg_replace("/[^A-Za-z0-9\_\-]/", '', $rowId); + $rowId = strtolower($rowId); - return new Document($documentId, $collection, $data, []); + return new Row($rowId, $table, $data, []); } protected function exportGroupStorage(int $batchSize, array $resources): void diff --git a/src/Migration/Sources/NHost.php b/src/Migration/Sources/NHost.php index b6e24ae0..aa29db3b 100644 --- a/src/Migration/Sources/NHost.php +++ b/src/Migration/Sources/NHost.php @@ -7,16 +7,16 @@ use Utopia\Migration\Resource; use Utopia\Migration\Resources\Auth\Hash; use Utopia\Migration\Resources\Auth\User; -use Utopia\Migration\Resources\Database\Attribute; -use Utopia\Migration\Resources\Database\Attributes\Boolean; -use Utopia\Migration\Resources\Database\Attributes\DateTime; -use Utopia\Migration\Resources\Database\Attributes\Decimal; -use Utopia\Migration\Resources\Database\Attributes\Integer; -use Utopia\Migration\Resources\Database\Attributes\Text; -use Utopia\Migration\Resources\Database\Collection; +use Utopia\Migration\Resources\Database\Column; +use Utopia\Migration\Resources\Database\Columns\Boolean; +use Utopia\Migration\Resources\Database\Columns\DateTime; +use Utopia\Migration\Resources\Database\Columns\Decimal; +use Utopia\Migration\Resources\Database\Columns\Integer; +use Utopia\Migration\Resources\Database\Columns\Text; use Utopia\Migration\Resources\Database\Database; -use Utopia\Migration\Resources\Database\Document; use Utopia\Migration\Resources\Database\Index; +use Utopia\Migration\Resources\Database\Row; +use Utopia\Migration\Resources\Database\Table; use Utopia\Migration\Resources\Storage\Bucket; use Utopia\Migration\Resources\Storage\File; use Utopia\Migration\Source; @@ -84,10 +84,15 @@ public static function getSupportedResources(): array // Database Resource::TYPE_DATABASE, - Resource::TYPE_COLLECTION, - Resource::TYPE_ATTRIBUTE, + Resource::TYPE_TABLE, + Resource::TYPE_COLUMN, Resource::TYPE_INDEX, + Resource::TYPE_ROW, + + // LEGACY Resource::TYPE_DOCUMENT, + Resource::TYPE_ATTRIBUTE, + Resource::TYPE_COLLECTION, // Storage Resource::TYPE_BUCKET, @@ -130,7 +135,7 @@ public function report(array $resources = []): array $report[Resource::TYPE_DATABASE] = 1; } - if (\in_array(Resource::TYPE_COLLECTION, $resources)) { + if (Resource::isSupported(Resource::TYPE_TABLE, $resources)) { $statement = $db->prepare('SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = \'public\''); $statement->execute(); @@ -138,10 +143,10 @@ public function report(array $resources = []): array throw new \Exception('Failed to access tables table. Error: '.$statement->errorInfo()[2]); } - $report[Resource::TYPE_COLLECTION] = $statement->fetchColumn(); + $report[Resource::TYPE_TABLE] = $statement->fetchColumn(); } - if (\in_array(Resource::TYPE_ATTRIBUTE, $resources)) { + if (Resource::isSupported(Resource::TYPE_COLUMN, $resources)) { $statement = $db->prepare('SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = \'public\''); $statement->execute(); @@ -149,7 +154,7 @@ public function report(array $resources = []): array throw new \Exception('Failed to access columns table. Error: '.$statement->errorInfo()[2]); } - $report[Resource::TYPE_ATTRIBUTE] = $statement->fetchColumn(); + $report[Resource::TYPE_COLUMN] = $statement->fetchColumn(); } if (\in_array(Resource::TYPE_INDEX, $resources)) { @@ -163,7 +168,7 @@ public function report(array $resources = []): array $report[Resource::TYPE_INDEX] = $statement->fetchColumn(); } - if (\in_array(Resource::TYPE_DOCUMENT, $resources)) { + if (Resource::isSupported(Resource::TYPE_ROW, $resources)) { $statement = $db->prepare('SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = \'public\''); $statement->execute(); @@ -171,7 +176,7 @@ public function report(array $resources = []): array throw new \Exception('Failed to access tables table. Error: '.$statement->errorInfo()[2]); } - $report[Resource::TYPE_DOCUMENT] = $statement->fetchColumn(); + $report[Resource::TYPE_ROW] = $statement->fetchColumn(); } // Storage @@ -295,13 +300,13 @@ protected function exportGroupDatabases(int $batchSize, array $resources): void } try { - if (\in_array(Resource::TYPE_COLLECTION, $resources)) { - $this->exportCollections($batchSize); + if (Resource::isSupported(Resource::TYPE_TABLE, $resources)) { + $this->exportTables($batchSize); } } catch (\Throwable $e) { $this->addError( new Exception( - Resource::TYPE_COLLECTION, + Resource::TYPE_TABLE, Transfer::GROUP_DATABASES, message: $e->getMessage(), code: $e->getCode(), @@ -311,13 +316,13 @@ protected function exportGroupDatabases(int $batchSize, array $resources): void } try { - if (\in_array(Resource::TYPE_ATTRIBUTE, $resources)) { - $this->exportAttributes($batchSize); + if (Resource::isSupported(Resource::TYPE_COLUMN, $resources)) { + $this->exportColumns($batchSize); } } catch (\Throwable $e) { $this->addError( new Exception( - Resource::TYPE_ATTRIBUTE, + Resource::TYPE_COLUMN, Transfer::GROUP_DATABASES, message: $e->getMessage(), code: $e->getCode(), @@ -327,13 +332,13 @@ protected function exportGroupDatabases(int $batchSize, array $resources): void } try { - if (\in_array(Resource::TYPE_DOCUMENT, $resources)) { - $this->exportDocuments($batchSize); + if (Resource::isSupported(Resource::TYPE_ROW, $resources)) { + $this->exportRows($batchSize); } } catch (\Throwable $e) { $this->addError( new Exception( - Resource::TYPE_DOCUMENT, + Resource::TYPE_ROW, Transfer::GROUP_DATABASES, message: $e->getMessage(), code: $e->getCode(), @@ -367,7 +372,7 @@ private function exportDatabases(int $batchSize): void $this->callback([$transferDatabase]); } - private function exportCollections(int $batchSize): void + private function exportTables(int $batchSize): void { $databases = $this->cache->get(Database::getName()); $db = $this->getDatabase(); @@ -388,54 +393,54 @@ private function exportCollections(int $batchSize): void $offset += $batchSize; - $transferCollections = []; + $transferTables = []; foreach ($tables as $table) { - $transferCollections[] = new Collection($database, $table['table_name'], $table['table_name']); + $transferTables[] = new Table($database, $table['table_name'], $table['table_name']); } - $this->callback($transferCollections); + $this->callback($transferTables); } } } - private function exportAttributes(int $batchSize): void + private function exportColumns(int $batchSize): void { - $collections = $this->cache->get(Collection::getName()); + $tables = $this->cache->get(Table::getName()); $db = $this->getDatabase(); - foreach ($collections as $collection) { - /** @var Collection $collection */ + foreach ($tables as $table) { + /** @var Table $table */ $statement = $db->prepare('SELECT * FROM information_schema."columns" where "table_name" = :tableName'); - $statement->bindValue(':tableName', $collection->getCollectionName(), \PDO::PARAM_STR); + $statement->bindValue(':tableName', $table->getTableName()); $statement->execute(); - $databaseCollection = $statement->fetchAll(\PDO::FETCH_ASSOC); + $databaseTable = $statement->fetchAll(\PDO::FETCH_ASSOC); - $attributes = []; + $columns = []; - foreach ($databaseCollection as $column) { - $attributes[] = $this->convertAttribute($column, $collection); + foreach ($databaseTable as $column) { + $columns[] = $this->convertColumn($column, $table); } - $this->callback($attributes); + $this->callback($columns); } } private function exportIndexes(int $batchSize): void { - $collections = $this->cache->get(Collection::getName()); + $tables = $this->cache->get(Table::getName()); $db = $this->getDatabase(); - foreach ($collections as $collection) { - /** @var Collection $collection */ + foreach ($tables as $table) { + /** @var Table $table */ $indexStatement = $db->prepare('SELECT indexname, indexdef FROM pg_indexes WHERE tablename = :tableName'); - $indexStatement->bindValue(':tableName', $collection->getCollectionName(), \PDO::PARAM_STR); + $indexStatement->bindValue(':tableName', $table->getTableName()); $indexStatement->execute(); $databaseIndexes = $indexStatement->fetchAll(\PDO::FETCH_ASSOC); $indexes = []; foreach ($databaseIndexes as $index) { - $result = $this->convertIndex($index, $collection); + $result = $this->convertIndex($index, $table); if ($result) { $indexes[] = $result; @@ -446,64 +451,64 @@ private function exportIndexes(int $batchSize): void } } - private function exportDocuments(int $batchSize): void + private function exportRows(int $batchSize): void { $databases = $this->cache->get(Database::getName()); - $collections = $this->cache->get(Collection::getName()); + $tables = $this->cache->get(Table::getName()); $db = $this->getDatabase(); foreach ($databases as $database) { /** @var Database $database */ - $collections = array_filter($collections, function (Collection $collection) use ($database) { - return $collection->getDatabase()->getId() === $database->getId(); + $tables = array_filter($tables, function (Table $table) use ($database) { + return $table->getDatabase()->getId() === $database->getId(); }); - foreach ($collections as $collection) { - /** @var Collection $collection */ - $total = $db->query('SELECT COUNT(*) FROM '.$collection->getDatabase()->getDatabaseName().'."'.$collection->getCollectionName().'"')->fetchColumn(); + foreach ($tables as $table) { + /** @var Table $table */ + $total = $db->query('SELECT COUNT(*) FROM '.$table->getDatabase()->getDatabaseName().'."'.$table->getTableName().'"')->fetchColumn(); $offset = 0; while ($offset < $total) { - $statement = $db->prepare('SELECT row_to_json(t) FROM (SELECT * FROM '.$collection->getDatabase()->getDatabaseName().'."'.$collection->getCollectionName().'" LIMIT :limit OFFSET :offset) t;'); + $statement = $db->prepare('SELECT row_to_json(t) FROM (SELECT * FROM '.$table->getDatabase()->getDatabaseName().'."'.$table->getTableName().'" LIMIT :limit OFFSET :offset) t;'); $statement->bindValue(':limit', $batchSize, \PDO::PARAM_INT); $statement->bindValue(':offset', $offset, \PDO::PARAM_INT); $statement->execute(); - $documents = $statement->fetchAll(\PDO::FETCH_ASSOC); + $rows = $statement->fetchAll(\PDO::FETCH_ASSOC); $offset += $batchSize; - $transferDocuments = []; + $transferRows = []; - $attributes = $this->cache->get(Attribute::getName()); - $collectionAttributes = array_filter($attributes, function (Attribute $attribute) use ($collection) { - return $attribute->getCollection()->getId() === $collection->getId(); + $columns = $this->cache->get(Column::getName()); + $tableColumns = array_filter($columns, function (Column $column) use ($table) { + return $column->getTable()->getId() === $table->getId(); }); - foreach ($documents as $document) { - $data = json_decode($document['row_to_json'], true); + foreach ($rows as $row) { + $data = json_decode($row['row_to_json'], true); $processedData = []; - foreach ($collectionAttributes as $attribute) { - /** @var Attribute $attribute */ - if (! $attribute->isArray() && \is_array($data[$attribute->getKey()])) { - $processedData[$attribute->getKey()] = json_encode($data[$attribute->getKey()]); + foreach ($tableColumns as $column) { + /** @var Column $column */ + if (! $column->isArray() && \is_array($data[$column->getKey()])) { + $processedData[$column->getKey()] = json_encode($data[$column->getKey()]); } else { - $processedData[$attribute->getKey()] = $data[$attribute->getKey()]; + $processedData[$column->getKey()] = $data[$column->getKey()]; } } - $transferDocuments[] = new Document('unique()', $collection, $processedData); + $transferRows[] = new Row('unique()', $table, $processedData); } - $this->callback($transferDocuments); + $this->callback($transferRows); } } } } - private function convertAttribute(array $column, Collection $collection): Attribute + private function convertColumn(array $column, Table $table): Column { $isArray = $column['data_type'] === 'ARRAY'; @@ -513,7 +518,7 @@ private function convertAttribute(array $column, Collection $collection): Attrib case 'bool': return new Boolean( $column['column_name'], - $collection, + $table, required: $column['is_nullable'] === 'NO', default: $column['column_default'], array: $isArray, @@ -522,20 +527,20 @@ private function convertAttribute(array $column, Collection $collection): Attrib case 'int2': if (! is_numeric($column['column_default']) && ! is_null($column['column_default'])) { $this->addWarning(new Warning( - Resource::TYPE_COLLECTION, + Resource::TYPE_TABLE, Transfer::GROUP_DATABASES, - 'Functional default values are not supported. Default value for attribute '.$column['column_name'].' will be set to null.', - $collection->getId() + 'Functional default values are not supported. Default value for column '.$column['column_name'].' will be set to null.', + $table->getId() )); - $collection->setStatus(Resource::STATUS_WARNING); + $table->setStatus(Resource::STATUS_WARNING); $column['column_default'] = null; } return new Integer( $column['column_name'], - $collection, + $table, required: $column['is_nullable'] === 'NO', default:$column['column_default'], array: $isArray, @@ -546,20 +551,20 @@ private function convertAttribute(array $column, Collection $collection): Attrib case 'int4': if (! is_numeric($column['column_default']) && ! is_null($column['column_default'])) { $this->addWarning(new Warning( - Resource::TYPE_COLLECTION, + Resource::TYPE_TABLE, Transfer::GROUP_DATABASES, - 'Functional default values are not supported. Default value for attribute '.$column['column_name'].' will be set to null.', - $collection->getId() + 'Functional default values are not supported. Default value for column '.$column['column_name'].' will be set to null.', + $table->getId() )); - $collection->setStatus(Resource::STATUS_WARNING); + $table->setStatus(Resource::STATUS_WARNING); $column['column_default'] = null; } return new Integer( $column['column_name'], - $collection, + $table, required: $column['is_nullable'] === 'NO', default: $column['column_default'], array: $isArray, @@ -571,19 +576,19 @@ private function convertAttribute(array $column, Collection $collection): Attrib case 'numeric': if (! is_numeric($column['column_default']) && ! is_null($column['column_default'])) { $this->addWarning(new Warning( - Resource::TYPE_COLLECTION, + Resource::TYPE_TABLE, Transfer::GROUP_DATABASES, - 'Functional default values are not supported. Default value for attribute '.$column['column_name'].' will be set to null.', - $collection->getId() + 'Functional default values are not supported. Default value for column '.$column['column_name'].' will be set to null.', + $table->getId() )); - $collection->setStatus(Resource::STATUS_WARNING); + $table->setStatus(Resource::STATUS_WARNING); $column['column_default'] = null; } return new Integer( $column['column_name'], - $collection, + $table, required: $column['is_nullable'] === 'NO', default: $column['column_default'], array: $isArray, @@ -596,20 +601,20 @@ private function convertAttribute(array $column, Collection $collection): Attrib case 'money': if (! is_numeric($column['column_default']) && ! is_null($column['column_default'])) { $this->addWarning(new Warning( - Resource::TYPE_COLLECTION, + Resource::TYPE_TABLE, Transfer::GROUP_DATABASES, - 'Functional default values are not supported. Default value for attribute '.$column['column_name'].' will be set to null.', - $collection->getId() + 'Functional default values are not supported. Default value for column '.$column['column_name'].' will be set to null.', + $table->getId() )); - $collection->setStatus(Resource::STATUS_WARNING); + $table->setStatus(Resource::STATUS_WARNING); $column['column_default'] = null; } return new Decimal( $column['column_name'], - $collection, + $table, required: $column['is_nullable'] === 'NO', default: $column['column_default'], array: $isArray, @@ -626,7 +631,7 @@ private function convertAttribute(array $column, Collection $collection): Attrib case 'interval': return new DateTime( $column['column_name'], - $collection, + $table, required: $column['is_nullable'] === 'NO', default: null, array: $isArray, @@ -635,7 +640,7 @@ private function convertAttribute(array $column, Collection $collection): Attrib // Strings and Objects return new Text( $column['column_name'], - $collection, + $table, required: $column['is_nullable'] === 'NO', default: $column['column_default'], array: $isArray, @@ -644,7 +649,7 @@ private function convertAttribute(array $column, Collection $collection): Attrib } } - private function convertIndex(array $index, Collection $collection): Index|false + private function convertIndex(array $index, Table $table): Index|false { $pattern = "/CREATE (?\w+)? INDEX (?\w+) ON (?\w+\.\w+) USING (?\w+) \((?\w+)\)/"; @@ -666,23 +671,23 @@ private function convertIndex(array $index, Collection $collection): Index|false $type = Index::TYPE_KEY; } - $attributes = []; + $columns = []; $order = []; $targets = explode(',', $matches['columns']); foreach ($targets as $target) { - if (\strpos($target, ' ') !== false) { + if (str_contains($target, ' ')) { $target = \explode(' ', $target); - $attributes[] = $target[0]; + $columns[] = $target[0]; $order[] = $target[1]; } else { - $attributes[] = $target; + $columns[] = $target; $order[] = 'ASC'; } } - return new Index($matches['name'], $matches['name'], $collection, $type, $attributes, $order); + return new Index($matches['name'], $matches['name'], $table, $type, $columns, $order); } else { return false; } @@ -769,7 +774,7 @@ private function exportFiles(int $batchSize): void $statement = $db->prepare('SELECT * FROM storage.files WHERE bucket_id=:bucketId order by created_at LIMIT :limit OFFSET :offset'); $statement->bindValue(':limit', $batchSize, \PDO::PARAM_INT); $statement->bindValue(':offset', $offset, \PDO::PARAM_INT); - $statement->bindValue(':bucketId', $bucket->getId(), \PDO::PARAM_STR); + $statement->bindValue(':bucketId', $bucket->getId()); $statement->execute(); $files = $statement->fetchAll(\PDO::FETCH_ASSOC); diff --git a/src/Migration/Sources/Supabase.php b/src/Migration/Sources/Supabase.php index fa57d8af..489f5211 100644 --- a/src/Migration/Sources/Supabase.php +++ b/src/Migration/Sources/Supabase.php @@ -264,7 +264,7 @@ public function report(array $resources = []): array $report[Resource::TYPE_DATABASE] = 1; } - if (\in_array(Resource::TYPE_COLLECTION, $resources)) { + if (Resource::isSupported(Resource::TYPE_TABLE, $resources)) { $statement = $this->pdo->prepare('SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = \'public\''); $statement->execute(); @@ -272,10 +272,10 @@ public function report(array $resources = []): array throw new \Exception('Failed to access tables table. Error: '.$statement->errorInfo()[2]); } - $report[Resource::TYPE_COLLECTION] = $statement->fetchColumn(); + $report[Resource::TYPE_TABLE] = $statement->fetchColumn(); } - if (\in_array(Resource::TYPE_ATTRIBUTE, $resources)) { + if (Resource::isSupported(Resource::TYPE_COLUMN, $resources)) { $statement = $this->pdo->prepare('SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = \'public\''); $statement->execute(); @@ -283,7 +283,7 @@ public function report(array $resources = []): array throw new \Exception('Failed to access columns table. Error: '.$statement->errorInfo()[2]); } - $report[Resource::TYPE_ATTRIBUTE] = $statement->fetchColumn(); + $report[Resource::TYPE_COLUMN] = $statement->fetchColumn(); } if (\in_array(Resource::TYPE_INDEX, $resources)) { @@ -297,7 +297,7 @@ public function report(array $resources = []): array $report[Resource::TYPE_INDEX] = $statement->fetchColumn(); } - if (\in_array(Resource::TYPE_DOCUMENT, $resources)) { + if (Resource::isSupported(Resource::TYPE_ROW, $resources)) { $statement = $this->pdo->prepare('SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = \'public\''); $statement->execute(); @@ -305,7 +305,7 @@ public function report(array $resources = []): array throw new \Exception('Failed to access tables table. Error: '.$statement->errorInfo()[2]); } - $report[Resource::TYPE_DOCUMENT] = $statement->fetchColumn(); + $report[Resource::TYPE_ROW] = $statement->fetchColumn(); } // Storage diff --git a/src/Migration/Transfer.php b/src/Migration/Transfer.php index aa660db9..67faa019 100644 --- a/src/Migration/Transfer.php +++ b/src/Migration/Transfer.php @@ -36,10 +36,15 @@ class Transfer public const GROUP_DATABASES_RESOURCES = [ Resource::TYPE_DATABASE, - Resource::TYPE_COLLECTION, + Resource::TYPE_TABLE, Resource::TYPE_INDEX, + Resource::TYPE_COLUMN, + Resource::TYPE_ROW, + + // legacy + Resource::TYPE_DOCUMENT, Resource::TYPE_ATTRIBUTE, - Resource::TYPE_DOCUMENT + Resource::TYPE_COLLECTION, ]; public const GROUP_SETTINGS_RESOURCES = []; @@ -54,10 +59,15 @@ class Transfer Resource::TYPE_ENVIRONMENT_VARIABLE, Resource::TYPE_DEPLOYMENT, Resource::TYPE_DATABASE, - Resource::TYPE_COLLECTION, + Resource::TYPE_TABLE, Resource::TYPE_INDEX, - Resource::TYPE_ATTRIBUTE, + Resource::TYPE_COLUMN, + Resource::TYPE_ROW, + + // legacy Resource::TYPE_DOCUMENT, + Resource::TYPE_ATTRIBUTE, + Resource::TYPE_COLLECTION, ]; public const ROOT_RESOURCES = [ @@ -132,9 +142,9 @@ public function getStatusCounters(): array foreach ($this->cache->getAll() as $resourceType => $resources) { foreach ($resources as $resource) { - if ($resourceType === Resource::TYPE_DOCUMENT && is_string($resource)) { - $documentStatus = $resource; - $status[$resourceType][$documentStatus]++; + if ($resourceType === Resource::TYPE_ROW && is_string($resource)) { + $rowStatus = $resource; + $status[$resourceType][$rowStatus]++; if ($status[$resourceType]['pending'] > 0) { $status[$resourceType]['pending']--; @@ -276,11 +286,11 @@ public function getReport(string $statusLevel = ''): array foreach ($cache as $type => $resources) { foreach ($resources as $id => $resource) { - if ($type === Resource::TYPE_DOCUMENT && is_string($resource)) { + if ($type === Resource::TYPE_ROW && is_string($resource)) { if ($statusLevel && $resource !== $statusLevel) { continue; } - // no message for document is stored + // no message for row is stored $report[] = [ 'resource' => $type, 'id' => $id, diff --git a/tests/Migration/E2E/Sources/NHostTest.php b/tests/Migration/E2E/Sources/NHostTest.php index b0d153b3..a2d59e05 100644 --- a/tests/Migration/E2E/Sources/NHostTest.php +++ b/tests/Migration/E2E/Sources/NHostTest.php @@ -6,8 +6,8 @@ use Utopia\Migration\Destination; use Utopia\Migration\Resource; use Utopia\Migration\Resources\Auth\User; -use Utopia\Migration\Resources\Database\Collection; use Utopia\Migration\Resources\Database\Database; +use Utopia\Migration\Resources\Database\Table; use Utopia\Migration\Resources\Storage\Bucket; use Utopia\Migration\Resources\Storage\File; use Utopia\Migration\Source; @@ -213,27 +213,27 @@ public function testValidateDatabaseTransfer($state) $this->assertEquals('public', $foundDatabase->getDatabaseName()); $this->assertEquals('public', $foundDatabase->getId()); - // Find known collection - $collections = $state['source']->cache->get(Resource::TYPE_COLLECTION); - $foundCollection = null; + // Find known table + $tables = $state['source']->cache->get(Resource::TYPE_TABLE); + $foundTable = null; - foreach ($collections as $collection) { - /** @var Collection $collection */ - if ($collection->getCollectionName() === 'TestTable') { - $foundCollection = $collection; + foreach ($tables as $table) { + /** @var Table $table */ + if ($table->getTableName() === 'TestTable') { + $foundTable = $table; break; } } - if (! $foundCollection) { - $this->fail('Collection "TestTable" not found'); + if (! $foundTable) { + $this->fail('Table "TestTable" not found'); } - $this->assertEquals('success', $foundCollection->getStatus()); - $this->assertEquals('TestTable', $foundCollection->getCollectionName()); - $this->assertEquals('TestTable', $foundCollection->getId()); - $this->assertEquals('public', $foundCollection->getDatabase()->getId()); + $this->assertEquals('success', $foundTable->getStatus()); + $this->assertEquals('TestTable', $foundTable->getTableName()); + $this->assertEquals('TestTable', $foundTable->getId()); + $this->assertEquals('public', $foundTable->getDatabase()->getId()); return $state; } @@ -241,27 +241,27 @@ public function testValidateDatabaseTransfer($state) #[Depends('testValidateDatabaseTransfer')] public function testDatabaseFunctionalDefaultsWarn($state): void { - // Find known collection - $collections = $state['source']->cache->get(Resource::TYPE_COLLECTION); - $foundCollection = null; - - foreach ($collections as $collection) { - /** @var Collection $collection */ - if ($collection->getCollectionName() === 'FunctionalDefaultTestTable') { - $foundCollection = $collection; + // Find known table + $tables = $state['source']->cache->get(Resource::TYPE_TABLE); + $foundTable = null; + + foreach ($tables as $table) { + /** @var Table $table */ + if ($table->getTableName() === 'FunctionalDefaultTestTable') { + $foundTable = $table; } break; } - if (! $foundCollection) { - $this->fail('Collection "FunctionalDefaultTestTable" not found'); + if (! $foundTable) { + $this->fail('Table "FunctionalDefaultTestTable" not found'); } - $this->assertEquals('warning', $foundCollection->getStatus()); - $this->assertEquals('FunctionalDefaultTestTable', $foundCollection->getCollectionName()); - $this->assertEquals('FunctionalDefaultTestTable', $foundCollection->getId()); - $this->assertEquals('public', $foundCollection->getDatabase()->getId()); + $this->assertEquals('warning', $foundTable->getStatus()); + $this->assertEquals('FunctionalDefaultTestTable', $foundTable->getTableName()); + $this->assertEquals('FunctionalDefaultTestTable', $foundTable->getId()); + $this->assertEquals('public', $foundTable->getDatabase()->getId()); } #[Depends('testValidateDatabaseTransfer')] diff --git a/tests/Migration/E2E/Sources/SupabaseTest.php b/tests/Migration/E2E/Sources/SupabaseTest.php index ca06d8b3..a4745799 100644 --- a/tests/Migration/E2E/Sources/SupabaseTest.php +++ b/tests/Migration/E2E/Sources/SupabaseTest.php @@ -6,9 +6,8 @@ use Utopia\Migration\Destination; use Utopia\Migration\Resource; use Utopia\Migration\Resources\Auth\User; -use Utopia\Migration\Resources\Database\Collection; use Utopia\Migration\Resources\Database\Database; -use Utopia\Migration\Resources\Database\Document; +use Utopia\Migration\Resources\Database\Table; use Utopia\Migration\Resources\Storage\Bucket; use Utopia\Migration\Resources\Storage\File; use Utopia\Migration\Source; @@ -195,39 +194,39 @@ public function testValidateDatabaseTransfer($state) $this->assertEquals('public', $foundDatabase->getDatabaseName()); $this->assertEquals('public', $foundDatabase->getId()); - // Find Known Collections - $collections = $state['source']->cache->get(Resource::TYPE_COLLECTION); - $this->assertGreaterThan(0, count($collections)); + // Find Known Tables + $tables = $state['source']->cache->get(Resource::TYPE_TABLE); + $this->assertGreaterThan(0, count($tables)); - $foundCollection = null; + $foundTable = null; - foreach ($collections as $collection) { - /** @var Collection $collection */ - if ($collection->getDatabase()->getDatabaseName() === 'public' && $collection->getCollectionName() === 'test') { - $foundCollection = $collection; + foreach ($tables as $table) { + /** @var Table $table */ + if ($table->getDatabase()->getDatabaseName() === 'public' && $table->getTableName() === 'test') { + $foundTable = $table; break; } } - if (! $foundCollection) { - $this->fail('Collection "test" not found'); + if (! $foundTable) { + $this->fail('Table "test" not found'); } - $this->assertEquals('success', $foundCollection->getStatus()); - $this->assertEquals('test', $foundCollection->getCollectionName()); - $this->assertEquals('public', $foundCollection->getDatabase()->getDatabaseName()); - $this->assertEquals('public', $foundCollection->getDatabase()->getId()); + $this->assertEquals('success', $foundTable->getStatus()); + $this->assertEquals('test', $foundTable->getTableName()); + $this->assertEquals('public', $foundTable->getDatabase()->getDatabaseName()); + $this->assertEquals('public', $foundTable->getDatabase()->getId()); // Find Known Documents - $documents = $state['source']->cache->get(Resource::TYPE_DOCUMENT); + $documents = $state['source']->cache->get(Resource::TYPE_ROW); // $this->assertGreaterThan(0, count($documents)); // // $foundDocument = null; // // foreach ($documents as $document) { // /** @var Document $document */ - // if ($document->getCollection()->getDatabase()->getDatabaseName() === 'public' && $document->getCollection()->getCollectionName() === 'test') { + // if ($document->getTable()->getDatabase()->getDatabaseName() === 'public' && $document->getTable()->getCollectionName() === 'test') { // $foundDocument = $document; // } // @@ -246,27 +245,27 @@ public function testValidateDatabaseTransfer($state) #[Depends('testValidateDatabaseTransfer')] public function testDatabaseFunctionalDefaultsWarn($state): void { - // Find known collection - $collections = $state['source']->cache->get(Resource::TYPE_COLLECTION); - $foundCollection = null; - - foreach ($collections as $collection) { - /** @var Collection $collection */ - if ($collection->getCollectionName() === 'FunctionalDefaultTestTable') { - $foundCollection = $collection; + // Find known table + $tables = $state['source']->cache->get(Resource::TYPE_TABLE); + $foundTable = null; + + foreach ($tables as $table) { + /** @var Table $table */ + if ($table->getTableName() === 'FunctionalDefaultTestTable') { + $foundTable = $table; } break; } - if (! $foundCollection) { - $this->fail('Collection "FunctionalDefaultTestTable" not found'); + if (! $foundTable) { + $this->fail('Table "FunctionalDefaultTestTable" not found'); } - $this->assertEquals('warning', $foundCollection->getStatus()); - $this->assertEquals('FunctionalDefaultTestTable', $foundCollection->getCollectionName()); - $this->assertEquals('FunctionalDefaultTestTable', $foundCollection->getId()); - $this->assertEquals('public', $foundCollection->getDatabase()->getId()); + $this->assertEquals('warning', $foundTable->getStatus()); + $this->assertEquals('FunctionalDefaultTestTable', $foundTable->getTableName()); + $this->assertEquals('FunctionalDefaultTestTable', $foundTable->getId()); + $this->assertEquals('public', $foundTable->getDatabase()->getId()); } #[Depends('testValidateDestinationErrors')] diff --git a/tests/Migration/Unit/Adapters/MockDestination.php b/tests/Migration/Unit/Adapters/MockDestination.php index b6970fac..d9d3bb54 100644 --- a/tests/Migration/Unit/Adapters/MockDestination.php +++ b/tests/Migration/Unit/Adapters/MockDestination.php @@ -34,11 +34,11 @@ public static function getName(): string public static function getSupportedResources(): array { return [ - Resource::TYPE_ATTRIBUTE, + Resource::TYPE_COLUMN, Resource::TYPE_BUCKET, - Resource::TYPE_COLLECTION, + Resource::TYPE_TABLE, Resource::TYPE_DATABASE, - Resource::TYPE_DOCUMENT, + Resource::TYPE_ROW, Resource::TYPE_FILE, Resource::TYPE_FUNCTION, Resource::TYPE_DEPLOYMENT, diff --git a/tests/Migration/Unit/Adapters/MockSource.php b/tests/Migration/Unit/Adapters/MockSource.php index e257a60c..ef599599 100644 --- a/tests/Migration/Unit/Adapters/MockSource.php +++ b/tests/Migration/Unit/Adapters/MockSource.php @@ -63,11 +63,11 @@ public static function getName(): string public static function getSupportedResources(): array { return [ - Resource::TYPE_ATTRIBUTE, + Resource::TYPE_COLUMN, Resource::TYPE_BUCKET, - Resource::TYPE_COLLECTION, + Resource::TYPE_TABLE, Resource::TYPE_DATABASE, - Resource::TYPE_DOCUMENT, + Resource::TYPE_ROW, Resource::TYPE_FILE, Resource::TYPE_FUNCTION, Resource::TYPE_DEPLOYMENT, @@ -77,6 +77,11 @@ public static function getSupportedResources(): array Resource::TYPE_ENVIRONMENT_VARIABLE, Resource::TYPE_TEAM, Resource::TYPE_MEMBERSHIP, + + // legacy + Resource::TYPE_DOCUMENT, + Resource::TYPE_ATTRIBUTE, + Resource::TYPE_COLLECTION, ]; } @@ -111,7 +116,7 @@ protected function exportGroupAuth(int $batchSize, array $resources): void protected function exportGroupDatabases(int $batchSize, array $resources): void { foreach (Transfer::GROUP_DATABASES_RESOURCES as $resource) { - if (!\in_array($resource, $resources)) { + if (!Resource::isSupported($resource, $resources)) { continue; }