mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Cleanup on create failures
This commit is contained in:
@@ -119,36 +119,55 @@ class Create extends Action
|
||||
throw new Exception(Exception::DATABASE_NOT_FOUND);
|
||||
}
|
||||
|
||||
$collectionKey = 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence();
|
||||
$databaseKey = 'database_' . $database->getSequence();
|
||||
|
||||
$collectionAttributes = [];
|
||||
$attributeDocuments = [];
|
||||
foreach ($attributes as $attributeDef) {
|
||||
$attrDoc = $this->buildAttributeDocument($database, $collection, $attributeDef, $dbForProject);
|
||||
$collectionAttributes[] = $attrDoc['collection'];
|
||||
$attributeDocuments[] = $attrDoc['document'];
|
||||
try {
|
||||
foreach ($attributes as $attributeDef) {
|
||||
$attrDoc = $this->buildAttributeDocument($database, $collection, $attributeDef, $dbForProject);
|
||||
$collectionAttributes[] = $attrDoc['collection'];
|
||||
$attributeDocuments[] = $attrDoc['document'];
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$dbForProject->deleteDocument($databaseKey, $collection->getId());
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$collectionIndexes = [];
|
||||
$indexDocuments = [];
|
||||
foreach ($indexes as $indexDef) {
|
||||
$idxDoc = $this->buildIndexDocument($database, $collection, $indexDef, $collectionAttributes);
|
||||
$collectionIndexes[] = $idxDoc['collection'];
|
||||
$indexDocuments[] = $idxDoc['document'];
|
||||
try {
|
||||
foreach ($indexes as $indexDef) {
|
||||
$idxDoc = $this->buildIndexDocument($database, $collection, $indexDef, $collectionAttributes);
|
||||
$collectionIndexes[] = $idxDoc['collection'];
|
||||
$indexDocuments[] = $idxDoc['document'];
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$dbForProject->deleteDocument($databaseKey, $collection->getId());
|
||||
throw $e;
|
||||
}
|
||||
|
||||
try {
|
||||
$dbForProject->createCollection(
|
||||
id: 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(),
|
||||
id: $collectionKey,
|
||||
attributes: $collectionAttributes,
|
||||
indexes: $collectionIndexes,
|
||||
permissions: $permissions,
|
||||
documentSecurity: $documentSecurity
|
||||
);
|
||||
} catch (DuplicateException) {
|
||||
$dbForProject->deleteDocument($databaseKey, $collection->getId());
|
||||
throw new Exception($this->getDuplicateException());
|
||||
} catch (IndexException $e) {
|
||||
$dbForProject->deleteDocument($databaseKey, $collection->getId());
|
||||
throw new Exception($this->getInvalidIndexException(), $e->getMessage());
|
||||
} catch (LimitException) {
|
||||
$dbForProject->deleteDocument($databaseKey, $collection->getId());
|
||||
throw new Exception($this->getLimitException());
|
||||
} catch (\Throwable $e) {
|
||||
$dbForProject->deleteDocument($databaseKey, $collection->getId());
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// Create documents in attributes and indexes collections
|
||||
@@ -160,7 +179,11 @@ class Create extends Action
|
||||
$dbForProject->createDocuments('indexes', $indexDocuments);
|
||||
}
|
||||
} catch (DuplicateException) {
|
||||
$this->cleanup($dbForProject, $databaseKey, $collectionKey, $collection->getId());
|
||||
throw new Exception($this->getDuplicateException());
|
||||
} catch (\Throwable $e) {
|
||||
$this->cleanup($dbForProject, $databaseKey, $collectionKey, $collection->getId());
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$dbForProject->purgeCachedDocument('database_' . $database->getSequence(), $collection->getId());
|
||||
@@ -362,4 +385,22 @@ class Create extends Action
|
||||
'document' => $document,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup on failure: delete the collection document and the underlying DB collection
|
||||
*/
|
||||
protected function cleanup(Database $dbForProject, string $databaseKey, string $collectionKey, string $collectionId): void
|
||||
{
|
||||
try {
|
||||
$dbForProject->deleteCollection($collectionKey);
|
||||
} catch (\Throwable) {
|
||||
// Ignore cleanup errors for collection deletion
|
||||
}
|
||||
|
||||
try {
|
||||
$dbForProject->deleteDocument($databaseKey, $collectionId);
|
||||
} catch (\Throwable) {
|
||||
// Ignore cleanup errors for document deletion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7110,6 +7110,76 @@ class DatabasesCustomServerTest extends Scope
|
||||
]));
|
||||
}
|
||||
|
||||
public function testCreateCollectionCleanupOnFailure(): void
|
||||
{
|
||||
// Create database
|
||||
$database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'Test Cleanup',
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $database['headers']['status-code']);
|
||||
$databaseId = $database['body']['$id'];
|
||||
|
||||
$collectionId = ID::unique();
|
||||
|
||||
// Test: Create collection with invalid index referencing non-existent attribute (should fail)
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => $collectionId,
|
||||
'name' => 'Should Fail',
|
||||
'attributes' => [
|
||||
[
|
||||
'key' => 'title',
|
||||
'type' => Database::VAR_STRING,
|
||||
'size' => 256,
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'key' => 'idx_invalid',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['nonexistent'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $collection['headers']['status-code']);
|
||||
|
||||
// Verify collection was cleaned up - creating with same ID should succeed
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => $collectionId,
|
||||
'name' => 'Should Succeed',
|
||||
'attributes' => [
|
||||
[
|
||||
'key' => 'title',
|
||||
'type' => Database::VAR_STRING,
|
||||
'size' => 256,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $collection['headers']['status-code']);
|
||||
|
||||
// Cleanup
|
||||
$this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
}
|
||||
|
||||
public function testCreateCollectionWithEnumAttribute(): void
|
||||
{
|
||||
// Create database
|
||||
|
||||
Reference in New Issue
Block a user