Merge pull request #8403 from appwrite/feat-1.6.x-migrations-and-filters

Feat 1.6.x migrations and filters
This commit is contained in:
Jake Barnby 2024-07-23 19:25:58 +12:00 committed by GitHub
commit c465026fd1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 299 additions and 4 deletions

View file

@ -4751,8 +4751,8 @@ $consoleCollections = array_merge([
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'required' => false,
'default' => [],
'array' => true,
'filters' => [],
],

View file

@ -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");
}

View file

@ -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;

View file

@ -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'
];
/**

View file

@ -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;
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace Appwrite\Utopia\Request\Filters;
use Appwrite\Utopia\Request\Filter;
class V18 extends Filter
{
// Convert 1.5 params to 1.6
public function parse(array $content, string $model): array
{
switch ($model) {
case 'account.deleteMfaAuthenticator':
unset($content['otp']);
break;
}
return $content;
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace Appwrite\Utopia\Response\Filters;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Filter;
class V18 extends Filter
{
// Convert 1.6 Data format to 1.5 format
public function parse(array $content, string $model): array
{
$parsedResponse = $content;
$parsedResponse = match($model) {
Response::MODEL_FUNCTION => $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;
}
}

View file

@ -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,

View file

@ -0,0 +1,51 @@
<?php
namespace Tests\Unit\Utopia\Request\Filters;
use Appwrite\Utopia\Request\Filter;
use Appwrite\Utopia\Request\Filters\V18;
use PHPUnit\Framework\TestCase;
class V18Test extends TestCase
{
/**
* @var Filter
*/
protected $filter;
public function setUp(): void
{
$this->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);
}
}

View file

@ -0,0 +1,84 @@
<?php
namespace Tests\Unit\Utopia\Response\Filters;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Filters\V18;
use PHPUnit\Framework\TestCase;
class V18Test extends TestCase
{
/**
* @var Filter
*/
protected $filter = null;
public function setUp(): void
{
$this->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);
}
}