diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php index 6407d36575..52de2c6022 100644 --- a/src/Appwrite/Migration/Migration.php +++ b/src/Appwrite/Migration/Migration.php @@ -41,18 +41,6 @@ abstract class Migration * @var array */ public static array $versions = [ - '0.13.0' => 'V12', - '0.13.1' => 'V12', - '0.13.2' => 'V12', - '0.13.3' => 'V12', - '0.13.4' => 'V12', - '0.14.0' => 'V13', - '0.14.1' => 'V13', - '0.14.2' => 'V13', - '0.15.0' => 'V14', - '0.15.1' => 'V14', - '0.15.2' => 'V14', - '0.15.3' => 'V14', '1.0.0-RC1' => 'V15' ]; diff --git a/src/Appwrite/Migration/Version/V12.php b/src/Appwrite/Migration/Version/V12.php deleted file mode 100644 index acc6552d65..0000000000 --- a/src/Appwrite/Migration/Version/V12.php +++ /dev/null @@ -1,618 +0,0 @@ -project->getAttribute('name') . ' (' . $this->project->getId() . ')'); - - $this->pdo = $register->get('db'); - - Console::info('Migrating Project Schemas'); - $this->migrateProjectSchema($this->project->getId()); - - /** - * Switch to migrated Console Project - */ - if ($this->project->getId() === 'console') { - $this->consoleDB->setNamespace('_console'); - $this->projectDB->setNamespace('_console'); - } - - Console::info('Migrating Permissions'); - $this->fixPermissions(); - Console::info('Migrating Collections'); - $this->migrateCustomCollections(); - $this->fixCollections(); - Console::info('Migrating Documents'); - $this->forEachDocument([$this, 'fixDocument']); - } - - /** - * Migrate Project Tables. - * - * @param string $projectId - * @return void - * @throws \Exception - * @throws \PDOException - */ - private function migrateProjectSchema(string $projectId): void - { - /** - * Remove empty generated Console Project. - */ - if ($this->consoleDB->getNamespace() === '_project_console' && $projectId === 'console') { - $all = ['_console_bucket_1', '_console_bucket_1_perms']; - foreach ($this->collections as $collection) { - $all[] = "_{$projectId}_{$collection['$id']}"; - $all[] = "_{$projectId}_{$collection['$id']}_perms"; - } - $this->pdo->prepare('DROP TABLE IF EXISTS ' . implode(', ', $all) . ';')->execute(); - } elseif ($this->projectDB->getNamespace() === '_console') { - return; - } - - /** - * Rename Database Tables. - */ - foreach ($this->collections as $collection) { - $id = $collection['$id']; - - /** - * Skip new tables that don't exists on old schema. - */ - if (in_array($id, ['buckets', 'deployments', 'builds'])) { - continue; - } - - $this->pdo->prepare("ALTER TABLE IF EXISTS `{$this->projectDB->getDefaultDatabase()}`.`_project_{$projectId}_{$id}` RENAME TO `_{$projectId}_{$id}`")->execute(); - $this->pdo->prepare("CREATE TABLE IF NOT EXISTS `{$this->projectDB->getDefaultDatabase()}`.`_{$projectId}_{$id}_perms` ( - `_id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `_type` VARCHAR(12) NOT NULL, - `_permission` VARCHAR(255) NOT NULL, - `_document` VARCHAR(255) NOT NULL, - PRIMARY KEY (`_id`), - UNIQUE INDEX `_index1` (`_type`,`_document`,`_permission`), - INDEX `_index2` (`_permission`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;")->execute(); - } - } - - /** - * Migrate all Collection Structure. - * - * @return void - */ - protected function fixCollections(): void - { - foreach ($this->collections as $collection) { - $id = $collection['$id']; - - /** - * Skip new tables that don't exists on old schema. - */ - if (in_array($id, ['buckets', 'deployments', 'builds'])) { - continue; - } - Console::log("- {$id}"); - switch ($id) { - case 'sessions': - try { - /** - * Rename providerToken to providerAccessToken - */ - $this->projectDB->renameAttribute($id, 'providerToken', 'providerAccessToken'); - } catch (\Throwable $th) { - Console::warning("'providerAccessToken' from {$id}: {$th->getMessage()}"); - } - try { - /** - * Create providerRefreshToken - */ - $this->projectDB->createAttribute(collection: $id, id: 'providerRefreshToken', type: Database::VAR_STRING, size: 16384, signed: true, required: false, filters: ['encrypt']); - } catch (\Throwable $th) { - Console::warning("'providerRefreshToken' from {$id}: {$th->getMessage()}"); - } - try { - /** - * Create providerAccessTokenExpiry - */ - $this->projectDB->createAttribute(collection: $id, id: 'providerAccessTokenExpiry', type: Database::VAR_INTEGER, size: 0, required: false); - } catch (\Throwable $th) { - Console::warning("'providerAccessTokenExpiry' from {$id}: {$th->getMessage()}"); - } - break; - - case 'memberships': - try { - /** - * Add search attribute and index to memberships. - */ - $this->projectDB->createAttribute(collection: $id, id: 'search', type: Database::VAR_STRING, size: 16384, required: false); - $this->projectDB->createIndex(collection: $id, id: '_key_search', type: Database::INDEX_FULLTEXT, attributes: ['search']); - } catch (\Throwable $th) { - Console::warning("'search' from {$id}: {$th->getMessage()}"); - } - break; - - case 'files': - /** - * Create bucket table if not exists. - */ - $this->createCollection('buckets'); - - if (!$this->projectDB->findOne('buckets', [Query::equal('$id', ['default'])])) { - $this->projectDB->createDocument('buckets', new Document([ - '$id' => ID::custom('default'), - '$collection' => ID::custom('buckets'), - 'dateCreated' => \time(), - 'dateUpdated' => \time(), - 'name' => 'Default', - 'permission' => 'file', - 'maximumFileSize' => (int) App::getEnv('_APP_STORAGE_LIMIT', 0), // 10MB - 'allowedFileExtensions' => [], - 'enabled' => true, - 'encryption' => true, - 'antivirus' => true, - '$read' => ['role:all'], - '$write' => ['role:all'], - 'search' => 'buckets Default', - ])); - $this->createCollection('files', 'bucket_1'); - - /** - * Migrate all files to default Bucket. - */ - $nextDocument = null; - do { - $queries = [Query::limit($this->limit)]; - if ($nextDocument !== null) { - $queries[] = Query::cursorAfter($nextDocument); - } - $documents = $this->projectDB->find('files', $queries); - $count = count($documents); - \Co\run(function (array $documents) { - foreach ($documents as $document) { - go(function (Document $document) { - /** - * Update File Path - */ - $path = "/storage/uploads/app-{$this->project->getId()}"; - $new = str_replace($path, "{$path}/default", $document->getAttribute('path')); - $document->setAttribute('path', $new); - - /** - * Populate search string from Migration to 0.12. - */ - if (empty($document->getAttribute('search'))) { - $document->setAttribute('search', $this->buildSearchAttribute(['$id', 'name'], $document)); - } - - /** - * Set new values. - */ - $document - ->setAttribute('bucketId', 'default') - ->setAttribute('chunksTotal', 1) - ->setAttribute('chunksUploaded', 1); - - $this->projectDB->createDocument('bucket_1', $document); - }, $document); - } - }, $documents); - - if ($count !== $this->limit) { - $nextDocument = null; - } else { - $nextDocument = end($documents); - $nextDocument->setAttribute('$collection', 'files'); - } - } while (!is_null($nextDocument)); - - /** - * Rename folder on volumes. - */ - $path = "/storage/uploads/app-{$this->project->getId()}"; - - if (is_dir("{$path}/")) { - mkdir("/storage/uploads/app-{$this->project->getId()}/default"); - - foreach (new \DirectoryIterator($path) as $fileinfo) { - if ($fileinfo->isDir() && !$fileinfo->isDot() && $fileinfo->getFilename() !== 'default') { - rename("{$path}/{$fileinfo->getFilename()}", "{$path}/default/{$fileinfo->getFilename()}"); - } - } - } - } - - break; - - case 'functions': - try { - /** - * Rename tag to deployment - */ - $this->projectDB->renameAttribute($id, 'tag', 'deployment'); - } catch (\Throwable $th) { - Console::warning("'deployment' from {$id}: {$th->getMessage()}"); - } - - /** - * Create deployments table if not exists. - */ - $this->createCollection('deployments'); - - /** - * Create builds table if not exists. - */ - $this->createCollection('builds'); - - break; - - case 'executions': - try { - /** - * Rename tag to deployment - */ - $this->projectDB->renameAttribute($id, 'tagId', 'deploymentId'); - } catch (\Throwable $th) { - Console::warning("'deploymentId' from {$id}: {$th->getMessage()}"); - } - - try { - /** - * Create statusCode - */ - $this->projectDB->createAttribute(collection: $id, id: 'statusCode', type: Database::VAR_INTEGER, size: 0, required: false); - } catch (\Throwable $th) { - Console::warning("'statusCode' from {$id}: {$th->getMessage()}"); - } - - break; - - case 'teams': - try { - /** - * Rename tag to deployment - */ - $this->projectDB->renameAttribute($id, 'sum', 'total'); - } catch (\Throwable $th) { - Console::warning("'total' from {$id}: {$th->getMessage()}"); - } - - break; - } - usleep(100000); - } - } - - /** - * Migrates permissions to dedicated table. - * - * @param \Utopia\Database\Document $document - * @param string $internalId - * @return void - * @throws \Exception - * @throws \PDOException - */ - protected function migratePermissionsToDedicatedTable(string $collection, Document $document): void - { - $sql = "SELECT _read, _write FROM `{$this->projectDB->getDefaultDatabase()}`.`{$this->projectDB->getNamespace()}_{$collection}` WHERE _uid = {$this->pdo->quote($document->getid())}"; - $stmt = $this->pdo->prepare($sql); - $stmt->execute(); - - $permissions = $stmt->fetch(); - - $read = json_decode($permissions['_read'] ?? null) ?? []; - $write = json_decode($permissions['_write'] ?? null) ?? []; - - $permissions = []; - foreach ($read as $permission) { - $permissions[] = "('read', '{$permission}', '{$document->getId()}')"; - } - - foreach ($write as $permission) { - $permissions[] = "('write', '{$permission}', '{$document->getId()}')"; - } - - if (!empty($permissions)) { - $queryPermissions = "INSERT IGNORE INTO `{$this->projectDB->getDefaultDatabase()}`.`{$this->projectDB->getNamespace()}_{$collection}_perms` (_type, _permission, _document) VALUES " . implode(', ', $permissions); - $stmtPermissions = $this->pdo->prepare($queryPermissions); - $stmtPermissions->execute(); - } - } - - /** - * Migrates all user's database collections. - * - * @return void - * @throws \Exception - */ - protected function migrateCustomCollections(): void - { - $nextCollection = null; - - do { - $queries = [Query::limit($this->limit)]; - if ($nextCollection !== null) { - $queries[] = Query::cursorAfter($nextCollection); - } - $documents = $this->projectDB->find('collections', $queries); - $count = count($documents); - - \Co\run(function (array $documents) { - foreach ($documents as $document) { - go(function (Document $collection) { - $id = $collection->getId(); - $projectId = $this->project->getId(); - $internalId = $collection->getInternalId(); - - if ($this->projectDB->exists(App::getEnv('_APP_DB_SCHEMA', 'appwrite'), "collection_{$internalId}")) { - return; - } - Console::log("- {$id} ({$collection->getAttribute('name')})"); - - /** - * Rename user's colletion table schema - */ - $this->pdo->prepare("ALTER TABLE IF EXISTS `{$this->projectDB->getDefaultDatabase()}`.`_project_{$projectId}_collection_{$id}` RENAME TO `_{$projectId}_collection_{$internalId}`")->execute(); - $this->pdo->prepare("CREATE TABLE IF NOT EXISTS `{$this->projectDB->getDefaultDatabase()}`.`_{$projectId}_collection_{$internalId}_perms` ( - `_id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `_type` VARCHAR(12) NOT NULL, - `_permission` VARCHAR(255) NOT NULL, - `_document` VARCHAR(255) NOT NULL, - PRIMARY KEY (`_id`), - UNIQUE INDEX `_index1` (`_type`,`_document`,`_permission`), - INDEX `_index2` (`_permission`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;")->execute(); - - /** - * Update metadata table. - */ - $this->pdo->prepare("UPDATE `{$this->projectDB->getDefaultDatabase()}`.`_{$projectId}__metadata` - SET - _uid = 'collection_{$internalId}', - name = 'collection_{$internalId}' - WHERE _uid = 'collection_{$id}'; - ")->execute(); - - - $nextDocument = null; - - do { - $queries = [Query::limit($this->limit)]; - if ($nextDocument !== null) { - $queries[] = Query::cursorAfter($nextDocument); - } - $documents = $this->projectDB->find('collection_' . $internalId, $queries); - $count = count($documents); - - foreach ($documents as $document) { - go(function (Document $document, string $internalId) { - $this->migratePermissionsToDedicatedTable("collection_{$internalId}", $document); - }, $document, $internalId); - } - - if ($count !== $this->limit) { - $nextDocument = null; - } else { - $nextDocument = end($documents); - } - } while (!is_null($nextDocument)); - - /** - * Remove _read and _write columns - */ - $this->pdo->prepare(" - ALTER TABLE `{$this->projectDB->getDefaultDatabase()}`.`{$this->projectDB->getNamespace()}_collection_{$internalId}` - DROP COLUMN _read, - DROP COLUMN _write - ")->execute(); - }, $document); - } - }, $documents); - - if ($count !== $this->limit) { - $nextCollection = null; - } else { - $nextCollection = end($documents); - } - } while (!is_null($nextCollection)); - } - - /** - * Migrate all Permission to new System with dedicated Table. - * - * @return void - * @throws \Exception - */ - protected function fixPermissions() - { - foreach ($this->collections as $collection) { - $id = $collection['$id']; - - /** - * Skip new tables that don't exists on old schema. - */ - if (in_array($id, ['buckets', 'deployments', 'builds'])) { - continue; - } - /** - * Check if permissions have already been migrated. - */ - try { - $stmtCheck = $this->pdo->prepare("SHOW COLUMNS from `{$this->projectDB->getDefaultDatabase()}`.`{$this->projectDB->getNamespace()}_{$id}` LIKE '_read'"); - $stmtCheck->execute(); - - if (empty($stmtCheck->fetchAll())) { - continue; - } - } catch (\Throwable $th) { - if ($th->getCode() === "42S02") { - continue; - } - throw $th; - } - - - Console::log("- {$collection['$id']}"); - $nextDocument = null; - - do { - $queries = [Query::limit($this->limit)]; - if ($nextDocument !== null) { - $queries[] = Query::cursorAfter($nextDocument); - } - $documents = $this->projectDB->find($id, $queries); - $count = count($documents); - - \Co\run(function (array $documents) { - foreach ($documents as $document) { - go(function (Document $document) { - $this->migratePermissionsToDedicatedTable($document->getCollection(), $document); - }, $document); - } - }, $documents); - - if ($count !== $this->limit) { - $nextDocument = null; - } else { - $nextDocument = end($documents); - } - } while (!is_null($nextDocument)); - - /** - * Remove _read and _write columns - */ - $this->pdo->prepare(" - ALTER TABLE `{$this->projectDB->getDefaultDatabase()}`.`{$this->projectDB->getNamespace()}_{$id}` - DROP COLUMN _read, - DROP COLUMN _write - ")->execute(); - } - - /** - * Timeout to give MariaDB some room to breath - */ - usleep(100000); - } - - /** - * Fix run on each document - * - * @param \Utopia\Database\Document $document - * @return \Utopia\Database\Document - */ - protected function fixDocument(Document $document) - { - switch ($document->getCollection()) { - case 'projects': - /** - * Bump Project version number. - */ - $document->setAttribute('version', '0.13.0'); - - /** - * Populate search string from Migration to 0.12. - */ - if (empty($document->getAttribute('search'))) { - $document->setAttribute('search', $this->buildSearchAttribute(['$id', 'name'], $document)); - } - - break; - - case 'users': - /** - * Populate search string from Migration to 0.12. - */ - if (empty($document->getAttribute('search'))) { - $document->setAttribute('search', $this->buildSearchAttribute(['$id', 'email', 'name'], $document)); - } - - break; - - case 'teams': - /** - * Populate search string from Migration to 0.12. - */ - if (empty($document->getAttribute('search'))) { - $document->setAttribute('search', $this->buildSearchAttribute(['$id', 'name'], $document)); - } - - break; - - case 'functions': - $document->setAttribute('deployment', null); - - /** - * Populate search string from Migration to 0.12. - */ - if (empty($document->getAttribute('search'))) { - $document->setAttribute('search', $this->buildSearchAttribute(['$id', 'name', 'runtime'], $document)); - } - - break; - - case 'executions': - /** - * Populate search string from Migration to 0.12. - */ - if (empty($document->getAttribute('search'))) { - $document->setAttribute('search', $this->buildSearchAttribute(['$id', 'functionId'], $document)); - } - - break; - - case 'memberships': - /** - * Populate search string. - */ - if (empty($document->getAttribute('search'))) { - $document->setAttribute('search', $this->buildSearchAttribute(['$id', 'userId'], $document)); - } - - break; - - case 'sessions': - $document - ->setAttribute('providerRefreshToken', '') - ->setAttribute('providerAccessTokenExpiry', 0) - ->setAttribute('providerAccessToken', $document->getAttribute('providerToken', '')) - ->removeAttribute('providerToken'); - - break; - } - - return $document; - } - - /** - * Builds a search string for a fulltext index. - * - * @param array $values - * @param Document $document - * @return string - */ - private function buildSearchAttribute(array $values, Document $document): string - { - $values = array_filter(array_map(fn (string $value) => $document->getAttribute($value) ?? '', $values)); - - return implode(' ', $values); - } -} diff --git a/src/Appwrite/Migration/Version/V13.php b/src/Appwrite/Migration/Version/V13.php deleted file mode 100644 index d204be84f3..0000000000 --- a/src/Appwrite/Migration/Version/V13.php +++ /dev/null @@ -1,337 +0,0 @@ -project->getAttribute('name') . ' (' . $this->project->getId() . ')'); - Console::info('Migrating Collections'); - $this->migrateCollections(); - Console::info('Migrating Documents'); - $this->forEachDocument([$this, 'fixDocument']); - } - - /** - * Migrate all Collections. - * - * @return void - */ - protected function migrateCollections(): void - { - foreach ($this->collections as $collection) { - $id = $collection['$id']; - - Console::log("- {$id}"); - switch ($id) { - case 'projects': - try { - /** - * Rename providers to authProviders. - */ - $this->projectDB->renameAttribute($id, 'providers', 'authProviders'); - } catch (\Throwable $th) { - Console::warning("'providers' from {$id}: {$th->getMessage()}"); - } - break; - case 'users': - try { - /** - * Recreate sessions for new subquery. - */ - $this->projectDB->deleteAttribute($id, 'sessions'); - $this->projectDB->createAttribute( - collection: $id, - id: 'sessions', - required: false, - type: Database::VAR_STRING, - format: '', - size: 16384, - filters: ['subQuerySessions'] - ); - } catch (\Throwable $th) { - Console::warning("'sessions' from {$id}: {$th->getMessage()}"); - } - try { - /** - * Recreate tokens for new subquery. - */ - $this->projectDB->deleteAttribute($id, 'tokens'); - $this->projectDB->createAttribute( - collection: $id, - id: 'tokens', - required: false, - type: Database::VAR_STRING, - format: '', - size: 16384, - filters: ['subQueryTokens'] - ); - } catch (\Throwable $th) { - Console::warning("'tokens' from {$id}: {$th->getMessage()}"); - } - try { - /** - * Recreate memberships for new subquery. - */ - $this->projectDB->deleteAttribute($id, 'memberships'); - $this->projectDB->createAttribute( - collection: $id, - id: 'memberships', - required: false, - type: Database::VAR_STRING, - format: '', - size: 16384, - filters: ['subQueryMemberships'] - ); - } catch (\Throwable $th) { - Console::warning("'memberships' from {$id}: {$th->getMessage()}"); - } - break; - case 'sessions': - try { - /** - * Add new index for users. - */ - $this->projectDB->createIndex(collection: $id, id: '_key_user', type: Database::INDEX_KEY, attributes: ['userId'], orders: [Database::ORDER_ASC]); - } catch (\Throwable $th) { - Console::warning("'_key_user' from {$id}: {$th->getMessage()}"); - } - break; - case 'builds': - try { - /** - * Increase stdout size. - */ - $this->projectDB->updateAttribute($id, 'stdout', size: 1_000_000); - } catch (\Throwable $th) { - Console::warning("'stdout' from {$id}: {$th->getMessage()}"); - } - try { - /** - * Increase stderr size. - */ - $this->projectDB->updateAttribute($id, 'stderr', size: 1_000_000); - } catch (\Throwable $th) { - Console::warning("'stderr' from {$id}: {$th->getMessage()}"); - } - break; - case 'executions': - try { - /** - * Rename stdout to response. - * Increase response size. - */ - $this->projectDB->renameAttribute($id, 'stdout', 'response'); - $this->projectDB->updateAttribute($id, 'response', size: 1_000_000); - } catch (\Throwable $th) { - Console::warning("'stdout' from {$id}: {$th->getMessage()}"); - } - try { - /** - * Increase stderr size. - */ - $this->projectDB->updateAttribute($id, 'stderr', size: 1_000_000); - } catch (\Throwable $th) { - Console::warning("'stderr' from {$id}: {$th->getMessage()}"); - } - break; - case 'stats': - try { - /** - * Increase value size ot BIGINT. - */ - $this->projectDB->updateAttribute($id, 'value', size: 8); - } catch (\Throwable $th) { - Console::warning("'size' from {$id}: {$th->getMessage()}"); - } - break; - case 'tokens': - try { - /** - * Create new Tokens collection. - */ - $this->createCollection('tokens'); - } catch (\Throwable $th) { - Console::warning("'tokens': {$th->getMessage()}"); - } - break; - } - usleep(100000); - } - } - - /** - * Fix run on each document - * - * @param \Utopia\Database\Document $document - * @return \Utopia\Database\Document - */ - protected function fixDocument(Document $document) - { - switch ($document->getCollection()) { - case 'projects': - /** - * Bump Project version number. - */ - $document->setAttribute('version', '0.14.0'); - - break; - - case 'functions': - /** - * Migrate events. - */ - if (!empty($document->getAttribute('events'))) { - $document->setAttribute('events', $this->migrateEvents($document->getAttribute('events'))); - } - - break; - - case 'webhooks': - /** - * Migrate events. - */ - if (!empty($document->getAttribute('events'))) { - $document->setAttribute('events', $this->migrateEvents($document->getAttribute('events'))); - } - - break; - - case 'users': - /** - * Remove deleted users. - */ - if ($document->getAttribute('deleted', false) === true) { - $this->projectDB->deleteDocument('users', $document->getId()); - } - break; - } - - return $document; - } - - public function migrateEvents(array $events): array - { - return array_filter(array_unique(array_map(function ($event) { - if (!in_array($event, $this->events)) { - return $event; - } - $parts = \explode('.', $event); - $first = array_shift($parts); - switch ($first) { - case 'account': - case 'users': - $first = 'users'; - - switch ($parts[0]) { - case 'recovery': - case 'sessions': - case 'verification': - $second = array_shift($parts); - return 'users.*.' . $second . '.*.' . implode('.', $parts); - - default: - return 'users.*.' . implode('.', $parts); - } - case 'functions': - switch ($parts[0]) { - case 'deployments': - case 'executions': - $second = array_shift($parts); - return 'functions.*.' . $second . '.*.' . implode('.', $parts); - - default: - return 'functions.*.' . implode('.', $parts); - } - case 'teams': - switch ($parts[0]) { - case 'memberships': - $second = array_shift($parts); - return 'teams.*.' . $second . '.*.' . implode('.', $parts); - - default: - return 'teams.*.' . implode('.', $parts); - } - case 'storage': - $second = array_shift($parts); - switch ($second) { - case 'buckets': - return 'buckets.*.' . implode('.', $parts); - case 'files': - return 'buckets.*.' . $second . '.*.' . implode('.', $parts); - } // intentional fallthrough - case 'database': - $second = array_shift($parts); - switch ($second) { - case 'collections': - return 'collections.*.' . implode('.', $parts); - case 'documents': - case 'indexes': - case 'attributes': - return 'collections.*.' . $second . '.*.' . implode('.', $parts); - } - } - return ''; - }, $events))); - } -} diff --git a/src/Appwrite/Migration/Version/V14.php b/src/Appwrite/Migration/Version/V14.php deleted file mode 100644 index b00e25ed4e..0000000000 --- a/src/Appwrite/Migration/Version/V14.php +++ /dev/null @@ -1,807 +0,0 @@ -pdo = $register->get('db'); - - if ($this->project->getId() === 'console' && $this->project->getInternalId() !== 'console') { - return; - } - - /** - * Disable SubQueries for Speed. - */ - foreach (['subQueryAttributes', 'subQueryIndexes', 'subQueryPlatforms', 'subQueryDomains', 'subQueryKeys', 'subQueryWebhooks', 'subQuerySessions', 'subQueryTokens', 'subQueryMemberships'] as $name) { - Database::addFilter($name, fn () => null, fn () => []); - } - - Console::log('Migrating project: ' . $this->project->getAttribute('name') . ' (' . $this->project->getId() . ')'); - Console::info('Migrating Collections'); - $this->migrateCollections(); - Console::info('Create Default Database Layer'); - $this->createDatabaseLayer(); - if ($this->project->getId() !== 'console') { - Console::info('Migrating Database Collections'); - $this->migrateCustomCollections(); - } - Console::info('Migrating Documents'); - $this->forEachDocument([$this, 'fixDocument']); - } - - /** - * Creates the default Database for existing Projects. - * - * @return void - * @throws \Throwable - */ - public function createDatabaseLayer(): void - { - try { - if (!$this->projectDB->exists('databases')) { - $this->createCollection('databases'); - } - } catch (\Throwable $th) { - Console::warning($th->getMessage()); - } - - if ($this->project->getInternalId() === 'console') { - return; - } - - try { - $this->projectDB->createDocument('databases', new Document([ - '$id' => ID::custom('default'), - 'name' => 'Default', - 'search' => 'default Default' - ])); - } catch (\Throwable $th) { - Console::warning($th->getMessage()); - } - } - - /** - * Migrates all Files. - * - * @param \Utopia\Database\Document $bucket - * @return void - * @throws \Exception - */ - protected function migrateBucketFiles(Document $bucket): void - { - $nextFile = null; - do { - $queries = [Query::limit($this->limit)]; - if ($nextFile !== null) { - $queries[] = Query::cursorAfter($nextFile); - } - $documents = $this->projectDB->find("bucket_{$bucket->getInternalId()}", $queries); - $count = count($documents); - - foreach ($documents as $document) { - go(function (Document $bucket, Document $document) { - Console::log("Migrating File {$document->getId()}"); - try { - /** - * Migrate $createdAt. - */ - if (empty($document->getCreatedAt())) { - $document->setAttribute('$createdAt', $document->getAttribute('dateCreated')); - $this->projectDB->updateDocument("bucket_{$bucket->getInternalId()}", $document->getId(), $document); - } - } catch (\Throwable $th) { - Console::warning($th->getMessage()); - } - }, $bucket, $document); - } - - if ($count !== $this->limit) { - $nextFile = null; - } else { - $nextFile = end($documents); - } - } while (!is_null($nextFile)); - } - - /** - * Migrates all Database Collections. - * @return void - * @throws \Exception - */ - protected function migrateCustomCollections(): void - { - try { - $this->pdo->prepare("ALTER TABLE IF EXISTS `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}_collections` RENAME TO `_{$this->project->getInternalId()}_database_1`")->execute(); - } catch (\Throwable $th) { - Console::warning($th->getMessage()); - } - try { - $this->pdo->prepare("ALTER TABLE IF EXISTS `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}_collections_perms` RENAME TO `_{$this->project->getInternalId()}_database_1_perms`")->execute(); - } catch (\Throwable $th) { - Console::warning($th->getMessage()); - } - - /** - * Update metadata table. - */ - try { - $this->pdo->prepare("UPDATE `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}__metadata` - SET - _uid = 'database_1', - name = 'database_1' - WHERE _uid = 'collections'; - ")->execute(); - } catch (\Throwable $th) { - Console::warning($th->getMessage()); - } - - try { - /** - * Add Database ID for Collections. - */ - $this->createAttributeFromCollection($this->projectDB, 'database_1', 'databaseId', 'collections'); - - /** - * Add Database Internal ID for Collections. - */ - $this->createAttributeFromCollection($this->projectDB, 'database_1', 'databaseInternalId', 'collections'); - } catch (\Throwable $th) { - Console::warning($th->getMessage()); - } - - $nextCollection = null; - - do { - $queries = [Query::limit($this->limit)]; - if ($nextCollection !== null) { - $queries[] = Query::cursorAfter($nextCollection); - } - $documents = $this->projectDB->find('database_1', $queries); - $count = count($documents); - - \Co\run(function (array $documents) { - foreach ($documents as $document) { - go(function (Document $collection) { - $id = $collection->getId(); - $internalId = $collection->getInternalId(); - - Console::log("- {$id} ({$collection->getAttribute('name')})"); - - try { - /** - * Rename user's colletion table schema - */ - $this->createNewMetaData("collection_{$internalId}", "database_1_collection_{$internalId}"); - } catch (\Throwable $th) { - Console::warning($th->getMessage()); - } - - try { - /** - * Update metadata table. - */ - $this->pdo->prepare("UPDATE `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}__metadata` - SET - _uid = 'database_1_collection_{$internalId}', - name = 'database_1_collection_{$internalId}' - WHERE _uid = 'collection_{$internalId}'; - ")->execute(); - } catch (\Throwable $th) { - Console::warning($th->getMessage()); - } - - try { - /** - * Update internal ID's. - */ - $collection - ->setAttribute('databaseId', 'default') - ->setAttribute('databaseInternalId', '1'); - $this->projectDB->updateDocument('database_1', $collection->getId(), $collection); - } catch (\Throwable $th) { - Console::warning($th->getMessage()); - } - /** - * Migrate Attributes - */ - $this->migrateAttributesAndCollections('attributes', $collection); - /** - * Migrate Indexes - */ - $this->migrateAttributesAndCollections('indexes', $collection); - }, $document); - } - }, $documents); - - if ($count !== $this->limit) { - $nextCollection = null; - } else { - $nextCollection = end($documents); - } - } while (!is_null($nextCollection)); - } - - protected function migrateAttributesAndCollections(string $type, Document $collection): void - { - /** - * Offset pagination instead of cursor, since documents are re-created! - */ - $offset = 0; - $attributesCount = $this->projectDB->count($type, queries: [Query::equal('collectionId', [$collection->getId()])]); - - do { - $queries = [ - Query::limit($this->limit), - Query::offset($offset), - Query::equal('collectionId', [$collection->getId()]), - ]; - $documents = $this->projectDB->find($type, $queries); - $offset += $this->limit; - - foreach ($documents as $document) { - go(function (Document $document, string $internalId, string $type) { - try { - /** - * Skip already migrated Documents. - */ - if (!is_null($document->getAttribute('databaseId'))) { - return; - } - /** - * Add Internal ID 'collectionInternalId' for Subqueries. - */ - $document->setAttribute('collectionInternalId', $internalId); - /** - * Add Internal ID 'databaseInternalId' for Subqueries. - */ - $document->setAttribute('databaseInternalId', '1'); - /** - * Add Internal ID 'databaseId'. - */ - $document->setAttribute('databaseId', 'default'); - - /** - * Re-create Attribute. - */ - $this->projectDB->deleteDocument($document->getCollection(), $document->getId()); - $this->projectDB->createDocument($document->getCollection(), $document->setAttribute('$id', "1_{$internalId}_{$document->getAttribute('key')}")); - } catch (\Throwable $th) { - Console::error("Failed to {$type} document: " . $th->getMessage()); - } - }, $document, $collection->getInternalId(), $type); - } - } while ($offset < $attributesCount); - } - - /** - * Migrate all Collections. - * - * @return void - */ - protected function migrateCollections(): void - { - foreach ($this->collections as $collection) { - $id = $collection['$id']; - - Console::log("- {$id}"); - - $this->createNewMetaData($id); - - $this->projectDB->setNamespace("_{$this->project->getInternalId()}"); - - switch ($id) { - case 'attributes': - case 'indexes': - try { - /** - * Create 'databaseInternalId' attribute - */ - $this->createAttributeFromCollection($this->projectDB, $id, 'databaseId'); - } catch (\Throwable $th) { - Console::warning("'databaseInternalId' from {$id}: {$th->getMessage()}"); - } - try { - /** - * Create 'databaseInternalId' attribute - */ - $this->createAttributeFromCollection($this->projectDB, $id, 'databaseInternalId'); - } catch (\Throwable $th) { - Console::warning("'databaseInternalId' from {$id}: {$th->getMessage()}"); - } - - try { - /** - * Create 'collectionInternalId' attribute - */ - $this->createAttributeFromCollection($this->projectDB, $id, 'collectionInternalId'); - } catch (\Throwable $th) { - Console::warning("'collectionInternalId' from {$id}: {$th->getMessage()}"); - } - - try { - /** - * Re-Create '_key_collection' index - */ - @$this->projectDB->deleteIndex($id, '_key_collection'); - $this->createIndexFromCollection($this->projectDB, $id, '_key_db_collection'); - } catch (\Throwable $th) { - Console::warning("'_key_collection' from {$id}: {$th->getMessage()}"); - } - - break; - case 'projects': - try { - /** - * Create 'teamInternalId' attribute - */ - $this->createAttributeFromCollection($this->projectDB, $id, 'teamInternalId'); - } catch (\Throwable $th) { - Console::warning("'teamInternalId' from {$id}: {$th->getMessage()}"); - } - - break; - case 'platforms': - case 'domains': - try { - /** - * Create 'projectInternalId' attribute - */ - $this->createAttributeFromCollection($this->projectDB, $id, 'projectInternalId'); - } catch (\Throwable $th) { - Console::warning("'projectInternalId' from {$id}: {$th->getMessage()}"); - } - try { - /** - * Re-Create '_key_project' index - */ - @$this->projectDB->deleteIndex($id, '_key_project'); - $this->createIndexFromCollection($this->projectDB, $id, '_key_project'); - } catch (\Throwable $th) { - Console::warning("'_key_project' from {$id}: {$th->getMessage()}"); - } - - break; - case 'keys': - try { - /** - * Create 'projectInternalId' attribute - */ - $this->createAttributeFromCollection($this->projectDB, $id, 'projectInternalId'); - } catch (\Throwable $th) { - Console::warning("'projectInternalId' from {$id}: {$th->getMessage()}"); - } - try { - /** - * Create 'expire' attribute - */ - $this->createAttributeFromCollection($this->projectDB, $id, 'expire'); - } catch (\Throwable $th) { - Console::warning("'expire' from {$id}: {$th->getMessage()}"); - } - try { - /** - * Re-Create '_key_project' index - */ - @$this->projectDB->deleteIndex($id, '_key_project'); - $this->createIndexFromCollection($this->projectDB, $id, '_key_project'); - } catch (\Throwable $th) { - Console::warning("'_key_project' from {$id}: {$th->getMessage()}"); - } - - break; - case 'webhooks': - try { - /** - * Create 'signatureKey' attribute - */ - $this->createAttributeFromCollection($this->projectDB, $id, 'signatureKey'); - } catch (\Throwable $th) { - Console::warning("'signatureKey' from {$id}: {$th->getMessage()}"); - } - try { - /** - * Create 'projectInternalId' attribute - */ - $this->createAttributeFromCollection($this->projectDB, $id, 'projectInternalId'); - } catch (\Throwable $th) { - Console::warning("'projectInternalId' from {$id}: {$th->getMessage()}"); - } - try { - /** - * Re-Create '_key_project' index - */ - @$this->projectDB->deleteIndex($id, '_key_project'); - $this->createIndexFromCollection($this->projectDB, $id, '_key_project'); - } catch (\Throwable $th) { - Console::warning("'_key_project' from {$id}: {$th->getMessage()}"); - } - - break; - case 'users': - try { - /** - * Create 'phone' attribute - */ - $this->createAttributeFromCollection($this->projectDB, $id, 'phone'); - } catch (\Throwable $th) { - Console::warning("'phone' from {$id}: {$th->getMessage()}"); - } - try { - /** - * Create 'phoneVerification' attribute - */ - $this->createAttributeFromCollection($this->projectDB, $id, 'phoneVerification'); - } catch (\Throwable $th) { - Console::warning("'phoneVerification' from {$id}: {$th->getMessage()}"); - } - try { - /** - * Create '_key_phone' index - */ - $this->createIndexFromCollection($this->projectDB, $id, '_key_phone'); - } catch (\Throwable $th) { - Console::warning("'_key_phone' from {$id}: {$th->getMessage()}"); - } - - break; - case 'tokens': - case 'sessions': - try { - /** - * Create 'userInternalId' attribute - */ - $this->createAttributeFromCollection($this->projectDB, $id, 'userInternalId'); - } catch (\Throwable $th) { - Console::warning("'userInternalId' from {$id}: {$th->getMessage()}"); - } - try { - /** - * Re-Create '_key_user' index - */ - @$this->projectDB->deleteIndex($id, '_key_user'); - $this->createIndexFromCollection($this->projectDB, $id, '_key_user'); - } catch (\Throwable $th) { - Console::warning("'_key_user' from {$id}: {$th->getMessage()}"); - } - - break; - case 'memberships': - try { - /** - * Create 'teamInternalId' attribute - */ - $this->createAttributeFromCollection($this->projectDB, $id, 'teamInternalId'); - } catch (\Throwable $th) { - Console::warning("'teamInternalId' from {$id}: {$th->getMessage()}"); - } - try { - /** - * Create 'userInternalId' attribute - */ - $this->createAttributeFromCollection($this->projectDB, $id, 'userInternalId'); - } catch (\Throwable $th) { - Console::warning("'userInternalId' from {$id}: {$th->getMessage()}"); - } - try { - /** - * Re-Create '_key_unique' index - */ - @$this->projectDB->deleteIndex($id, '_key_unique'); - $this->createIndexFromCollection($this->projectDB, $id, '_key_unique'); - } catch (\Throwable $th) { - Console::warning("'_key_unique' from {$id}: {$th->getMessage()}"); - } - try { - /** - * Re-Create '_key_team' index - */ - @$this->projectDB->deleteIndex($id, '_key_team'); - $this->createIndexFromCollection($this->projectDB, $id, '_key_team'); - } catch (\Throwable $th) { - Console::warning("'_key_team' from {$id}: {$th->getMessage()}"); - } - try { - /** - * Re-Create '_key_user' index - */ - @$this->projectDB->deleteIndex($id, '_key_user'); - $this->createIndexFromCollection($this->projectDB, $id, '_key_user'); - } catch (\Throwable $th) { - Console::warning("'_key_user' from {$id}: {$th->getMessage()}"); - } - break; - } - usleep(50000); - } - } - - /** - * Fix run on each document - * - * @param \Utopia\Database\Document $document - * @return \Utopia\Database\Document - */ - protected function fixDocument(Document $document) - { - switch ($document->getCollection()) { - case 'projects': - /** - * Bump Project version number. - */ - $document->setAttribute('version', '0.15.0'); - - if (!empty($document->getAttribute('teamId')) && is_null($document->getAttribute('teamInternalId'))) { - $internalId = $this->projectDB->getDocument('teams', $document->getAttribute('teamId'))->getInternalId(); - $document->setAttribute('teamInternalId', $internalId); - } - - break; - case 'keys': - /** - * Add new 'expire' attribute and default to never (0). - */ - if (is_null($document->getAttribute('expire'))) { - $document->setAttribute('expire', 0); - } - /** - * Add Internal ID 'projectId' for Subqueries. - */ - if (!empty($document->getAttribute('projectId')) && is_null($document->getAttribute('projectInternalId'))) { - $internalId = $this->projectDB->getDocument('projects', $document->getAttribute('projectId'))->getInternalId(); - $document->setAttribute('projectInternalId', $internalId); - } - - break; - case 'audit': - /** - * Add Database Layer to collection resource. - */ - if (str_starts_with($document->getAttribute('resource'), 'collection/')) { - $document - ->setAttribute('resource', "database/default/{$document->getAttribute('resource')}") - ->setAttribute('event', "databases.default.{$document->getAttribute('event')}"); - } - - if (str_starts_with($document->getAttribute('resource'), 'document/')) { - $collectionId = explode('.', $document->getAttribute('event'))[1]; - $document - ->setAttribute('resource', "database/default/collection/{$collectionId}/{$document->getAttribute('resource')}") - ->setAttribute('event', "databases.default.{$document->getAttribute('event')}"); - } - - break; - case 'stats': - /** - * Add Database Layer to stats metric. - */ - if (str_starts_with($document->getAttribute('metric'), 'database.')) { - $metric = ltrim($document->getAttribute('metric'), 'database.'); - $document->setAttribute('metric', "databases.default.{$metric}"); - } - - break; - case 'webhooks': - /** - * Add new 'signatureKey' attribute and generate a random value. - */ - if (empty($document->getAttribute('signatureKey'))) { - $document->setAttribute('signatureKey', \bin2hex(\random_bytes(64))); - } - /** - * Add Internal ID 'projectId' for Subqueries. - */ - if (!empty($document->getAttribute('projectId')) && is_null($document->getAttribute('projectInternalId'))) { - $internalId = $this->projectDB->getDocument('projects', $document->getAttribute('projectId'))->getInternalId(); - $document->setAttribute('projectInternalId', $internalId); - } - - break; - case 'domains': - /** - * Add Internal ID 'projectId' for Subqueries. - */ - if (!empty($document->getAttribute('projectId')) && is_null($document->getAttribute('projectInternalId'))) { - $internalId = $this->projectDB->getDocument('projects', $document->getAttribute('projectId'))->getInternalId(); - $document->setAttribute('projectInternalId', $internalId); - } - - break; - case 'tokens': - case 'sessions': - /** - * Add Internal ID 'userId' for Subqueries. - */ - if (!empty($document->getAttribute('userId')) && is_null($document->getAttribute('userInternalId'))) { - $internalId = $this->projectDB->getDocument('users', $document->getAttribute('userId'))->getInternalId(); - $document->setAttribute('userInternalId', $internalId); - } - - break; - case 'memberships': - /** - * Add Internal ID 'userId' for Subqueries. - */ - if (!empty($document->getAttribute('userId')) && is_null($document->getAttribute('userInternalId'))) { - $internalId = $this->projectDB->getDocument('users', $document->getAttribute('userId'))->getInternalId(); - $document->setAttribute('userInternalId', $internalId); - } - /** - * Add Internal ID 'teamId' for Subqueries. - */ - if (!empty($document->getAttribute('teamId')) && is_null($document->getAttribute('teamInternalId'))) { - $internalId = $this->projectDB->getDocument('teams', $document->getAttribute('teamId'))->getInternalId(); - $document->setAttribute('teamInternalId', $internalId); - } - - break; - case 'platforms': - /** - * Migrate dateCreated to $createdAt. - */ - if (empty($document->getCreatedAt())) { - $document->setAttribute('$createdAt', $document->getAttribute('dateCreated')); - } - /** - * Migrate dateUpdated to $updatedAt. - */ - if (empty($document->getUpdatedAt())) { - $document->setAttribute('$updatedAt', $document->getAttribute('dateUpdated')); - } - /** - * Add Internal ID 'projectId' for Subqueries. - */ - if (!empty($document->getAttribute('projectId')) && is_null($document->getAttribute('projectInternalId'))) { - $internalId = $this->projectDB->getDocument('projects', $document->getAttribute('projectId'))->getInternalId(); - $document->setAttribute('projectInternalId', $internalId); - } - - break; - case 'buckets': - /** - * Migrate dateCreated to $createdAt. - */ - if (empty($document->getCreatedAt())) { - $document->setAttribute('$createdAt', $document->getAttribute('dateCreated')); - } - /** - * Migrate dateUpdated to $updatedAt. - */ - if (empty($document->getUpdatedAt())) { - $document->setAttribute('$updatedAt', $document->getAttribute('dateUpdated')); - } - - /** - * Migrate all Storage Buckets to use Internal ID. - */ - $internalId = $this->projectDB->getDocument('buckets', $document->getId())->getInternalId(); - $this->createNewMetaData("bucket_{$internalId}"); - - /** - * Migrate all Storage Bucket Files. - */ - $this->migrateBucketFiles($document); - - break; - case 'users': - /** - * Set 'phoneVerification' to false if not set. - */ - if (is_null($document->getAttribute('phoneVerification'))) { - $document->setAttribute('phoneVerification', false); - } - - break; - case 'functions': - /** - * Migrate dateCreated to $createdAt. - */ - if (empty($document->getCreatedAt())) { - $document->setAttribute('$createdAt', $document->getAttribute('dateCreated')); - } - /** - * Migrate dateUpdated to $updatedAt. - */ - if (empty($document->getUpdatedAt())) { - $document->setAttribute('$updatedAt', $document->getAttribute('dateUpdated')); - } - - break; - case 'deployments': - case 'executions': - case 'teams': - /** - * Migrate dateCreated to $createdAt. - */ - if (empty($document->getCreatedAt())) { - $document->setAttribute('$createdAt', $document->getAttribute('dateCreated')); - } - - break; - } - - return $document; - } - - /** - * Creates new metadata that was introduced for a collection and enforces the Internal ID. - * - * @param string $id - * @return void - */ - protected function createNewMetaData(string $id, string $to = null): void - { - $to ??= $id; - /** - * Skip files collection. - */ - if (in_array($id, ['files', 'databases'])) { - return; - } - - try { - /** - * Replace project UID with Internal ID. - */ - $this->pdo->prepare("ALTER TABLE IF EXISTS `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getId()}_{$id}` RENAME TO `_{$this->project->getInternalId()}_{$to}`")->execute(); - } catch (\Throwable $th) { - Console::warning("Migrating {$id} Collection: {$th->getMessage()}"); - } - try { - /** - * Replace project UID with Internal ID on permissions table. - */ - $this->pdo->prepare("ALTER TABLE IF EXISTS `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getId()}_{$id}_perms` RENAME TO `_{$this->project->getInternalId()}_{$to}_perms`")->execute(); - } catch (\Throwable $th) { - Console::warning("Migrating {$id} Collection: {$th->getMessage()}"); - } - try { - /** - * Add _createdAt attribute. - */ - $this->pdo->prepare("ALTER TABLE `_{$this->project->getInternalId()}_{$to}` ADD COLUMN IF NOT EXISTS `_createdAt` int unsigned DEFAULT NULL")->execute(); - } catch (\Throwable $th) { - Console::warning("Migrating {$id} Collection: {$th->getMessage()}"); - } - try { - /** - * Add _updatedAt attribute. - */ - $this->pdo->prepare("ALTER TABLE `_{$this->project->getInternalId()}_{$to}` ADD COLUMN IF NOT EXISTS `_updatedAt` int unsigned DEFAULT NULL")->execute(); - } catch (\Throwable $th) { - Console::warning("Migrating {$id} Collection: {$th->getMessage()}"); - } - try { - /** - * Create index for _createdAt. - */ - $this->pdo->prepare("CREATE INDEX IF NOT EXISTS `_created_at` ON `_{$this->project->getInternalId()}_{$to}` (`_createdAt`)")->execute(); - } catch (\Throwable $th) { - Console::warning("Migrating {$id} Collection: {$th->getMessage()}"); - } - try { - /** - * Create index for _updatedAt. - */ - $this->pdo->prepare("CREATE INDEX IF NOT EXISTS `_updated_at` ON `_{$this->project->getInternalId()}_{$to}` (`_updatedAt`)")->execute(); - } catch (\Throwable $th) { - Console::warning("Migrating {$id} Collection: {$th->getMessage()}"); - } - } -} diff --git a/tests/unit/Migration/MigrationV12Test.php b/tests/unit/Migration/MigrationV12Test.php deleted file mode 100644 index 5b88ae7f5d..0000000000 --- a/tests/unit/Migration/MigrationV12Test.php +++ /dev/null @@ -1,79 +0,0 @@ -migration = new V12(); - $reflector = new ReflectionClass('Appwrite\Migration\Version\V12'); - $this->method = $reflector->getMethod('fixDocument'); - $this->method->setAccessible(true); - } - - public function testMigrationProjects(): void - { - $document = $this->fixDocument(new Document([ - '$id' => ID::custom('project'), - '$collection' => ID::custom('projects'), - 'name' => 'Appwrite', - 'version' => '0.12.0', - 'search' => '' - ])); - - $this->assertEquals($document->getAttribute('version'), '0.13.0'); - $this->assertEquals($document->getAttribute('search'), 'project Appwrite'); - } - - public function testMigrationUsers(): void - { - $document = $this->fixDocument(new Document([ - '$id' => ID::custom('user'), - '$collection' => ID::custom('users'), - 'email' => 'test@appwrite.io', - 'name' => 'Torsten Dittmann' - ])); - - $this->assertEquals($document->getAttribute('search'), 'user test@appwrite.io Torsten Dittmann'); - } - - public function testMigrationTeams(): void - { - $document = $this->fixDocument(new Document([ - '$id' => ID::custom('team'), - '$collection' => ID::custom('teams'), - 'name' => 'Appwrite' - ])); - - $this->assertEquals($document->getAttribute('search'), 'team Appwrite'); - } - - public function testMigrationFunctions(): void - { - $document = $this->fixDocument(new Document([ - '$id' => ID::custom('function'), - '$collection' => ID::custom('functions'), - 'name' => 'My Function', - 'runtime' => 'php-8.0' - ])); - - $this->assertEquals($document->getAttribute('search'), 'function My Function php-8.0'); - } - - public function testMigrationExecutions(): void - { - $document = $this->fixDocument(new Document([ - '$id' => ID::custom('execution'), - '$collection' => ID::custom('executions'), - 'functionId' => ID::custom('function') - ])); - - $this->assertEquals($document->getAttribute('search'), 'execution function'); - } -} diff --git a/tests/unit/Migration/MigrationV13Test.php b/tests/unit/Migration/MigrationV13Test.php deleted file mode 100644 index a0daaf309d..0000000000 --- a/tests/unit/Migration/MigrationV13Test.php +++ /dev/null @@ -1,41 +0,0 @@ -migration = new V13(); - $reflector = new ReflectionClass('Appwrite\Migration\Version\V13'); - $this->method = $reflector->getMethod('fixDocument'); - $this->method->setAccessible(true); - } - - public function testMigrateFunctions(): void - { - $document = $this->fixDocument(new Document([ - '$id' => ID::custom('func'), - '$collection' => ID::custom('functions'), - 'events' => ['account.create', 'users.create'] - ])); - - $this->assertEquals($document->getAttribute('events'), ['users.*.create']); - } - - public function testMigrationWebhooks(): void - { - $document = $this->fixDocument(new Document([ - '$id' => ID::custom('webh'), - '$collection' => ID::custom('webhooks'), - 'events' => ['account.create', 'users.create'] - ])); - - $this->assertEquals($document->getAttribute('events'), ['users.*.create']); - } -} diff --git a/tests/unit/Migration/MigrationV14Test.php b/tests/unit/Migration/MigrationV14Test.php deleted file mode 100644 index 0ca249069b..0000000000 --- a/tests/unit/Migration/MigrationV14Test.php +++ /dev/null @@ -1,173 +0,0 @@ -migration = new V14(); - $reflector = new ReflectionClass('Appwrite\Migration\Version\V14'); - $this->method = $reflector->getMethod('fixDocument'); - $this->method->setAccessible(true); - } - - public function testMigrateProjects(): void - { - $document = $this->fixDocument(new Document([ - '$id' => ID::custom('appwrite'), - '$collection' => ID::custom('projects'), - 'version' => '0.14.0' - ])); - - $this->assertEquals($document->getAttribute('version'), '0.15.0'); - $this->assertEquals($document->getAttribute('version'), '0.15.0'); - } - - public function testMigrateKeys(): void - { - $document = $this->fixDocument(new Document([ - '$id' => ID::custom('appwrite'), - '$collection' => 'keys' - ])); - - $this->assertArrayHasKey('expire', $document->getArrayCopy()); - $this->assertEquals($document->getAttribute('expire'), 0); - } - - public function testMigrateWebhooks(): void - { - $document = $this->fixDocument(new Document([ - '$id' => ID::custom('appwrite'), - '$collection' => 'webhooks' - ])); - - $this->assertArrayHasKey('signatureKey', $document->getArrayCopy()); - $this->assertEquals(strlen($document->getAttribute('signatureKey')), 128); - } - - public function testMigrateUsers(): void - { - $document = $this->fixDocument(new Document([ - '$id' => ID::custom('appwrite'), - '$collection' => ID::custom('users'), - 'phoneVerification' => null - ])); - - $this->assertArrayHasKey('phoneVerification', $document->getArrayCopy()); - $this->assertFalse($document->getAttribute('phoneVerification')); - } - - public function testMigratePlatforms(): void - { - $document = $this->fixDocument(new Document([ - '$id' => ID::custom('appwrite'), - '$collection' => ID::custom('platforms'), - '$createdAt' => null, - '$updatedAt' => null, - 'dateCreated' => 123456789, - 'dateUpdated' => 987654321 - ])); - - $this->assertEquals($document->getCreatedAt(), 123456789); - $this->assertEquals($document->getUpdatedAt(), 987654321); - } - - public function testMigrateFunctions(): void - { - $document = $this->fixDocument(new Document([ - '$id' => ID::custom('appwrite'), - '$collection' => ID::custom('functions'), - '$createdAt' => null, - '$updatedAt' => null, - 'dateCreated' => 123456789, - 'dateUpdated' => 987654321 - ])); - - $this->assertEquals($document->getCreatedAt(), 123456789); - $this->assertEquals($document->getUpdatedAt(), 987654321); - } - - public function testMigrateDeployments(): void - { - $document = $this->fixDocument(new Document([ - '$id' => ID::custom('appwrite'), - '$collection' => ID::custom('deployments'), - '$createdAt' => null, - 'dateCreated' => 123456789, - ])); - - $this->assertEquals($document->getCreatedAt(), 123456789); - } - - public function testMigrateExecutions(): void - { - $document = $this->fixDocument(new Document([ - '$id' => ID::custom('appwrite'), - '$collection' => ID::custom('executions'), - '$createdAt' => null, - 'dateCreated' => 123456789, - ])); - - $this->assertEquals($document->getCreatedAt(), 123456789); - } - - public function testMigrateTeams(): void - { - $document = $this->fixDocument(new Document([ - '$id' => ID::custom('appwrite'), - '$collection' => ID::custom('teams'), - '$createdAt' => null, - 'dateCreated' => 123456789, - ])); - - $this->assertEquals($document->getCreatedAt(), 123456789); - } - - public function testMigrateAudits(): void - { - $document = $this->fixDocument(new Document([ - '$id' => ID::custom('appwrite'), - '$collection' => ID::custom('audit'), - 'resource' => 'collection/movies', - 'event' => 'collections.movies.create' - ])); - - $this->assertEquals($document->getAttribute('resource'), 'database/default/collection/movies'); - $this->assertEquals($document->getAttribute('event'), 'databases.default.collections.movies.create'); - - $document = $this->fixDocument(new Document([ - '$id' => ID::custom('appwrite'), - '$collection' => ID::custom('audit'), - 'resource' => 'document/avatar', - 'event' => 'collections.movies.documents.avatar.create' - ])); - - $this->assertEquals($document->getAttribute('resource'), 'database/default/collection/movies/document/avatar'); - $this->assertEquals($document->getAttribute('event'), 'databases.default.collections.movies.documents.avatar.create'); - } - - public function testMigrateStats(): void - { - $document = $this->fixDocument(new Document([ - '$id' => ID::custom('appwrite'), - '$collection' => ID::custom('stats'), - 'metric' => 'database.collections.62b2039844d4277495d0.documents.create' - ])); - - $this->assertEquals($document->getAttribute('metric'), 'databases.default.collections.62b2039844d4277495d0.documents.create'); - - $document = $this->fixDocument(new Document([ - '$id' => ID::custom('appwrite'), - '$collection' => ID::custom('stats'), - 'metric' => 'users.create' - ])); - - $this->assertEquals($document->getAttribute('metric'), 'users.create'); - } -}