diff --git a/src/Migration/Destinations/Appwrite.php b/src/Migration/Destinations/Appwrite.php index 41023b1a..d8b1fef9 100644 --- a/src/Migration/Destinations/Appwrite.php +++ b/src/Migration/Destinations/Appwrite.php @@ -481,12 +481,14 @@ protected function createDatabase(Database $resource): bool $validator = new UID(); if (!$validator->isValid($resource->getId())) { - throw new Exception( + $resource->setStatus(Resource::STATUS_ERROR, $validator->getDescription()); + $this->addError(new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), message: $validator->getDescription(), - ); + )); + return false; } $createdAt = $this->normalizeDateTime($resource->getCreatedAt()); @@ -579,12 +581,14 @@ protected function createEntity(Table $resource): bool $validator = new UID(); if (!$validator->isValid($resource->getId())) { - throw new Exception( + $resource->setStatus(Resource::STATUS_ERROR, $validator->getDescription()); + $this->addError(new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), message: $validator->getDescription(), - ); + )); + return false; } $database = $this->dbForProject->getDocument( @@ -593,12 +597,14 @@ protected function createEntity(Table $resource): bool ); if ($database->isEmpty()) { - throw new Exception( + $resource->setStatus(Resource::STATUS_ERROR, 'Database not found'); + $this->addError(new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), message: 'Database not found', - ); + )); + return false; } $createdAt = $this->normalizeDateTime($resource->getCreatedAt()); @@ -724,12 +730,14 @@ protected function createField(Column|Attribute $resource): bool ); if ($database->isEmpty()) { - throw new Exception( + $resource->setStatus(Resource::STATUS_ERROR, 'Database not found'); + $this->addError(new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), message: 'Database not found', - ); + )); + return false; } $table = $this->dbForProject->getDocument( @@ -738,41 +746,49 @@ protected function createField(Column|Attribute $resource): bool ); if ($table->isEmpty()) { - throw new Exception( + $resource->setStatus(Resource::STATUS_ERROR, 'Table not found'); + $this->addError(new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), message: 'Table not found', - ); + )); + return false; } if (!empty($resource->getFormat())) { if (!Structure::hasFormat($resource->getFormat(), $type)) { - throw new Exception( + $resource->setStatus(Resource::STATUS_ERROR, "Format {$resource->getFormat()} not available for column type {$type}"); + $this->addError(new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), message: "Format {$resource->getFormat()} not available for column type {$type}", - ); + )); + return false; } } if ($resource->isRequired() && $resource->getDefault() !== null) { - throw new Exception( + $resource->setStatus(Resource::STATUS_ERROR, 'Cannot set default value for required column'); + $this->addError(new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), message: 'Cannot set default value for required column', - ); + )); + return false; } if ($resource->isArray() && $resource->getDefault() !== null) { - throw new Exception( + $resource->setStatus(Resource::STATUS_ERROR, 'Cannot set default value for array column'); + $this->addError(new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), message: 'Cannot set default value for array column', - ); + )); + return false; } if ($type === UtopiaDatabase::VAR_RELATIONSHIP) { @@ -782,12 +798,14 @@ protected function createField(Column|Attribute $resource): bool $resource->getOptions()['relatedCollection'] ); if ($relatedTable->isEmpty()) { - throw new Exception( + $resource->setStatus(Resource::STATUS_ERROR, 'Related table not found'); + $this->addError(new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), message: 'Related table not found', - ); + )); + return false; } } @@ -872,20 +890,26 @@ protected function createField(Column|Attribute $resource): bool $this->dbForProject->checkAttribute($table, $column); $column = $this->dbForProject->createDocument(self::META_ATTRIBUTES, $column); - } catch (DuplicateException) { - throw new Exception( + } catch (DuplicateException $e) { + $resource->setStatus(Resource::STATUS_ERROR, 'Attribute already exists'); + $this->addError(new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), message: 'Attribute already exists', - ); - } catch (LimitException) { - throw new Exception( + previous: $e, + )); + return false; + } catch (LimitException $e) { + $resource->setStatus(Resource::STATUS_ERROR, 'Attribute limit exceeded'); + $this->addError(new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), message: 'Attribute limit exceeded', - ); + previous: $e, + )); + return false; } catch (\Throwable $e) { $this->purgeTableCaches($database, $table, $dbForDatabases); throw $e; @@ -927,24 +951,30 @@ protected function createField(Column|Attribute $resource): bool $this->dbForProject->createDocument(self::META_ATTRIBUTES, $twoWayAttribute); $this->trackOrphanCandidate($database, $relatedTable, 'attributeKeys', $twoWayKey, $dbForDatabases); - } catch (DuplicateException) { + } catch (DuplicateException $e) { $this->dbForProject->deleteDocument(self::META_ATTRIBUTES, $column->getId()); - throw new Exception( + $resource->setStatus(Resource::STATUS_ERROR, 'Attribute already exists'); + $this->addError(new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), message: 'Attribute already exists', - ); - } catch (LimitException) { + previous: $e, + )); + return false; + } catch (LimitException $e) { $this->dbForProject->deleteDocument(self::META_ATTRIBUTES, $column->getId()); - throw new Exception( + $resource->setStatus(Resource::STATUS_ERROR, 'Attribute limit exceeded'); + $this->addError(new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), message: 'Attribute limit exceeded', - ); + previous: $e, + )); + return false; } catch (\Throwable $e) { $this->purgeTableCaches($database, $relatedTable, $dbForDatabases); throw $e; @@ -989,19 +1019,14 @@ protected function createField(Column|Attribute $resource): bool throw new \Exception('Failed to create Column', Exception::CODE_INTERNAL); } } - } catch (\Throwable) { + } catch (\Throwable $e) { $this->dbForProject->deleteDocument(self::META_ATTRIBUTES, $column->getId()); if (isset($twoWayAttribute)) { $this->dbForProject->deleteDocument(self::META_ATTRIBUTES, $twoWayAttribute->getId()); } - throw new Exception( - resourceName: $resource->getName(), - resourceGroup: $resource->getGroup(), - resourceId: $resource->getId(), - message: 'Failed to create column', - ); + throw $e; } if ($type === UtopiaDatabase::VAR_RELATIONSHIP && $options['twoWay']) { @@ -1029,12 +1054,14 @@ protected function createIndex(Index $resource): bool $resource->getTable()->getDatabase()->getId(), ); if ($database->isEmpty()) { - throw new Exception( + $resource->setStatus(Resource::STATUS_ERROR, 'Database not found'); + $this->addError(new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), message: 'Database not found', - ); + )); + return false; } $table = $this->dbForProject->getDocument( @@ -1042,12 +1069,14 @@ protected function createIndex(Index $resource): bool $resource->getTable()->getId(), ); if ($table->isEmpty()) { - throw new Exception( + $resource->setStatus(Resource::STATUS_ERROR, 'Table not found'); + $this->addError(new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), message: 'Table not found', - ); + )); + return false; } $dbForDatabases = ($this->getDatabasesDB)($database); @@ -1101,12 +1130,14 @@ protected function createIndex(Index $resource): bool ], $dbForDatabases->getLimitForIndexes()); if ($count >= $dbForDatabases->getLimitForIndexes()) { - throw new Exception( + $resource->setStatus(Resource::STATUS_ERROR, 'Index limit reached for table'); + $this->addError(new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), message: 'Index limit reached for table', - ); + )); + return false; } // Lengths hidden by default @@ -1159,12 +1190,14 @@ protected function createIndex(Index $resource): bool ); if (!$validator->isValid($index)) { - throw new Exception( + $resource->setStatus(Resource::STATUS_ERROR, 'Invalid index: ' . $validator->getDescription()); + $this->addError(new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), message: 'Invalid index: ' . $validator->getDescription(), - ); + )); + return false; } $index = $this->dbForProject->createDocument(self::META_INDEXES, $index); @@ -1190,12 +1223,7 @@ protected function createIndex(Index $resource): bool } catch (\Throwable $th) { $this->dbForProject->deleteDocument(self::META_INDEXES, $index->getId()); - throw new Exception( - resourceName: $resource->getName(), - resourceGroup: $resource->getGroup(), - resourceId: $resource->getId(), - message: 'Failed to create index', - ); + throw $th; } $this->dbForProject->purgeCachedDocument($this->databaseCollectionId($database), $table->getId()); @@ -1218,12 +1246,14 @@ protected function createRecord(Row $resource, bool $isLast): bool $validator = new UID(); if (!$validator->isValid($resource->getId())) { - throw new Exception( + $resource->setStatus(Resource::STATUS_ERROR, $validator->getDescription()); + $this->addError(new Exception( resourceName: $resource->getName(), resourceGroup: $resource->getGroup(), resourceId: $resource->getId(), message: $validator->getDescription(), - ); + )); + return false; } // Check if document has already been created @@ -1318,19 +1348,41 @@ protected function createRecord(Row $resource, bool $isLast): bool } $collectionId = $this->tableCollectionId($database, $table); - match ($this->onDuplicate) { - OnDuplicate::Overwrite => $dbForDatabases->skipRelationshipsExistCheck( - fn () => $dbForDatabases->upsertDocuments($collectionId, $this->rowBuffer) - ), - OnDuplicate::Skip => $dbForDatabases->skipDuplicates( - fn () => $dbForDatabases->skipRelationshipsExistCheck( + try { + match ($this->onDuplicate) { + OnDuplicate::Overwrite => $dbForDatabases->skipRelationshipsExistCheck( + fn () => $dbForDatabases->upsertDocuments($collectionId, $this->rowBuffer) + ), + OnDuplicate::Skip => $dbForDatabases->skipDuplicates( + fn () => $dbForDatabases->skipRelationshipsExistCheck( + fn () => $dbForDatabases->createDocuments($collectionId, $this->rowBuffer) + ) + ), + OnDuplicate::Fail => $dbForDatabases->skipRelationshipsExistCheck( fn () => $dbForDatabases->createDocuments($collectionId, $this->rowBuffer) - ) - ), - OnDuplicate::Fail => $dbForDatabases->skipRelationshipsExistCheck( - fn () => $dbForDatabases->createDocuments($collectionId, $this->rowBuffer) - ), - }; + ), + }; + } catch (DuplicateException $e) { + $resource->setStatus(Resource::STATUS_ERROR, 'Document already exists'); + $this->addError(new Exception( + resourceName: $resource->getName(), + resourceGroup: $resource->getGroup(), + resourceId: $resource->getId(), + message: 'Document already exists', + previous: $e, + )); + return false; + } catch (StructureException $e) { + $resource->setStatus(Resource::STATUS_ERROR, $e->getMessage()); + $this->addError(new Exception( + resourceName: $resource->getName(), + resourceGroup: $resource->getGroup(), + resourceId: $resource->getId(), + message: $e->getMessage(), + previous: $e, + )); + return false; + } } finally { $this->rowBuffer = []; }