diff --git a/briar-headless/README.md b/briar-headless/README.md index 69b5c6137..eda86afbe 100644 --- a/briar-headless/README.md +++ b/briar-headless/README.md @@ -247,6 +247,12 @@ needs to be provided in the request body as follows: } ``` +### Deleting all private messages + +`DELETE /v1/messages/{contactId}/all` + +It returns with a status code `200`, if removal was successful. + ### Listing blog posts `GET /v1/blogs/posts` diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/Router.kt b/briar-headless/src/main/java/org/briarproject/briar/headless/Router.kt index 4d226a559..a268607b0 100644 --- a/briar-headless/src/main/java/org/briarproject/briar/headless/Router.kt +++ b/briar-headless/src/main/java/org/briarproject/briar/headless/Router.kt @@ -89,6 +89,9 @@ constructor( path("/messages/:contactId/read") { post { ctx -> messagingController.markMessageRead(ctx) } } + path("/messages/:contactId/all") { + delete { ctx -> messagingController.deleteAllMessages(ctx) } + } path("/forums") { get { ctx -> forumController.list(ctx) } post { ctx -> forumController.create(ctx) } diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/MessagingController.kt b/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/MessagingController.kt index 6fe4f23fa..ae6616d0b 100644 --- a/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/MessagingController.kt +++ b/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/MessagingController.kt @@ -10,4 +10,6 @@ interface MessagingController { fun markMessageRead(ctx: Context): Context + fun deleteAllMessages(ctx: Context): Context + } diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/MessagingControllerImpl.kt b/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/MessagingControllerImpl.kt index 533e519e6..c3bdb96ee 100644 --- a/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/MessagingControllerImpl.kt +++ b/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/MessagingControllerImpl.kt @@ -106,6 +106,16 @@ constructor( return MessageId(idBytes) } + override fun deleteAllMessages(ctx: Context): Context { + val contactId = ctx.getContactIdFromPathParam() + try { + val result = conversationManager.deleteAllMessages(contactId) + return ctx.json(result.output()) + } catch (e: NoSuchContactException) { + throw NotFoundResponse() + } + } + override fun eventOccurred(e: Event) { when (e) { is ConversationMessageReceivedEvent<*> -> { diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/OutputConversationMessage.kt b/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/OutputConversationMessage.kt index 3c42a39d0..487bcdbb0 100644 --- a/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/OutputConversationMessage.kt +++ b/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/OutputConversationMessage.kt @@ -5,6 +5,7 @@ import org.briarproject.bramble.api.sync.MessageId import org.briarproject.bramble.api.sync.event.MessagesAckedEvent import org.briarproject.bramble.api.sync.event.MessagesSentEvent import org.briarproject.briar.api.conversation.ConversationMessageHeader +import org.briarproject.briar.api.conversation.DeletionResult import org.briarproject.briar.api.messaging.PrivateMessage import org.briarproject.briar.api.messaging.PrivateMessageHeader import org.briarproject.briar.headless.json.JsonDict @@ -47,6 +48,15 @@ internal fun PrivateMessage.output(contactId: ContactId, text: String) = JsonDic "text" to text ) +internal fun DeletionResult.output() = JsonDict( + "allDeleted" to allDeleted(), + "hasIntroductionSessionInProgress" to hasIntroductionSessionInProgress(), + "hasInvitationSessionInProgress" to hasInvitationSessionInProgress(), + "hasNotAllIntroductionSelected" to hasNotAllIntroductionSelected(), + "hasNotAllInvitationSelected" to hasNotAllInvitationSelected(), + "hasNotFullyDownloaded" to hasNotFullyDownloaded() +) + internal fun MessagesAckedEvent.output() = JsonDict( "contactId" to contactId.int, "messageIds" to messageIds.toJson() diff --git a/briar-headless/src/test/java/org/briarproject/briar/headless/messaging/MessagingControllerImplTest.kt b/briar-headless/src/test/java/org/briarproject/briar/headless/messaging/MessagingControllerImplTest.kt index d827e50e6..84c7c5ec2 100644 --- a/briar-headless/src/test/java/org/briarproject/briar/headless/messaging/MessagingControllerImplTest.kt +++ b/briar-headless/src/test/java/org/briarproject/briar/headless/messaging/MessagingControllerImplTest.kt @@ -17,6 +17,7 @@ import org.briarproject.bramble.test.ImmediateExecutor import org.briarproject.bramble.test.TestUtils.getRandomId import org.briarproject.bramble.util.StringUtils.getRandomString import org.briarproject.briar.api.client.SessionId +import org.briarproject.briar.api.conversation.DeletionResult import org.briarproject.briar.api.introduction.IntroductionRequest import org.briarproject.briar.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_TEXT_LENGTH import org.briarproject.briar.api.messaging.MessagingManager @@ -32,6 +33,7 @@ import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertThrows import org.junit.jupiter.api.Test import org.spongycastle.util.encoders.Base64 +import kotlin.random.Random internal class MessagingControllerImplTest : ControllerTest() { @@ -343,6 +345,53 @@ internal class MessagingControllerImplTest : ControllerTest() { assertJsonEquals(json, request.output(contact.id)) } + @Test + fun testDeleteAllMessages() { + val result = DeletionResult() + every { ctx.pathParam("contactId") } returns "1" + every { conversationManager.deleteAllMessages(ContactId(1)) } returns result + every { ctx.json(result.output()) } returns ctx + controller.deleteAllMessages(ctx) + } + + @Test + fun testDeleteAllMessagesInvalidContactId() { + every { ctx.pathParam("contactId") } returns "foo" + assertThrows(NotFoundResponse::class.java) { + controller.deleteAllMessages(ctx) + } + } + + @Test + fun testDeleteAllMessagesNonexistentContactId() { + every { ctx.pathParam("contactId") } returns "1" + every { conversationManager.deleteAllMessages(ContactId(1)) } throws NoSuchContactException() + assertThrows(NotFoundResponse::class.java) { + controller.deleteAllMessages(ctx) + } + } + + @Test + fun testOutputDeletionResult() { + val result = DeletionResult() + if (Random.nextBoolean()) result.addInvitationNotAllSelected() + if (Random.nextBoolean()) result.addInvitationSessionInProgress() + if (Random.nextBoolean()) result.addIntroductionNotAllSelected() + if (Random.nextBoolean()) result.addIntroductionSessionInProgress() + if (Random.nextBoolean()) result.addNotFullyDownloaded() + val json = """ + { + "allDeleted": ${result.allDeleted()}, + "hasIntroductionSessionInProgress": ${result.hasIntroductionSessionInProgress()}, + "hasInvitationSessionInProgress": ${result.hasInvitationSessionInProgress()}, + "hasNotAllIntroductionSelected": ${result.hasNotAllIntroductionSelected()}, + "hasNotAllInvitationSelected": ${result.hasNotAllInvitationSelected()}, + "hasNotFullyDownloaded": ${result.hasNotFullyDownloaded()} + } + """ + assertJsonEquals(json, result.output()) + } + private fun expectGetContact() { every { ctx.pathParam("contactId") } returns contact.id.int.toString() every { contactManager.getContact(contact.id) } returns contact