diff --git a/app/config/errors.php b/app/config/errors.php index cba8dc8acc..5a55d35871 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -484,6 +484,11 @@ return [ 'description' => 'Project with the requested ID could not be found. Please check the value of the X-Appwrite-Project header to ensure the correct project ID is being used.', 'code' => 404, ], + Exception::PROJECT_ALREADY_EXISTS => [ + 'name' => Exception::PROJECT_ALREADY_EXISTS, + 'description' => 'Project with the requested ID already exists.', + 'code' => 409, + ], Exception::PROJECT_UNKNOWN => [ 'name' => Exception::PROJECT_UNKNOWN, 'description' => 'The project ID is either missing or not valid. Please check the value of the X-Appwrite-Project header to ensure the correct project ID is being used.', diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index c7a85f0489..dec5520d85 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -29,6 +29,7 @@ use Appwrite\Extend\Exception; use Appwrite\Utopia\Database\Validator\Queries\Projects; use Utopia\Cache\Cache; use Utopia\Pools\Group; +use Utopia\Database\Exception\Duplicate; use Utopia\Validator\ArrayList; use Utopia\Validator\Boolean; use Utopia\Validator\Hostname; @@ -96,39 +97,43 @@ App::post('/v1/projects') throw new Exception(Exception::PROJECT_RESERVED_PROJECT, "'console' is a reserved project."); } - $project = $dbForConsole->createDocument('projects', new Document([ - '$id' => $projectId, - '$permissions' => [ - Permission::read(Role::team(ID::custom($teamId))), - Permission::update(Role::team(ID::custom($teamId), 'owner')), - Permission::update(Role::team(ID::custom($teamId), 'developer')), - Permission::delete(Role::team(ID::custom($teamId), 'owner')), - Permission::delete(Role::team(ID::custom($teamId), 'developer')), - ], - 'name' => $name, - 'teamInternalId' => $team->getInternalId(), - 'teamId' => $team->getId(), - 'region' => $region, - 'description' => $description, - 'logo' => $logo, - 'url' => $url, - 'version' => APP_VERSION_STABLE, - 'legalName' => $legalName, - 'legalCountry' => $legalCountry, - 'legalState' => $legalState, - 'legalCity' => $legalCity, - 'legalAddress' => $legalAddress, - 'legalTaxId' => ID::custom($legalTaxId), - 'services' => new stdClass(), - 'platforms' => null, - 'authProviders' => [], - 'webhooks' => null, - 'keys' => null, - 'domains' => null, - 'auths' => $auths, - 'search' => implode(' ', [$projectId, $name]), - 'database' => $database, - ])); + try { + $project = $dbForConsole->createDocument('projects', new Document([ + '$id' => $projectId, + '$permissions' => [ + Permission::read(Role::team(ID::custom($teamId))), + Permission::update(Role::team(ID::custom($teamId), 'owner')), + Permission::update(Role::team(ID::custom($teamId), 'developer')), + Permission::delete(Role::team(ID::custom($teamId), 'owner')), + Permission::delete(Role::team(ID::custom($teamId), 'developer')), + ], + 'name' => $name, + 'teamInternalId' => $team->getInternalId(), + 'teamId' => $team->getId(), + 'region' => $region, + 'description' => $description, + 'logo' => $logo, + 'url' => $url, + 'version' => APP_VERSION_STABLE, + 'legalName' => $legalName, + 'legalCountry' => $legalCountry, + 'legalState' => $legalState, + 'legalCity' => $legalCity, + 'legalAddress' => $legalAddress, + 'legalTaxId' => ID::custom($legalTaxId), + 'services' => new stdClass(), + 'platforms' => null, + 'authProviders' => [], + 'webhooks' => null, + 'keys' => null, + 'domains' => null, + 'auths' => $auths, + 'search' => implode(' ', [$projectId, $name]), + 'database' => $database + ])); + } catch (Duplicate $th) { + throw new Exception(Exception::PROJECT_ALREADY_EXISTS); + } $dbForProject = new Database($pools->get($database)->pop()->getResource(), $cache); $dbForProject->setNamespace("_{$project->getInternalId()}"); diff --git a/phpunit.xml b/phpunit.xml index 927e91567a..4012c8c276 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -7,7 +7,7 @@ convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" - > +> diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index ecf6e17c1f..71bb548823 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -156,6 +156,7 @@ class Exception extends \Exception public const PROJECT_UNKNOWN = 'project_unknown'; public const PROJECT_PROVIDER_DISABLED = 'project_provider_disabled'; public const PROJECT_PROVIDER_UNSUPPORTED = 'project_provider_unsupported'; + public const PROJECT_ALREADY_EXISTS = 'project_already_exists'; public const PROJECT_INVALID_SUCCESS_URL = 'project_invalid_success_url'; public const PROJECT_INVALID_FAILURE_URL = 'project_invalid_failure_url'; public const PROJECT_RESERVED_PROJECT = 'project_reserved_project'; diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index 789020bf42..7254f88efe 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -3,6 +3,7 @@ namespace Tests\E2E\Services\Projects; use Appwrite\Auth\Auth; +use Appwrite\Extend\Exception; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\ProjectConsole; use Tests\E2E\Scopes\SideClient; @@ -98,7 +99,37 @@ class ProjectsConsoleClientTest extends Scope $this->assertEquals(400, $response['headers']['status-code']); - return ['projectId' => $projectId]; + return [ + 'projectId' => $projectId, + 'teamId' => $team['body']['$id'] + ]; + } + + /** + * @depends testCreateProject + */ + public function testCreateDuplicateProject($data) + { + $teamId = $data['teamId'] ?? ''; + $projectId = $data['projectId'] ?? ''; + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_POST, '/projects', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'projectId' => $projectId, + 'name' => 'Project Duplicate', + 'teamId' => $teamId, + 'region' => 'default' + ]); + + $this->assertEquals(409, $response['headers']['status-code']); + $this->assertEquals(409, $response['body']['code']); + $this->assertEquals(Exception::PROJECT_ALREADY_EXISTS, $response['body']['type']); + $this->assertEquals('Project with the requested ID already exists.', $response['body']['message']); } /**