diff --git a/briar-headless/README.md b/briar-headless/README.md index eda86afbe..b608e5436 100644 --- a/briar-headless/README.md +++ b/briar-headless/README.md @@ -183,6 +183,18 @@ Note that it's also possible to add contacts nearby via Bluetooth/Wifi or introductions. In these cases contacts omit the `pendingContact` state and directly become `contact`s. +### Changing alias of a contact + +`PUT /v1/contacts/{contactId}/alias` + +The alias should be posted as a JSON object: + +```json +{ + "alias": "A nickname for the new contact" +} +``` + ### Removing a contact `DELETE /v1/contacts/{contactId}` 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 a268607b0..598c2a815 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 @@ -81,6 +81,9 @@ constructor( path("/:contactId") { delete { ctx -> contactController.delete(ctx) } } + path("/:contactId/alias") { + put { ctx -> contactController.setContactAlias(ctx) } + } } path("/messages/:contactId") { get { ctx -> messagingController.list(ctx) } 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 f936004fb..88d26416b 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 @@ -9,6 +9,7 @@ interface ContactController { fun addPendingContact(ctx: Context): Context fun listPendingContacts(ctx: Context): Context fun removePendingContact(ctx: Context): Context + fun setContactAlias(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 b7e542b61..6815f2c52 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 @@ -92,9 +92,7 @@ constructor( val link = ctx.getFromJson(objectMapper, "link") val alias = ctx.getFromJson(objectMapper, "alias") if (!LINK_REGEX.matcher(link).find()) throw BadRequestResponse("Invalid Link") - val aliasUtf8 = toUtf8(alias) - if (aliasUtf8.isEmpty() || aliasUtf8.size > MAX_AUTHOR_NAME_LENGTH) - throw BadRequestResponse("Invalid Alias") + checkAliasLength(alias) val pendingContact = contactManager.addPendingContact(link, alias) return ctx.json(pendingContact.output()) } @@ -125,6 +123,18 @@ constructor( return ctx } + override fun setContactAlias(ctx: Context): Context { + val contactId = ctx.getContactIdFromPathParam() + val alias = ctx.getFromJson(objectMapper, "alias") + checkAliasLength(alias) + try { + contactManager.setContactAlias(contactId, alias) + } catch (e: NoSuchContactException) { + throw NotFoundResponse() + } + return ctx + } + override fun delete(ctx: Context): Context { val contactId = ctx.getContactIdFromPathParam() try { @@ -135,4 +145,10 @@ constructor( return ctx } + private fun checkAliasLength(alias: String) { + val aliasUtf8 = toUtf8(alias) + if (aliasUtf8.isEmpty() || aliasUtf8.size > MAX_AUTHOR_NAME_LENGTH) + throw BadRequestResponse("Invalid Alias") + } + } 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 a169e8eef..7a8476b1e 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 @@ -6,6 +6,7 @@ import io.javalin.plugin.json.JavalinJson.toJson import io.mockk.Runs import io.mockk.every import io.mockk.just +import io.mockk.mockkStatic import io.mockk.runs import org.briarproject.bramble.api.Pair import org.briarproject.bramble.api.contact.Contact @@ -27,6 +28,7 @@ import org.briarproject.bramble.test.TestUtils.getPendingContact import org.briarproject.bramble.test.TestUtils.getRandomBytes import org.briarproject.bramble.util.StringUtils.getRandomString import org.briarproject.briar.headless.ControllerTest +import org.briarproject.briar.headless.getFromJson import org.briarproject.briar.headless.json.JsonDict import org.junit.jupiter.api.Assertions.assertNotNull import org.junit.jupiter.api.Assertions.assertThrows @@ -194,6 +196,66 @@ internal class ContactControllerTest : ControllerTest() { } } + @Test + fun testSetContactAlias() { + mockkStatic("org.briarproject.briar.headless.RouterKt") + every { ctx.pathParam("contactId") } returns "1" + every { ctx.getFromJson(objectMapper, "alias") } returns "foo" + every { contactManager.setContactAlias(ContactId(1), "foo") } just Runs + controller.setContactAlias(ctx) + } + + @Test + fun testSetContactAliasInvalidId() { + mockkStatic("org.briarproject.briar.headless.RouterKt") + every { ctx.pathParam("contactId") } returns "foo" + every { ctx.getFromJson(objectMapper, "alias") } returns "bar" + assertThrows(NotFoundResponse::class.java) { + controller.setContactAlias(ctx) + } + } + + @Test + fun testSetContactAliasNonexistentId() { + mockkStatic("org.briarproject.briar.headless.RouterKt") + every { ctx.pathParam("contactId") } returns "1" + every { ctx.getFromJson(objectMapper, "alias") } returns "foo" + every { contactManager.setContactAlias(ContactId(1), "foo") } throws NotFoundResponse() + assertThrows(NotFoundResponse::class.java) { + controller.setContactAlias(ctx) + } + } + + @Test + fun testSetContactAliasInvalid() { + mockkStatic("org.briarproject.briar.headless.RouterKt") + every { ctx.pathParam("contactId") } returns "1" + every { ctx.getFromJson(objectMapper, "alias") } returns getRandomString(MAX_AUTHOR_NAME_LENGTH + 1) + assertThrows(BadRequestResponse::class.java) { + controller.setContactAlias(ctx) + } + } + + @Test + fun testSetContactAliasEmpty() { + mockkStatic("org.briarproject.briar.headless.RouterKt") + every { ctx.pathParam("contactId") } returns "1" + every { ctx.getFromJson(objectMapper, "alias") } returns "" + assertThrows(BadRequestResponse::class.java) { + controller.setContactAlias(ctx) + } + } + + @Test + fun testSetContactAliasMissing() { + mockkStatic("org.briarproject.briar.headless.RouterKt") + every { ctx.pathParam("contactId") } returns "1" + every { ctx.body() } returns "" + assertThrows(BadRequestResponse::class.java) { + controller.setContactAlias(ctx) + } + } + @Test fun testDelete() { every { ctx.pathParam("contactId") } returns "1"