diff --git a/app/config/collections.php b/app/config/collections.php index d0c3df165e..b4dc1049fb 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -4751,8 +4751,8 @@ $consoleCollections = array_merge([ 'format' => '', 'size' => Database::LENGTH_KEY, 'signed' => true, - 'required' => true, - 'default' => null, + 'required' => false, + 'default' => [], 'array' => true, 'filters' => [], ], diff --git a/app/controllers/general.php b/app/controllers/general.php index 10c9eb8e18..70acd9d482 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -11,9 +11,11 @@ use Appwrite\Network\Validator\Origin; use Appwrite\Utopia\Request; use Appwrite\Utopia\Request\Filters\V16 as RequestV16; use Appwrite\Utopia\Request\Filters\V17 as RequestV17; +use Appwrite\Utopia\Request\Filters\V18 as RequestV18; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Filters\V16 as ResponseV16; use Appwrite\Utopia\Response\Filters\V17 as ResponseV17; +use Appwrite\Utopia\Response\Filters\V18 as ResponseV18; use Appwrite\Utopia\View; use Executor\Executor; use MaxMind\Db\Reader; @@ -434,6 +436,9 @@ App::init() if (version_compare($requestFormat, '1.5.0', '<')) { $request->addFilter(new RequestV17()); } + if (version_compare($requestFormat, '1.6.0', '<')) { + $request->addFilter(new RequestV18()); + } } $domain = $request->getHostname(); @@ -550,6 +555,9 @@ App::init() if (version_compare($responseFormat, '1.5.0', '<')) { $response->addFilter(new ResponseV17()); } + if (version_compare($responseFormat, '1.6.0', '<')) { + $response->addFilter(new ResponseV18()); + } if (version_compare($responseFormat, APP_VERSION_STABLE, '>')) { $response->addHeader('X-Appwrite-Warning', "The current SDK is built for Appwrite " . $responseFormat . ". However, the current Appwrite server version is ". APP_VERSION_STABLE . ". Please downgrade your SDK to match the Appwrite version: https://appwrite.io/docs/sdks"); } diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 583a0160a1..672366fa01 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -206,6 +206,7 @@ App::init() throw new Exception(Exception::USER_API_KEY_AND_SESSION_SET); } + // Remove after migration if(!\str_contains($apiKey, '_')) { $keyType = API_KEY_STANDARD; $authKey = $apiKey; diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php index 597943f842..9075b85702 100644 --- a/src/Appwrite/Migration/Migration.php +++ b/src/Appwrite/Migration/Migration.php @@ -86,7 +86,7 @@ abstract class Migration '1.5.5' => 'V20', '1.5.6' => 'V20', '1.5.7' => 'V20', - '1.6.0' => 'V21', + '1.6.0' => 'V21' ]; /** diff --git a/src/Appwrite/Migration/Version/V21.php b/src/Appwrite/Migration/Version/V21.php index 75d083d87d..be6c38be67 100644 --- a/src/Appwrite/Migration/Version/V21.php +++ b/src/Appwrite/Migration/Version/V21.php @@ -7,6 +7,8 @@ use Exception; use Throwable; use Utopia\CLI\Console; use Utopia\Database\Database; +use Utopia\Database\DateTime; +use Utopia\Database\Document; class V21 extends Migration { @@ -31,6 +33,9 @@ class V21 extends Migration Console::info('Migrating Collections'); $this->migrateCollections(); + + Console::info('Migrating Documents'); + $this->forEachDocument([$this, 'fixDocument']); } /** @@ -57,16 +62,92 @@ class V21 extends Migration switch ($id) { case 'projects': - // Create accessedAt attribute try { $this->createAttributeFromCollection($this->projectDB, $id, 'accessedAt'); } catch (Throwable $th) { Console::warning("'accessedAt' from {$id}: {$th->getMessage()}"); } + break; + case 'schedules': + // Create data attribute + try { + $this->createAttributeFromCollection($this->projectDB, $id, 'data'); + } catch (Throwable $th) { + Console::warning("'data' from {$id}: {$th->getMessage()}"); + } break; + + case 'functions': + // Create scopes attribute + try { + $this->createAttributeFromCollection($this->projectDB, $id, 'scopes'); + } catch (Throwable $th) { + Console::warning("'scopes' from {$id}: {$th->getMessage()}"); + } + + // Create size attribute + try { + $this->createAttributeFromCollection($this->projectDB, $id, 'size'); + } catch (Throwable $th) { + Console::warning("'size' from {$id}: {$th->getMessage()}"); + } + + break; + case 'executions': + // Create requestMethod index + try { + $this->createIndexFromCollection($this->projectDB, $id, '_key_requestMethod'); + } catch (\Throwable $th) { + Console::warning("'_key_requestMethod' from {$id}: {$th->getMessage()}"); + } + + // Create requestPath index + try { + $this->createIndexFromCollection($this->projectDB, $id, '_key_requestPath'); + } catch (\Throwable $th) { + Console::warning("'_key_requestPath' from {$id}: {$th->getMessage()}"); + } + + // Create deployment index + try { + $this->createIndexFromCollection($this->projectDB, $id, '_key_deployment'); + } catch (\Throwable $th) { + Console::warning("'_key_deployment' from {$id}: {$th->getMessage()}"); + } } + + usleep(50000); } } + + /** + * Fix run on each document + * + * @param Document $document + * @return Document + */ + protected function fixDocument(Document $document): Document + { + switch ($document->getCollection()) { + case 'projects': + /** + * Bump version number. + */ + $document->setAttribute('version', '1.6.0'); + + // Add accessedAt attribute + $document->setAttribute('accessedAt', DateTime::now()); + break; + case 'functions': + // Add scopes attribute + $document->setAttribute('scopes', []); + + // Add size attribute + $document->setAttribute('size', 's-1vcpu-512m'); + } + + return $document; + } } diff --git a/src/Appwrite/Utopia/Request/Filters/V18.php b/src/Appwrite/Utopia/Request/Filters/V18.php new file mode 100644 index 0000000000..4f889ce66b --- /dev/null +++ b/src/Appwrite/Utopia/Request/Filters/V18.php @@ -0,0 +1,21 @@ + $this->parseFunction($content), + Response::MODEL_PROJECT => $this->parseProject($content), + default => $parsedResponse, + }; + + return $parsedResponse; + } + + protected function parseFunction(array $content) + { + unset($content['scopes']); + return $content; + } + + protected function parseProject(array $content) + { + unset($content['authMockNumbers']); + unset($content['authSessionAlerts']); + return $content; + } +} diff --git a/src/Executor/Executor.php b/src/Executor/Executor.php index 29ed756932..3206381e95 100644 --- a/src/Executor/Executor.php +++ b/src/Executor/Executor.php @@ -75,6 +75,12 @@ class Executor $runtimeId = "$projectId-$deploymentId-build"; $route = "/runtimes"; $timeout = (int) System::getEnv('_APP_FUNCTIONS_BUILD_TIMEOUT', 900); + + // Remove after migration + if ($version == 'v3') { + $version = 'v4'; + } + $params = [ 'runtimeId' => $runtimeId, 'source' => $source, @@ -188,6 +194,13 @@ class Executor $runtimeId = "$projectId-$deploymentId"; $route = '/runtimes/' . $runtimeId . '/execution'; + + + // Remove after migration + if ($version == 'v3') { + $version = 'v4'; + } + $params = [ 'runtimeId' => $runtimeId, 'variables' => $variables, diff --git a/tests/unit/Utopia/Request/Filters/V18Test.php b/tests/unit/Utopia/Request/Filters/V18Test.php new file mode 100644 index 0000000000..4e1f81573a --- /dev/null +++ b/tests/unit/Utopia/Request/Filters/V18Test.php @@ -0,0 +1,51 @@ +filter = new V18(); + } + + public function tearDown(): void + { + } + + public function deleteMfaAuthenticatorProvider() + { + return [ + 'remove otp' => [ + [ + 'type' => 'totp', + 'otp' => 1230 + ], + [ + 'type' => 'totp' + ] + ] + ]; + } + + /** + * @dataProvider deleteMfaAuthenticatorProvider + */ + public function testdeleteMfaAuthenticator(array $content, array $expected): void + { + $model = 'account.deleteMfaAuthenticator'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } +} diff --git a/tests/unit/Utopia/Response/Filters/V18Test.php b/tests/unit/Utopia/Response/Filters/V18Test.php new file mode 100644 index 0000000000..36719a7620 --- /dev/null +++ b/tests/unit/Utopia/Response/Filters/V18Test.php @@ -0,0 +1,84 @@ +filter = new V18(); + } + + public function tearDown(): void + { + } + + public function functionProvider(): array + { + return [ + 'remove scopes' => [ + [ + 'scopes' => [ + 'example_scope', + 'example_scope2', + ], + ], + [ + ] + ] + ]; + } + + /** + * @dataProvider functionProvider + */ + public function testFunction(array $content, array $expected): void + { + $model = Response::MODEL_FUNCTION; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + public function projectProvider(): array + { + return [ + 'remove authMockNumbers and authSessionAlerts' => [ + [ + 'authMockNumbers' => [ + 'example_mock_number', + 'example_mock_number2', + ], + 'authSessionAlerts' => [ + 'example_alert', + 'example_alert2', + ], + ], + [ + ] + ] + ]; + } + + /** + * @dataProvider projectProvider + */ + public function testProject(array $content, array $expected): void + { + $model = Response::MODEL_PROJECT; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } +}