From d94962f0861f3958c9907cdc191017aa31ab67cb Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 29 Jan 2024 17:20:45 +1300 Subject: [PATCH 1/3] Add message delete route --- app/controllers/api/messaging.php | 48 ++++++++ .../e2e/Services/Messaging/MessagingBase.php | 116 +++++++++++++----- 2 files changed, 132 insertions(+), 32 deletions(-) diff --git a/app/controllers/api/messaging.php b/app/controllers/api/messaging.php index a6c2f923eb..b2970f3d0a 100644 --- a/app/controllers/api/messaging.php +++ b/app/controllers/api/messaging.php @@ -3266,3 +3266,51 @@ App::patch('/v1/messaging/messages/push/:messageId') $response ->dynamic($message, Response::MODEL_MESSAGE); }); + +App::delete('/v1/messaging/messages/:messageId') + ->desc('Delete a message') + ->groups(['api', 'messaging']) + ->label('audits.event', 'message.delete') + ->label('audits.resource', 'message/{request.route.messageId}') + ->label('event', 'messages.[messageId].delete') + ->label('scope', 'messages.write') + ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY]) + ->label('sdk.namespace', 'messaging') + ->label('sdk.method', 'delete') + ->label('sdk.description', '/docs/references/messaging/delete-message.md') + ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_NONE) + ->param('messageId', '', new UID(), 'Message ID.') + ->inject('dbForProject') + ->inject('dbForConsole') + ->inject('response') + ->action(function (string $messageId, Database $dbForProject, Database $dbForConsole, Response $response) { + $message = $dbForProject->getDocument('messages', $messageId); + + if ($message->isEmpty()) { + throw new Exception(Exception::MESSAGE_NOT_FOUND); + } + + switch ($message->getAttribute('status')) { + case MessageStatus::PROCESSING: + throw new Exception(Exception::MESSAGE_ALREADY_SCHEDULED); + case MessageStatus::SCHEDULED: + $scheduleId = $message->getAttribute('scheduleId'); + + if (!empty($scheduleId)) { + try { + $dbForConsole->deleteDocument('schedules', $scheduleId); + } catch (Exception) { + // Ignore + } + } + break; + default: + break; + } + + $dbForProject->deleteDocument('messages', $message->getId()); + + $response->noContent(); + }); diff --git a/tests/e2e/Services/Messaging/MessagingBase.php b/tests/e2e/Services/Messaging/MessagingBase.php index 0e7fca8507..715f5c57eb 100644 --- a/tests/e2e/Services/Messaging/MessagingBase.php +++ b/tests/e2e/Services/Messaging/MessagingBase.php @@ -643,6 +643,60 @@ trait MessagingBase $this->assertEquals(204, $response['headers']['status-code']); } + /** + * @depends testCreateDraftEmail + */ + public function testListTargets(array $message) + { + $response = $this->client->call(Client::METHOD_GET, '/messaging/messages/does_not_exist/targets', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + + $this->assertEquals(404, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $message['$id'] . '/targets', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + $targetList = $response['body']; + $this->assertEquals(1, $targetList['total']); + $this->assertEquals(1, count($targetList['targets'])); + $this->assertEquals($message['targets'][0], $targetList['targets'][0]['$id']); + + // Test for empty targets + $response = $this->client->call(Client::METHOD_POST, '/messaging/messages/email', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], [ + 'messageId' => ID::unique(), + 'subject' => 'New blog post', + 'content' => 'Check out the new blog post at http://localhost', + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + $message = $response['body']; + + $response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $message['$id'] . '/targets', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + $targetList = $response['body']; + $this->assertEquals(0, $targetList['total']); + $this->assertEquals(0, count($targetList['targets'])); + } + public function testCreateDraftEmail() { // Create User @@ -746,7 +800,6 @@ trait MessagingBase // Get target $target = $user['body']['targets'][0]; - // Create Subscriber $subscriber = $this->client->call(Client::METHOD_POST, '/messaging/topics/' . $topic['body']['$id'] . '/subscribers', \array_merge([ 'content-type' => 'application/json', @@ -785,14 +838,19 @@ trait MessagingBase $this->assertEquals(1, $message['body']['deliveredTotal']); $this->assertEquals(0, \count($message['body']['deliveryErrors'])); - return $message; + return [ + 'message' => $email['body'], + 'topic' => $topic['body'], + ]; } /** * @depends testSendEmail */ - public function testUpdateEmail(array $email): void + public function testUpdateEmail(array $params): void { + $email = $params['message']; + $message = $this->client->call(Client::METHOD_PATCH, '/messaging/messages/email/' . $email['body']['$id'], [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1170,56 +1228,50 @@ trait MessagingBase } /** - * @depends testCreateDraftEmail + * @depends testSendEmail + * @return void + * @throws \Exception */ - public function testListTargets(array $message) + public function testDeleteMessage(array $params): void { - $response = $this->client->call(Client::METHOD_GET, '/messaging/messages/does_not_exist/targets', [ + $message = $params['message']; + $topic = $params['topic']; + + $response = $this->client->call(Client::METHOD_DELETE, '/messaging/messages/' . $message['$id'], [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ]); - $this->assertEquals(404, $response['headers']['status-code']); + $this->assertEquals(204, $response['headers']['status-code']); - $response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $message['$id'] . '/targets', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]); - - $this->assertEquals(200, $response['headers']['status-code']); - - $targetList = $response['body']; - $this->assertEquals(1, $targetList['total']); - $this->assertEquals(1, count($targetList['targets'])); - $this->assertEquals($message['targets'][0], $targetList['targets'][0]['$id']); - - // Test for empty targets + // Test for FAILURE $response = $this->client->call(Client::METHOD_POST, '/messaging/messages/email', [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ], [ 'messageId' => ID::unique(), - 'subject' => 'New blog post', - 'content' => 'Check out the new blog post at http://localhost', + 'status' => 'processing', + 'topics' => [$topic['$id']], + 'subject' => 'Test subject', + 'content' => 'Test content', ]); - $this->assertEquals(201, $response['headers']['status-code']); - - $message = $response['body']; - - $response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $message['$id'] . '/targets', [ + $response = $this->client->call(Client::METHOD_DELETE, '/messaging/messages/' . $response['body']['$id'], [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ]); - $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(400, $response['headers']['status-code']); - $targetList = $response['body']; - $this->assertEquals(0, $targetList['total']); - $this->assertEquals(0, count($targetList['targets'])); + $response = $this->client->call(Client::METHOD_DELETE, '/messaging/messages/does_not_exist', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + + $this->assertEquals(404, $response['headers']['status-code']); } } From 770c76b1ad384c45c1638d09b80676723b57053c Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 29 Jan 2024 17:46:07 +1300 Subject: [PATCH 2/3] Add doc ref --- docs/references/messaging/delete.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/references/messaging/delete.md diff --git a/docs/references/messaging/delete.md b/docs/references/messaging/delete.md new file mode 100644 index 0000000000..b07d020900 --- /dev/null +++ b/docs/references/messaging/delete.md @@ -0,0 +1 @@ +Delete a message by its unique ID. \ No newline at end of file From afb32b49d517a85d58887c1d0b7e72ea5beffab1 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 30 Jan 2024 16:33:18 +1300 Subject: [PATCH 3/3] Throw if status still scheduled and scheduled time before now --- app/controllers/api/messaging.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/controllers/api/messaging.php b/app/controllers/api/messaging.php index b2970f3d0a..895fba08b1 100644 --- a/app/controllers/api/messaging.php +++ b/app/controllers/api/messaging.php @@ -3297,6 +3297,14 @@ App::delete('/v1/messaging/messages/:messageId') throw new Exception(Exception::MESSAGE_ALREADY_SCHEDULED); case MessageStatus::SCHEDULED: $scheduleId = $message->getAttribute('scheduleId'); + $scheduledAt = $message->getAttribute('scheduledAt'); + + $now = DateTime::now(); + $scheduledDate = DateTime::formatTz($scheduledAt); + + if ($now > $scheduledDate) { + throw new Exception(Exception::MESSAGE_ALREADY_SCHEDULED); + } if (!empty($scheduleId)) { try {