diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 569cea66d0..41e8777edb 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -76,7 +76,7 @@ Http::patch('/v1/projects/:projectId/service/all') ->groups(['api', 'projects']) ->label('scope', 'projects.write') ->action(function () { - throw new Exception(Exception::GENERAL_API_DISABLED, 'Bulk API no longer exists for services. Please change status individually.'); + throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED); }); Http::patch('/v1/projects/:projectId/api/all') @@ -84,7 +84,7 @@ Http::patch('/v1/projects/:projectId/api/all') ->groups(['api', 'projects']) ->label('scope', 'projects.write') ->action(function () { - throw new Exception(Exception::GENERAL_API_DISABLED, 'Bulk API no longer exists for services. Please change status individually.'); + throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED); }); Http::patch('/v1/projects/:projectId/oauth2') diff --git a/app/realtime.php b/app/realtime.php index 489e26e393..c93f7e4e6a 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -661,6 +661,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $logUser = $user; $apis = $project->getAttribute('apis', []); + // Websocket is what to check, but realtime is checked too for backwards compatibility $websocketEnabled = $apis['websocket'] ?? $apis['realtime'] ?? true; if ( !$websocketEnabled diff --git a/tests/e2e/Services/Projects/ProjectsBase.php b/tests/e2e/Services/Projects/ProjectsBase.php index d42c5feda3..6122e95c75 100644 --- a/tests/e2e/Services/Projects/ProjectsBase.php +++ b/tests/e2e/Services/Projects/ProjectsBase.php @@ -388,6 +388,7 @@ trait ProjectsBase $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/service', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-response-format' => '1.9.0', 'cookie' => 'a_session_console=' . $this->getRoot()['session'], ]), [ 'service' => $key, diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index e0f94b64cc..096d90d8d2 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -2497,49 +2497,222 @@ class ProjectsConsoleClientTest extends Scope $id = $project['body']['$id']; + // Bulk disable should no longer work $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/service/all', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-response-format' => '1.9.0', 'cookie' => 'a_session_console=' . $this->getRoot()['session'], ]), [ 'status' => false, ]); - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertNotEmpty($response['body']['$id']); - - $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'cookie' => 'a_session_console=' . $this->getRoot()['session'], - ])); - - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertNotEmpty($response['body']['$id']); - - $matches = []; - $pattern = '/serviceStatusFor.*/'; - - foreach ($response['body'] as $key => $value) { - if (\preg_match($pattern, $key)) { - $matches[$key] = $value; - } - } - - foreach ($matches as $value) { - $this->assertFalse($value); - } + $this->assertEquals(405, $response['headers']['status-code']); + $this->assertEquals('general_not_implemented', $response['body']['type']); + // Bulk enable should no longer work $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/service/all', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-response-format' => '1.9.0', 'cookie' => 'a_session_console=' . $this->getRoot()['session'], ]), [ 'status' => true, ]); + $this->assertEquals(405, $response['headers']['status-code']); + $this->assertEquals('general_not_implemented', $response['body']['type']); + } + + public function testUpdateProjectApisAll(): void + { + $team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ]), [ + 'teamId' => ID::unique(), + 'name' => 'Project Test', + ]); + + $this->assertEquals(201, $team['headers']['status-code']); + $this->assertNotEmpty($team['body']['$id']); + + $project = $this->client->call(Client::METHOD_POST, '/projects', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ]), [ + 'projectId' => ID::unique(), + 'name' => 'Project Test', + 'teamId' => $team['body']['$id'], + 'region' => System::getEnv('_APP_REGION', 'default') + ]); + + $this->assertEquals(201, $project['headers']['status-code']); + $this->assertNotEmpty($project['body']['$id']); + + $id = $project['body']['$id']; + + // Bulk disable should no longer work + $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/api/all', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-response-format' => '1.9.0', + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ]), [ + 'status' => false, + ]); + + $this->assertEquals(405, $response['headers']['status-code']); + $this->assertEquals('general_not_implemented', $response['body']['type']); + + // Bulk enable should no longer work + $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/api/all', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-response-format' => '1.9.0', + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ]), [ + 'status' => true, + ]); + + $this->assertEquals(405, $response['headers']['status-code']); + $this->assertEquals('general_not_implemented', $response['body']['type']); + } + + public function testUpdateProjectApiStatus(): void + { + $team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ]), [ + 'teamId' => ID::unique(), + 'name' => 'Project Test', + ]); + + $this->assertEquals(201, $team['headers']['status-code']); + $this->assertNotEmpty($team['body']['$id']); + + $project = $this->client->call(Client::METHOD_POST, '/projects', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ]), [ + 'projectId' => ID::unique(), + 'name' => 'Project Test', + 'teamId' => $team['body']['$id'], + 'region' => System::getEnv('_APP_REGION', 'default') + ]); + + $this->assertEquals(201, $project['headers']['status-code']); + $this->assertNotEmpty($project['body']['$id']); + + $id = $project['body']['$id']; + $protocols = ['rest', 'graphql', 'websocket']; + + /** + * Test for Disabled using old format (api + status) + */ + foreach ($protocols as $key) { + + $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/api', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-response-format' => '1.9.0', + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ]), [ + 'api' => $key, + 'status' => false, + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']['$id']); + + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ])); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertEquals(false, $response['body']['protocolStatusFor' . ucfirst($key)]); + } + + /** + * Test for Enabled using old format (api + status) + */ + foreach ($protocols as $key) { + + $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/api', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-response-format' => '1.9.0', + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ]), [ + 'api' => $key, + 'status' => true, + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']['$id']); + + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ])); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertEquals(true, $response['body']['protocolStatusFor' . ucfirst($key)]); + } + } + + public function testUpdateProjectApiStatusRealtimeBackwardsCompat(): void + { + $team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ]), [ + 'teamId' => ID::unique(), + 'name' => 'Project Test', + ]); + + $this->assertEquals(201, $team['headers']['status-code']); + + $project = $this->client->call(Client::METHOD_POST, '/projects', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ]), [ + 'projectId' => ID::unique(), + 'name' => 'Project Test', + 'teamId' => $team['body']['$id'], + 'region' => System::getEnv('_APP_REGION', 'default') + ]); + + $this->assertEquals(201, $project['headers']['status-code']); + + $id = $project['body']['$id']; + + /** + * Test that "realtime" gets renamed to "websocket" via request filter + */ + $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/api', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-response-format' => '1.9.0', + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ]), [ + 'api' => 'realtime', + 'status' => false, + ]); + $this->assertEquals(200, $response['headers']['status-code']); - $this->assertNotEmpty($response['body']['$id']); $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id, array_merge([ 'content-type' => 'application/json', @@ -2548,17 +2721,29 @@ class ProjectsConsoleClientTest extends Scope ])); $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(false, $response['body']['protocolStatusForWebsocket']); - $matches = []; - foreach ($response['body'] as $key => $value) { - if (\preg_match($pattern, $key)) { - $matches[$key] = $value; - } - } + // Re-enable via old "realtime" name + $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/api', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-response-format' => '1.9.0', + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ]), [ + 'api' => 'realtime', + 'status' => true, + ]); - foreach ($matches as $value) { - $this->assertTrue($value); - } + $this->assertEquals(200, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ])); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(true, $response['body']['protocolStatusForWebsocket']); } public function testUpdateProjectServiceStatusAdmin(): array @@ -2604,6 +2789,7 @@ class ProjectsConsoleClientTest extends Scope $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/service', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-response-format' => '1.9.0', 'cookie' => 'a_session_console=' . $this->getRoot()['session'], ]), [ 'service' => $key, @@ -2649,6 +2835,7 @@ class ProjectsConsoleClientTest extends Scope $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/service/', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-response-format' => '1.9.0', ], $this->getHeaders()), [ 'service' => $key, 'status' => true, @@ -2678,6 +2865,7 @@ class ProjectsConsoleClientTest extends Scope $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/service', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-response-format' => '1.9.0', 'cookie' => 'a_session_console=' . $this->getRoot()['session'], ]), [ 'service' => $key, @@ -2725,6 +2913,7 @@ class ProjectsConsoleClientTest extends Scope $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/service/', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-response-format' => '1.9.0', ], $this->getHeaders()), [ 'service' => $service, 'status' => true, @@ -2752,6 +2941,7 @@ class ProjectsConsoleClientTest extends Scope $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/service', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-response-format' => '1.9.0', 'cookie' => 'a_session_console=' . $this->getRoot()['session'], ]), [ 'service' => $key, @@ -2841,6 +3031,7 @@ class ProjectsConsoleClientTest extends Scope $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/service/', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-response-format' => '1.9.0', ], $this->getHeaders()), [ 'service' => $service, 'status' => true,