Merge branch 'debug-whitelist-console' of https://github.com/appwrite/appwrite into multi-region-support

This commit is contained in:
shimon 2024-11-21 22:00:17 +02:00
commit 3d333633e4
11 changed files with 163 additions and 141 deletions

View file

@ -127,6 +127,11 @@ jobs:
Messaging,
Migrations
]
tables-mode: [
'Project',
'Shared V1',
'Shared V2',
]
steps:
- name: checkout
@ -145,11 +150,11 @@ jobs:
docker compose up -d
sleep 30
- name: Run ${{matrix.service}} Tests
run: docker compose exec -T appwrite test /usr/src/code/tests/e2e/Services/${{matrix.service}} --debug
- name: Run ${{matrix.service}} Shared Tables Tests
run: _APP_DATABASE_SHARED_TABLES=database_db_main docker compose exec -T appwrite test /usr/src/code/tests/e2e/Services/${{matrix.service}} --debug
- name: Run ${{ matrix.service }} tests with ${{ matrix.tables-mode }} table mode
run: docker compose exec -T appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug
env:
_APP_DATABASE_SHARED_TABLES: ${{ matrix.table_mode == 'Shared V1' || matrix.table_mode == 'Shared V2' && 'database_db_main' || '' }}
_APP_DATABASE_SHARED_TABLES_V1: ${{ matrix.table_mode == 'Shared V1' && 'database_db_main' || '' }}
benchmarking:
name: Benchmark

View file

@ -686,7 +686,7 @@ return [
],
Exception::ATTRIBUTE_LIMIT_EXCEEDED => [
'name' => Exception::ATTRIBUTE_LIMIT_EXCEEDED,
'description' => 'The maximum number of attributes has been reached.',
'description' => 'The maximum number or size of attributes for this collection has been reached.',
'code' => 400,
],
Exception::ATTRIBUTE_VALUE_INVALID => [

View file

@ -153,7 +153,7 @@ function createAttribute(string $databaseId, string $collectionId, Document $att
} catch (DuplicateException) {
throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS);
} catch (LimitException) {
throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED, 'Attribute limit exceeded');
throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED);
} catch (\Throwable $e) {
$dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collectionId);
$dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $collection->getInternalId());
@ -197,7 +197,7 @@ function createAttribute(string $databaseId, string $collectionId, Document $att
throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS);
} catch (LimitException) {
$dbForProject->deleteDocument('attributes', $attribute->getId());
throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED, 'Attribute limit exceeded');
throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED);
} catch (\Throwable $e) {
$dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedCollection->getId());
$dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedCollection->getInternalId());
@ -393,6 +393,8 @@ function updateAttribute(
throw new Exception(Exception::ATTRIBUTE_INVALID_RESIZE);
} catch (NotFoundException) {
throw new Exception(Exception::ATTRIBUTE_NOT_FOUND);
} catch (LimitException) {
throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED);
}
}
@ -2627,7 +2629,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes')
$validator = new IndexValidator(
$collection->getAttribute('attributes'),
$dbForProject->getAdapter()->getMaxIndexLength()
$dbForProject->getAdapter()->getMaxIndexLength(),
$dbForProject->getAdapter()->getInternalIndexesKeys(),
);
if (!$validator->isValid($index)) {
throw new Exception(Exception::INDEX_INVALID, $validator->getDescription());

View file

@ -136,6 +136,16 @@ App::post('/v1/projects')
$dsn = $databases[array_rand($databases)];
}
$dsns = [
'fra' => 'database_db_fra1_self_hosted_0_0',
'syd' => 'database_db_syd1_self_hosted_0_0',
'nyc' => 'database_db_nyc1_self_hosted_0_0',
];
if (isset($dsns[$region])) {
$dsn = $dsns[$region];
}
// TODO: Temporary until all projects are using shared tables.
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));

View file

