diff --git a/app/config/errors.php b/app/config/errors.php index fa112bcb6f..08cc497b3f 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -1236,6 +1236,26 @@ return [ 'description' => 'The specified database type is not supported for CSV import or export operations.', 'code' => 400, ], + Exception::MIGRATION_SOURCE_PROJECT_ID_REQUIRED => [ + 'name' => Exception::MIGRATION_SOURCE_PROJECT_ID_REQUIRED, + 'description' => 'A source projectId is required for Appwrite migrations. Provide it in the migration credentials.', + 'code' => 400, + ], + Exception::MIGRATION_SOURCE_PROJECT_NOT_FOUND => [ + 'name' => Exception::MIGRATION_SOURCE_PROJECT_NOT_FOUND, + 'description' => 'The source project for the provided projectId was not found. Verify the projectId and the API key has access to it.', + 'code' => 404, + ], + Exception::MIGRATION_SOURCE_TYPE_INVALID => [ + 'name' => Exception::MIGRATION_SOURCE_TYPE_INVALID, + 'description' => 'The migration source type is invalid. Use one of the supported source types.', + 'code' => 400, + ], + Exception::MIGRATION_DESTINATION_TYPE_INVALID => [ + 'name' => Exception::MIGRATION_DESTINATION_TYPE_INVALID, + 'description' => 'The migration destination type is invalid. Use one of the supported destination types.', + 'code' => 400, + ], /** Realtime */ Exception::REALTIME_MESSAGE_FORMAT_INVALID => [ diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 6fc3e88635..54ee4069d1 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -346,6 +346,10 @@ class Exception extends \Exception public const string MIGRATION_IN_PROGRESS = 'migration_in_progress'; public const string MIGRATION_PROVIDER_ERROR = 'migration_provider_error'; public const string MIGRATION_DATABASE_TYPE_UNSUPPORTED = 'migration_database_type_unsupported'; + public const string MIGRATION_SOURCE_PROJECT_ID_REQUIRED = 'migration_source_project_id_required'; + public const string MIGRATION_SOURCE_PROJECT_NOT_FOUND = 'migration_source_project_not_found'; + public const string MIGRATION_SOURCE_TYPE_INVALID = 'migration_source_type_invalid'; + public const string MIGRATION_DESTINATION_TYPE_INVALID = 'migration_destination_type_invalid'; /** Realtime */ public const string REALTIME_MESSAGE_FORMAT_INVALID = 'realtime_message_format_invalid'; diff --git a/src/Appwrite/Platform/Workers/Migrations.php b/src/Appwrite/Platform/Workers/Migrations.php index f5cc57a6c2..f37571d2b0 100644 --- a/src/Appwrite/Platform/Workers/Migrations.php +++ b/src/Appwrite/Platform/Workers/Migrations.php @@ -196,13 +196,13 @@ class Migrations extends Action $projectDB = null; $useAppwriteApiSource = false; if ($source === SourceAppwrite::getName() && empty($credentials['projectId'])) { - throw new MigrationException('', '', message: 'Source projectId is required for Appwrite migrations', code: MigrationException::CODE_VALIDATION); + throw new Exception(Exception::MIGRATION_SOURCE_PROJECT_ID_REQUIRED); } if (! empty($credentials['projectId'])) { $this->sourceProject = $this->dbForPlatform->getDocument('projects', $credentials['projectId']); if ($this->sourceProject->isEmpty()) { - throw new MigrationException('', '', message: 'Source project not found for provided projectId', code: MigrationException::CODE_NOT_FOUND); + throw new Exception(Exception::MIGRATION_SOURCE_PROJECT_NOT_FOUND); } $sourceRegion = $this->sourceProject->getAttribute('region', 'default'); @@ -265,7 +265,7 @@ class Migrations extends Action $this->deviceForMigrations, $this->dbForProject, ), - default => throw new MigrationException('', '', message: 'Invalid source type', code: MigrationException::CODE_VALIDATION), + default => throw new Exception(Exception::MIGRATION_SOURCE_TYPE_INVALID), }; $resources = $migration->getAttribute('resources', []); @@ -310,7 +310,7 @@ class Migrations extends Action $options['filename'], $options['columns'] ?? [], ), - default => throw new MigrationException('', '', message: 'Invalid destination type', code: MigrationException::CODE_VALIDATION), + default => throw new Exception(Exception::MIGRATION_DESTINATION_TYPE_INVALID), }; } @@ -538,8 +538,18 @@ class Migrations extends Action $caughtError = $th; - // MigrationException is reserved for user-facing failures and stays in the migration report only. - if (!$th instanceof MigrationException) { + // Mirror general.php's HTTP-error pattern: typed AppwriteException uses its + // registry-driven isPublishable() flag; library-thrown Migration\Exception is + // always user-facing; anything else falls back to the code heuristic. + if ($th instanceof Exception) { + $publish = $th->isPublishable(); + } elseif ($th instanceof MigrationException) { + $publish = false; + } else { + $publish = $th->getCode() === 0 || $th->getCode() >= 500; + } + + if ($publish) { call_user_func($this->logError, $th, 'appwrite-worker', 'appwrite-queue-' . self::getName(), [ 'migrationId' => $migration->getId(), 'source' => $migration->getAttribute('source') ?? '',