diff --git a/briar-headless/README.md b/briar-headless/README.md index a27d9dc3d..d35bcc98e 100644 --- a/briar-headless/README.md +++ b/briar-headless/README.md @@ -72,9 +72,16 @@ Returns a JSON array of contacts: The only workaround is to add a contact to the Briar app running on a rooted Android phone and then move its database (and key files) to the headless peer. +### Removing a contact + +`DELETE /v1/contacts/{contactId}` + +The `{contactId}` is the `contactId` of the contact (`1` in the example above). +It returns with a status code `200`, if removal was successful. + ### Listing all private messages -`GET /messages/{contactId}` +`GET /v1/messages/{contactId}` The `{contactId}` is the `contactId` of the contact (`1` in the example above). It returns a JSON array of private messages: @@ -100,7 +107,7 @@ Attention: There can messages of other `type`s where the message `text` is `null ### Writing a private message -`POST /messages/{contactId}` +`POST /v1/messages/{contactId}` The text of the message should be posted as JSON: 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 7e8000ce4..c155fcfd9 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 @@ -7,9 +7,11 @@ import io.javalin.Context import io.javalin.Javalin import io.javalin.JavalinEvent.SERVER_START_FAILED import io.javalin.JavalinEvent.SERVER_STOPPED +import io.javalin.NotFoundResponse import io.javalin.apibuilder.ApiBuilder.* import io.javalin.core.util.ContextUtil import io.javalin.core.util.Header.AUTHORIZATION +import org.briarproject.bramble.api.contact.ContactId import org.briarproject.briar.headless.blogs.BlogController import org.briarproject.briar.headless.contact.ContactController import org.briarproject.briar.headless.event.WebSocketController @@ -63,6 +65,9 @@ constructor( path("/v1") { path("/contacts") { get { ctx -> contactController.list(ctx) } + path("/:contactId") { + delete { ctx -> contactController.delete(ctx) } + } } path("/messages/:contactId") { get { ctx -> messagingController.list(ctx) } @@ -112,6 +117,21 @@ constructor( } +/** + * Returns a [ContactId] from the "contactId" path parameter. + * + * @throws NotFoundResponse when contactId is not a number. + */ +fun Context.getContactIdFromPathParam(): ContactId { + val contactString = pathParam("contactId") + val contactInt = try { + Integer.parseInt(contactString) + } catch (e: NumberFormatException) { + throw NotFoundResponse() + } + return ContactId(contactInt) +} + /** * Returns a String from the JSON field or throws [BadRequestResponse] if null or empty. */ diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/contact/ContactController.kt b/briar-headless/src/main/java/org/briarproject/briar/headless/contact/ContactController.kt index 9eb21a120..d7a0bfbc4 100644 --- a/briar-headless/src/main/java/org/briarproject/briar/headless/contact/ContactController.kt +++ b/briar-headless/src/main/java/org/briarproject/briar/headless/contact/ContactController.kt @@ -5,5 +5,6 @@ import io.javalin.Context interface ContactController { fun list(ctx: Context): Context + fun delete(ctx: Context): Context } diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/contact/ContactControllerImpl.kt b/briar-headless/src/main/java/org/briarproject/briar/headless/contact/ContactControllerImpl.kt index bf94c70a5..89f4c0789 100644 --- a/briar-headless/src/main/java/org/briarproject/briar/headless/contact/ContactControllerImpl.kt +++ b/briar-headless/src/main/java/org/briarproject/briar/headless/contact/ContactControllerImpl.kt @@ -1,7 +1,10 @@ package org.briarproject.briar.headless.contact import io.javalin.Context +import io.javalin.NotFoundResponse import org.briarproject.bramble.api.contact.ContactManager +import org.briarproject.bramble.api.db.NoSuchContactException +import org.briarproject.briar.headless.getContactIdFromPathParam import javax.annotation.concurrent.Immutable import javax.inject.Inject import javax.inject.Singleton @@ -19,4 +22,14 @@ constructor(private val contactManager: ContactManager) : ContactController { return ctx.json(contacts) } + override fun delete(ctx: Context): Context { + val contactId = ctx.getContactIdFromPathParam() + try { + contactManager.removeContact(contactId) + } catch (e: NoSuchContactException) { + throw NotFoundResponse() + } + return ctx + } + } 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 66d951aa8..0521666f9 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 @@ -26,6 +26,7 @@ import org.briarproject.briar.api.privategroup.invitation.GroupInvitationRequest import org.briarproject.briar.api.privategroup.invitation.GroupInvitationResponse import org.briarproject.briar.headless.event.WebSocketController import org.briarproject.briar.headless.event.output +import org.briarproject.briar.headless.getContactIdFromPathParam import org.briarproject.briar.headless.getFromJson import org.briarproject.briar.headless.json.JsonDict import java.util.concurrent.Executor @@ -84,13 +85,7 @@ constructor( } private fun getContact(ctx: Context): Contact { - val contactString = ctx.pathParam("contactId") - val contactInt = try { - Integer.parseInt(contactString) - } catch (e: NumberFormatException) { - throw NotFoundResponse() - } - val contactId = ContactId(contactInt) + val contactId = ctx.getContactIdFromPathParam() return try { contactManager.getContact(contactId) } catch (e: NoSuchContactException) { diff --git a/briar-headless/src/test/java/org/briarproject/briar/headless/contact/ContactControllerTest.kt b/briar-headless/src/test/java/org/briarproject/briar/headless/contact/ContactControllerTest.kt index 7566c9842..428b9aaa2 100644 --- a/briar-headless/src/test/java/org/briarproject/briar/headless/contact/ContactControllerTest.kt +++ b/briar-headless/src/test/java/org/briarproject/briar/headless/contact/ContactControllerTest.kt @@ -1,10 +1,16 @@ package org.briarproject.briar.headless.contact +import io.javalin.NotFoundResponse import io.javalin.json.JavalinJson.toJson +import io.mockk.Runs import io.mockk.every +import io.mockk.just import org.briarproject.bramble.api.contact.Contact +import org.briarproject.bramble.api.contact.ContactId +import org.briarproject.bramble.api.db.NoSuchContactException import org.briarproject.bramble.identity.output import org.briarproject.briar.headless.ControllerTest +import org.junit.jupiter.api.Assertions.assertThrows import org.junit.jupiter.api.Test internal class ContactControllerTest : ControllerTest() { @@ -25,6 +31,30 @@ internal class ContactControllerTest : ControllerTest() { controller.list(ctx) } + @Test + fun testDelete() { + every { ctx.pathParam("contactId") } returns "1" + every { contactManager.removeContact(ContactId(1)) } just Runs + controller.delete(ctx) + } + + @Test + fun testDeleteInvalidContactId() { + every { ctx.pathParam("contactId") } returns "foo" + assertThrows(NotFoundResponse::class.java) { + controller.delete(ctx) + } + } + + @Test + fun testDeleteNonexistentContactId() { + every { ctx.pathParam("contactId") } returns "1" + every { contactManager.removeContact(ContactId(1)) } throws NoSuchContactException() + assertThrows(NotFoundResponse::class.java) { + controller.delete(ctx) + } + } + @Test fun testOutputContact() { val json = """