@ -63,7 +63,7 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
}
if (System::getEnv('_APP_OPTIONS_ROUTER_PROTECTION', 'disabled') === 'enabled') {
if ($host !== 'localhost' && $host !== APP_HOSTNAME_INTERNAL) { // localhost allowed for proxy, APP_HOSTNAME_INTERNAL allowed for migrations
if ($host !== 'localhost' && $host !== APP_HOSTNAME_INTERNAL && $host !== System::getEnv('_APP_CONSOLE_DOMAIN', '')) {
throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'Router protection does not allow accessing Appwrite over this domain. Please add it as custom domain to your project or disable _APP_OPTIONS_ROUTER_PROTECTION environment variable.');
}
}
@ -438,7 +438,7 @@ App::init()
});
App::init()
->groups(['api', 'web'])
->groups(['api'])
->inject('utopia')
->inject('swooleRequest')
->inject('request')

View file

@ -61,8 +61,6 @@ include __DIR__ . '/controllers/general.php';
$http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $register) {
$app = new App('UTC');
$app->setCompression(true);
$app->setCompressionMinSize(intval(System::getEnv('_APP_COMPRESSION_MIN_SIZE_BYTES', '1024'))); // 1KB
go(function () use ($register, $app) {
$pools = $register->get('pools');

View file

@ -51,7 +51,7 @@
"utopia-php/cache": "0.11.*",
"utopia-php/cli": "0.15.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "0.53.18",
"utopia-php/database": "0.53.20",
"utopia-php/domains": "0.5.*",
"utopia-php/dsn": "0.2.1",
"utopia-php/framework": "0.33.*",

52
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "bd9b8f5f8fe295deb07002ca0a953949",
"content-hash": "217b0c1b6c156d51bf5a2674f87a7630",
"packages": [
{
"name": "adhocore/jwt",
@ -1770,16 +1770,16 @@
},
{
"name": "utopia-php/database",
"version": "0.53.18",
"version": "0.53.20",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "895176b61b969d326bf2e36695606b8a33132094"
"reference": "e43f8ee26e06ee8812737e63642dbd7ee7c9dc04"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/895176b61b969d326bf2e36695606b8a33132094",
"reference": "895176b61b969d326bf2e36695606b8a33132094",
"url": "https://api.github.com/repos/utopia-php/database/zipball/e43f8ee26e06ee8812737e63642dbd7ee7c9dc04",
"reference": "e43f8ee26e06ee8812737e63642dbd7ee7c9dc04",
"shasum": ""
},
"require": {
@ -1820,9 +1820,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.53.18"
"source": "https://github.com/utopia-php/database/tree/0.53.20"
},
"time": "2024-11-08T04:02:13+00:00"
"time": "2024-11-12T00:23:36+00:00"
},
{
"name": "utopia-php/domains",
@ -2222,16 +2222,16 @@
},
{
"name": "utopia-php/migration",
"version": "0.6.11",
"version": "0.6.12",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/migration.git",
"reference": "4d167914d3f7fa1fe816b2b2c6f221e70166bfd7"
"reference": "9a8c905af4cece5c5ec9542a5b534befce067260"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/4d167914d3f7fa1fe816b2b2c6f221e70166bfd7",
"reference": "4d167914d3f7fa1fe816b2b2c6f221e70166bfd7",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/9a8c905af4cece5c5ec9542a5b534befce067260",
"reference": "9a8c905af4cece5c5ec9542a5b534befce067260",
"shasum": ""
},
"require": {
@ -2272,9 +2272,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/migration/issues",
"source": "https://github.com/utopia-php/migration/tree/0.6.11"
"source": "https://github.com/utopia-php/migration/tree/0.6.12"
},
"time": "2024-10-31T06:19:57+00:00"
"time": "2024-11-12T00:31:53+00:00"
},
{
"name": "utopia-php/mongo",
@ -2542,16 +2542,16 @@
},
{
"name": "utopia-php/queue",
"version": "0.7.1",
"version": "0.7.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/queue.git",
"reference": "94c240d9f6383829807ce7b2d737f04b159fd3e8"
"reference": "40fdd9799d0a11dd33fca06f8223032a47dce2f6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/queue/zipball/94c240d9f6383829807ce7b2d737f04b159fd3e8",
"reference": "94c240d9f6383829807ce7b2d737f04b159fd3e8",
"url": "https://api.github.com/repos/utopia-php/queue/zipball/40fdd9799d0a11dd33fca06f8223032a47dce2f6",
"reference": "40fdd9799d0a11dd33fca06f8223032a47dce2f6",
"shasum": ""
},
"require": {
@ -2597,9 +2597,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/queue/issues",
"source": "https://github.com/utopia-php/queue/tree/0.7.1"
"source": "https://github.com/utopia-php/queue/tree/0.7.2"
},
"time": "2024-11-05T17:00:38+00:00"
"time": "2024-11-11T10:04:02+00:00"
},
{
"name": "utopia-php/registry",
@ -2817,16 +2817,16 @@
},
{
"name": "utopia-php/vcs",
"version": "0.8.3",
"version": "0.8.5",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/vcs.git",
"reference": "a032ed0611a8f4467aeaa9484f73223074457337"
"reference": "7622330628d53844a3873ca873338150756bab82"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/a032ed0611a8f4467aeaa9484f73223074457337",
"reference": "a032ed0611a8f4467aeaa9484f73223074457337",
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/7622330628d53844a3873ca873338150756bab82",
"reference": "7622330628d53844a3873ca873338150756bab82",
"shasum": ""
},
"require": {
@ -2860,9 +2860,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/vcs/issues",
"source": "https://github.com/utopia-php/vcs/tree/0.8.3"
"source": "https://github.com/utopia-php/vcs/tree/0.8.5"
},
"time": "2024-11-05T17:10:09+00:00"
"time": "2024-11-11T18:33:10+00:00"
},
{
"name": "utopia-php/websocket",
@ -7052,7 +7052,7 @@
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {

View file

@ -92,6 +92,7 @@ class Databases extends Action
* @throws Authorization
* @throws Conflict
* @throws \Exception
* @throws \Throwable
*/
private function createAttribute(Document $database, Document $collection, Document $attribute, Document $project, Database $dbForConsole, Database $dbForProject): void
{
@ -169,7 +170,6 @@ class Databases extends Action
$dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'available'));
} catch (\Throwable $e) {
// TODO: Send non DatabaseExceptions to Sentry
Console::error($e->getMessage());
if ($e instanceof DatabaseException) {
@ -192,15 +192,17 @@ class Databases extends Action
$relatedAttribute->setAttribute('status', 'failed')
);
}
throw $e;
} finally {
$this->trigger($database, $collection, $attribute, $project, $projectId, $events);
}
if ($type === Database::VAR_RELATIONSHIP && $options['twoWay']) {
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId());
}
if ($type === Database::VAR_RELATIONSHIP && $options['twoWay']) {
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId());
}
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId);
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId);
}
}
/**
@ -214,6 +216,7 @@ class Databases extends Action
* @throws Authorization
* @throws Conflict
* @throws \Exception
* @throws \Throwable
**/
private function deleteAttribute(Document $database, Document $collection, Document $attribute, Document $project, Database $dbForConsole, Database $dbForProject): void
{
@ -247,113 +250,116 @@ class Databases extends Action
// - stuck: attribute was available but cannot be removed
try {
if ($status !== 'failed') {
if ($type === Database::VAR_RELATIONSHIP) {
if ($options['twoWay']) {
$relatedCollection = $dbForProject->getDocument('database_' . $database->getInternalId(), $options['relatedCollection']);
if ($relatedCollection->isEmpty()) {
throw new DatabaseException('Collection not found');
try {
if ($status !== 'failed') {
if ($type === Database::VAR_RELATIONSHIP) {
if ($options['twoWay']) {
$relatedCollection = $dbForProject->getDocument('database_' . $database->getInternalId(), $options['relatedCollection']);
if ($relatedCollection->isEmpty()) {
throw new DatabaseException('Collection not found');
}
$relatedAttribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $options['twoWayKey']);
}
$relatedAttribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $options['twoWayKey']);
}
if (!$dbForProject->deleteRelationship('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) {
$dbForProject->updateDocument('attributes', $relatedAttribute->getId(), $relatedAttribute->setAttribute('status', 'stuck'));
throw new DatabaseException('Failed to delete Relationship');
if (!$dbForProject->deleteRelationship('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) {
$dbForProject->updateDocument('attributes', $relatedAttribute->getId(), $relatedAttribute->setAttribute('status', 'stuck'));
throw new DatabaseException('Failed to delete Relationship');
}
} elseif (!$dbForProject->deleteAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) {
throw new DatabaseException('Failed to delete Attribute');
}
} elseif (!$dbForProject->deleteAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) {
throw new DatabaseException('Failed to delete Attribute');
}
}
$dbForProject->deleteDocument('attributes', $attribute->getId());
$dbForProject->deleteDocument('attributes', $attribute->getId());
if (!$relatedAttribute->isEmpty()) {
$dbForProject->deleteDocument('attributes', $relatedAttribute->getId());
}
} catch (\Throwable $e) {
// TODO: Send non DatabaseExceptions to Sentry
Console::error($e->getMessage());
if ($e instanceof DatabaseException) {
$attribute->setAttribute('error', $e->getMessage());
if (!$relatedAttribute->isEmpty()) {
$relatedAttribute->setAttribute('error', $e->getMessage());
$dbForProject->deleteDocument('attributes', $relatedAttribute->getId());
}
} catch (\Throwable $e) {
Console::error($e->getMessage());
if ($e instanceof DatabaseException) {
$attribute->setAttribute('error', $e->getMessage());
if (!$relatedAttribute->isEmpty()) {
$relatedAttribute->setAttribute('error', $e->getMessage());
}
}
}
$dbForProject->updateDocument(
'attributes',
$attribute->getId(),
$attribute->setAttribute('status', 'stuck')
);
if (!$relatedAttribute->isEmpty()) {
$dbForProject->updateDocument(
'attributes',
$relatedAttribute->getId(),
$relatedAttribute->setAttribute('status', 'stuck')
$attribute->getId(),
$attribute->setAttribute('status', 'stuck')
);
if (!$relatedAttribute->isEmpty()) {
$dbForProject->updateDocument(
'attributes',
$relatedAttribute->getId(),
$relatedAttribute->setAttribute('status', 'stuck')
);
}
throw $e;
} finally {
$this->trigger($database, $collection, $attribute, $project, $projectId, $events);
}
} finally {
$this->trigger($database, $collection, $attribute, $project, $projectId, $events);
}
// The underlying database removes/rebuilds indexes when attribute is removed
// Update indexes table with changes
/** @var Document[] $indexes */
$indexes = $collection->getAttribute('indexes', []);
// The underlying database removes/rebuilds indexes when attribute is removed
// Update indexes table with changes
/** @var Document[] $indexes */
$indexes = $collection->getAttribute('indexes', []);
foreach ($indexes as $index) {
/** @var string[] $attributes */
$attributes = $index->getAttribute('attributes');
$lengths = $index->getAttribute('lengths');
$orders = $index->getAttribute('orders');
foreach ($indexes as $index) {
/** @var string[] $attributes */
$attributes = $index->getAttribute('attributes');
$lengths = $index->getAttribute('lengths');
$orders = $index->getAttribute('orders');
$found = \array_search($key, $attributes);
$found = \array_search($key, $attributes);
if ($found !== false) {
// If found, remove entry from attributes, lengths, and orders
// array_values wraps array_diff to reindex array keys
// when found attribute is removed from array
$attributes = \array_values(\array_diff($attributes, [$attributes[$found]]));
$lengths = \array_values(\array_diff($lengths, isset($lengths[$found]) ? [$lengths[$found]] : []));
$orders = \array_values(\array_diff($orders, isset($orders[$found]) ? [$orders[$found]] : []));
if ($found !== false) {
// If found, remove entry from attributes, lengths, and orders
// array_values wraps array_diff to reindex array keys
// when found attribute is removed from array
$attributes = \array_values(\array_diff($attributes, [$attributes[$found]]));
$lengths = \array_values(\array_diff($lengths, isset($lengths[$found]) ? [$lengths[$found]] : []));
$orders = \array_values(\array_diff($orders, isset($orders[$found]) ? [$orders[$found]] : []));
if (empty($attributes)) {
$dbForProject->deleteDocument('indexes', $index->getId());
} else {
$index
->setAttribute('attributes', $attributes, Document::SET_TYPE_ASSIGN)
->setAttribute('lengths', $lengths, Document::SET_TYPE_ASSIGN)
->setAttribute('orders', $orders, Document::SET_TYPE_ASSIGN);
// Check if an index exists with the same attributes and orders
$exists = false;
foreach ($indexes as $existing) {
if (
$existing->getAttribute('key') !== $index->getAttribute('key') // Ignore itself
&& $existing->getAttribute('attributes') === $index->getAttribute('attributes')
&& $existing->getAttribute('orders') === $index->getAttribute('orders')
) {
$exists = true;
break;
}
}
if ($exists) { // Delete the duplicate if created, else update in db
$this->deleteIndex($database, $collection, $index, $project, $dbForConsole, $dbForProject);
if (empty($attributes)) {
$dbForProject->deleteDocument('indexes', $index->getId());
} else {
$dbForProject->updateDocument('indexes', $index->getId(), $index);
$index
->setAttribute('attributes', $attributes, Document::SET_TYPE_ASSIGN)
->setAttribute('lengths', $lengths, Document::SET_TYPE_ASSIGN)
->setAttribute('orders', $orders, Document::SET_TYPE_ASSIGN);
// Check if an index exists with the same attributes and orders
$exists = false;
foreach ($indexes as $existing) {
if (
$existing->getAttribute('key') !== $index->getAttribute('key') // Ignore itself
&& $existing->getAttribute('attributes') === $index->getAttribute('attributes')
&& $existing->getAttribute('orders') === $index->getAttribute('orders')
) {
$exists = true;
break;
}
}
if ($exists) { // Delete the duplicate if created, else update in db
$this->deleteIndex($database, $collection, $index, $project, $dbForConsole, $dbForProject);
} else {
$dbForProject->updateDocument('indexes', $index->getId(), $index);
}
}
}
}
}
} finally {
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId);
$dbForProject->purgeCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId());
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId);
$dbForProject->purgeCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId());
if (!$relatedCollection->isEmpty() && !$relatedAttribute->isEmpty()) {
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId());
$dbForProject->purgeCachedCollection('database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId());
if (!$relatedCollection->isEmpty() && !$relatedAttribute->isEmpty()) {
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId());
$dbForProject->purgeCachedCollection('database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId());
}
}
}
@ -369,6 +375,7 @@ class Databases extends Action
* @throws Conflict
* @throws Structure
* @throws DatabaseException
* @throws \Throwable
*/
private function createIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForConsole, Database $dbForProject): void
{
@ -400,9 +407,7 @@ class Databases extends Action
}
$dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'available'));
} catch (\Throwable $e) {
// TODO: Send non DatabaseExceptions to Sentry
Console::error($e->getMessage());
if ($e instanceof DatabaseException) {
$index->setAttribute('error', $e->getMessage());
}
@ -411,11 +416,12 @@ class Databases extends Action
$index->getId(),
$index->setAttribute('status', 'failed')
);
throw $e;
} finally {
$this->trigger($database, $collection, $index, $project, $projectId, $events);
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId);
}
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId);
}
/**
@ -430,6 +436,7 @@ class Databases extends Action
* @throws Conflict
* @throws Structure
* @throws DatabaseException
* @throws \Throwable
*/
private function deleteIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForConsole, Database $dbForProject): void
{
@ -458,7 +465,6 @@ class Databases extends Action
$dbForProject->deleteDocument('indexes', $index->getId());
$index->setAttribute('status', 'deleted');
} catch (\Throwable $e) {
// TODO: Send non DatabaseExceptions to Sentry
Console::error($e->getMessage());
if ($e instanceof DatabaseException) {
@ -469,11 +475,13 @@ class Databases extends Action
$index->getId(),
$index->setAttribute('status', 'stuck')
);
throw $e;
} finally {
$this->trigger($database, $collection, $index, $project, $projectId, $events);
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collection->getId());
}
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collection->getId());
}
/**

View file

@ -268,8 +268,6 @@ class Migrations extends Action
$transfer = $source = $destination = null;
try {
$migration = $this->dbForProject->getDocument('migrations', $migration->getId());
if (
$migration->getAttribute('source') === SourceAppwrite::getName() &&
empty($migration->getAttribute('credentials', []))

View file

@ -1362,7 +1362,7 @@ class DatabasesCustomServerTest extends Scope
]);
$this->assertEquals(400, $tooWide['headers']['status-code']);
$this->assertEquals('Attribute limit exceeded', $tooWide['body']['message']);
$this->assertEquals('attribute_limit_exceeded', $tooWide['body']['type']);
}
public function testIndexLimitException()