From 3938c53256f21d1a24de81e350631885eeabdad8 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Thu, 18 Jan 2024 01:33:32 +0000 Subject: [PATCH] Add endpoint to list a message's targets This endpoint is needed so that the Console can fetch the associated targets to display when viewing the details of a message or going in to edit a message. --- app/controllers/api/messaging.php | 60 ++++++++++++ .../messaging/list-message-targets.md | 1 + .../e2e/Services/Messaging/MessagingBase.php | 97 ++++++++++++++++++- 3 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 docs/references/messaging/list-message-targets.md diff --git a/app/controllers/api/messaging.php b/app/controllers/api/messaging.php index f19d619613..ef72be19cf 100644 --- a/app/controllers/api/messaging.php +++ b/app/controllers/api/messaging.php @@ -14,6 +14,7 @@ use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Database\Validator\Queries\Messages; use Appwrite\Utopia\Database\Validator\Queries\Providers; use Appwrite\Utopia\Database\Validator\Queries\Subscribers; +use Appwrite\Utopia\Database\Validator\Queries\Targets; use Appwrite\Utopia\Database\Validator\Queries\Topics; use Appwrite\Utopia\Response; use Utopia\App; @@ -2742,6 +2743,65 @@ App::get('/v1/messaging/messages/:messageId/logs') ]), Response::MODEL_LOG_LIST); }); +App::get('/v1/messaging/messages/:messageId/targets') + ->desc('List message targets') + ->groups(['api', 'messaging']) + ->label('scope', 'messages.read') + ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY]) + ->label('sdk.namespace', 'messaging') + ->label('sdk.method', 'listTargets') + ->label('sdk.description', '/docs/references/messaging/list-message-targets.md') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_TARGET_LIST) + ->param('messageId', '', new UID(), 'Message ID.') + ->param('queries', [], new Targets(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Targets::ALLOWED_ATTRIBUTES), true) + ->inject('response') + ->inject('dbForProject') + ->inject('locale') + ->inject('geodb') + ->action(function (string $messageId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { + $message = $dbForProject->getDocument('messages', $messageId); + + if ($message->isEmpty()) { + throw new Exception(Exception::MESSAGE_NOT_FOUND); + } + + $targetIDs = $message->getAttribute('targets'); + + if (empty($targetIDs)) { + $response->dynamic(new Document([ + 'targets' => [], + 'total' => 0, + ]), Response::MODEL_TARGET_LIST); + return; + } + + $queries = Query::parseQueries($queries); + + $queries[] = Query::equal('$id', $targetIDs); + + // Get cursor document if there was a cursor query + $cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + $cursor = reset($cursor); + + if ($cursor) { + $targetId = $cursor->getValue(); + $cursorDocument = $dbForProject->getDocument('targets', $targetId); + + if ($cursorDocument->isEmpty()) { + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Target '{$targetId}' for the 'cursor' value not found."); + } + + $cursor->setValue($cursorDocument); + } + + $response->dynamic(new Document([ + 'targets' => $dbForProject->find('targets', $queries), + 'total' => $dbForProject->count('targets', $queries, APP_LIMIT_COUNT), + ]), Response::MODEL_TARGET_LIST); + }); + App::get('/v1/messaging/messages/:messageId') ->desc('Get a message') ->groups(['api', 'messaging']) diff --git a/docs/references/messaging/list-message-targets.md b/docs/references/messaging/list-message-targets.md new file mode 100644 index 0000000000..cc39250d5f --- /dev/null +++ b/docs/references/messaging/list-message-targets.md @@ -0,0 +1 @@ +List the targets associated with a message as set via the targets attribute. \ No newline at end of file diff --git a/tests/e2e/Services/Messaging/MessagingBase.php b/tests/e2e/Services/Messaging/MessagingBase.php index dafc618ac1..d6c4b59ce3 100644 --- a/tests/e2e/Services/Messaging/MessagingBase.php +++ b/tests/e2e/Services/Messaging/MessagingBase.php @@ -2,10 +2,10 @@ namespace Tests\E2E\Services\Messaging; +use Appwrite\Enum\MessageStatus; use Tests\E2E\Client; use Utopia\App; use Utopia\Database\Helpers\ID; -use Utopia\Database\Query; use Utopia\DSN\DSN; trait MessagingBase @@ -617,6 +617,47 @@ trait MessagingBase $this->assertEquals(204, $response['headers']['status-code']); } + public function testCreateDraftEmail() + { + // Create User + $response = $this->client->call(Client::METHOD_POST, '/users', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], [ + 'userId' => ID::unique(), + 'email' => uniqid() . "@example.com", + 'password' => 'password', + 'name' => 'Messaging User', + ]); + + $this->assertEquals(201, $response['headers']['status-code'], "Error creating user: " . var_export($response['body'], true)); + + $user = $response['body']; + + $this->assertEquals(1, \count($user['targets'])); + $targetId = $user['targets'][0]['$id']; + + // Create Email + $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(), + 'targets' => [$targetId], + 'subject' => 'New blog post', + 'content' => 'Check out the new blog post at http://localhost', + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + $message = $response['body']; + $this->assertEquals(MessageStatus::DRAFT, $message['status']); + + return $message; + } + public function testSendEmail() { if (empty(App::getEnv('_APP_MESSAGE_EMAIL_TEST_DSN'))) { @@ -1101,4 +1142,58 @@ trait MessagingBase $this->assertEquals(1, $message['body']['deliveredTotal']); $this->assertEquals(0, \count($message['body']['deliveryErrors'])); } + + /** + * @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'])); + } }