mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-20 14:49:53 +01:00
[headless] make events related to adding contacts available via websocket
This commit is contained in:
@@ -274,8 +274,51 @@ it will send a JSON object to connected websocket clients:
|
|||||||
Note that the JSON object in `data` is exactly what the REST API returns
|
Note that the JSON object in `data` is exactly what the REST API returns
|
||||||
when listing private messages.
|
when listing private messages.
|
||||||
|
|
||||||
# TODO
|
### A new contact was added remotely
|
||||||
|
|
||||||
* PendingContactStateChangedEvent
|
When the Briar peer adds a new contact remotely,
|
||||||
* PendingContactRemovedEvent
|
it will send a JSON object representing the new contact to connected websocket clients:
|
||||||
* ContactAddedRemotelyEvent
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"author": {
|
||||||
|
"formatVersion": 1,
|
||||||
|
"id": "y1wkIzAimAbYoCGgWxkWlr6vnq1F8t1QRA/UMPgI0E0=",
|
||||||
|
"name": "Test",
|
||||||
|
"publicKey": "BDu6h1S02bF4W6rgoZfZ6BMjTj/9S9hNN7EQoV05qUo="
|
||||||
|
},
|
||||||
|
"contactId": 1,
|
||||||
|
"verified": true
|
||||||
|
},
|
||||||
|
"name": "ContactAddedRemotelyEvent",
|
||||||
|
"type": "event"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### A pending contact changed its state
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"pendingContactId":"YqKjsczCuxScXohb5+RAYtFEwK71icoB4ldztV2gh7M=",
|
||||||
|
"state":"waiting_for_connection"
|
||||||
|
},
|
||||||
|
"name": "PendingContactStateChangedEvent",
|
||||||
|
"type": "event"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For a list of valid states, please see the section on adding contacts above.
|
||||||
|
|
||||||
|
### A pending contact was removed
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"pendingContactId": "YqKjsczCuxScXohb5+RAYtFEwK71icoB4ldztV2gh7M="
|
||||||
|
},
|
||||||
|
"name": "PendingContactRemovedEvent",
|
||||||
|
"type": "event"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
@@ -5,8 +5,14 @@ import io.javalin.Context
|
|||||||
import io.javalin.NotFoundResponse
|
import io.javalin.NotFoundResponse
|
||||||
import org.briarproject.bramble.api.contact.ContactManager
|
import org.briarproject.bramble.api.contact.ContactManager
|
||||||
import org.briarproject.bramble.api.contact.PendingContactId
|
import org.briarproject.bramble.api.contact.PendingContactId
|
||||||
|
import org.briarproject.bramble.api.contact.event.ContactAddedRemotelyEvent
|
||||||
|
import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent
|
||||||
|
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent
|
||||||
import org.briarproject.bramble.api.db.NoSuchContactException
|
import org.briarproject.bramble.api.db.NoSuchContactException
|
||||||
import org.briarproject.bramble.api.db.NoSuchPendingContactException
|
import org.briarproject.bramble.api.db.NoSuchPendingContactException
|
||||||
|
import org.briarproject.bramble.api.event.Event
|
||||||
|
import org.briarproject.bramble.api.event.EventListener
|
||||||
|
import org.briarproject.briar.headless.event.WebSocketController
|
||||||
import org.briarproject.briar.headless.getContactIdFromPathParam
|
import org.briarproject.briar.headless.getContactIdFromPathParam
|
||||||
import org.briarproject.briar.headless.getFromJson
|
import org.briarproject.briar.headless.getFromJson
|
||||||
import org.briarproject.briar.headless.json.JsonDict
|
import org.briarproject.briar.headless.json.JsonDict
|
||||||
@@ -16,12 +22,33 @@ import javax.annotation.concurrent.Immutable
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
internal const val EVENT_CONTACT_ADDED_REMOTELY = "ContactAddedRemotelyEvent"
|
||||||
|
internal const val EVENT_PENDING_CONTACT_STATE_CHANGED = "PendingContactStateChangedEvent"
|
||||||
|
internal const val EVENT_PENDING_CONTACT_REMOVED = "PendingContactRemovedEvent"
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@Singleton
|
@Singleton
|
||||||
internal class ContactControllerImpl
|
internal class ContactControllerImpl
|
||||||
@Inject
|
@Inject
|
||||||
constructor(private val contactManager: ContactManager, private val objectMapper: ObjectMapper) :
|
constructor(
|
||||||
ContactController {
|
private val contactManager: ContactManager,
|
||||||
|
private val objectMapper: ObjectMapper,
|
||||||
|
private val webSocket: WebSocketController
|
||||||
|
) : ContactController, EventListener {
|
||||||
|
|
||||||
|
override fun eventOccurred(e: Event) = when (e) {
|
||||||
|
is ContactAddedRemotelyEvent -> {
|
||||||
|
webSocket.sendEvent(EVENT_CONTACT_ADDED_REMOTELY, e.output())
|
||||||
|
}
|
||||||
|
is PendingContactStateChangedEvent -> {
|
||||||
|
webSocket.sendEvent(EVENT_PENDING_CONTACT_STATE_CHANGED, e.output())
|
||||||
|
}
|
||||||
|
is PendingContactRemovedEvent -> {
|
||||||
|
webSocket.sendEvent(EVENT_PENDING_CONTACT_REMOVED, e.output())
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun list(ctx: Context): Context {
|
override fun list(ctx: Context): Context {
|
||||||
val contacts = contactManager.contacts.map { contact ->
|
val contacts = contactManager.contacts.map { contact ->
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.briarproject.briar.headless.contact
|
|||||||
|
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
|
import org.briarproject.bramble.api.event.EventBus
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@@ -9,7 +10,11 @@ class HeadlessContactModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
internal fun provideContactController(contactController: ContactControllerImpl): ContactController {
|
internal fun provideContactController(
|
||||||
|
eventBus: EventBus,
|
||||||
|
contactController: ContactControllerImpl
|
||||||
|
): ContactController {
|
||||||
|
eventBus.addListener(contactController)
|
||||||
return contactController
|
return contactController
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.briar.headless.contact
|
package org.briarproject.briar.headless.contact
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.Contact
|
import org.briarproject.bramble.api.contact.Contact
|
||||||
|
import org.briarproject.bramble.api.contact.event.ContactAddedRemotelyEvent
|
||||||
import org.briarproject.bramble.identity.output
|
import org.briarproject.bramble.identity.output
|
||||||
import org.briarproject.briar.headless.json.JsonDict
|
import org.briarproject.briar.headless.json.JsonDict
|
||||||
|
|
||||||
@@ -9,3 +10,5 @@ internal fun Contact.output() = JsonDict(
|
|||||||
"author" to author.output(),
|
"author" to author.output(),
|
||||||
"verified" to isVerified
|
"verified" to isVerified
|
||||||
)
|
)
|
||||||
|
|
||||||
|
internal fun ContactAddedRemotelyEvent.output() = contact.output()
|
||||||
|
|||||||
@@ -1,18 +1,32 @@
|
|||||||
package org.briarproject.briar.headless.contact
|
package org.briarproject.briar.headless.contact
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.PendingContact
|
import org.briarproject.bramble.api.contact.PendingContact
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContactState
|
||||||
import org.briarproject.bramble.api.contact.PendingContactState.*
|
import org.briarproject.bramble.api.contact.PendingContactState.*
|
||||||
|
import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent
|
||||||
|
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent
|
||||||
import org.briarproject.briar.headless.json.JsonDict
|
import org.briarproject.briar.headless.json.JsonDict
|
||||||
|
|
||||||
internal fun PendingContact.output() = JsonDict(
|
internal fun PendingContact.output() = JsonDict(
|
||||||
"pendingContactId" to id.bytes,
|
"pendingContactId" to id.bytes,
|
||||||
"alias" to alias,
|
"alias" to alias,
|
||||||
"state" to when(state) {
|
"state" to state.output(),
|
||||||
WAITING_FOR_CONNECTION -> "waiting_for_connection"
|
|
||||||
CONNECTED -> "connected"
|
|
||||||
ADDING_CONTACT -> "adding_contact"
|
|
||||||
FAILED -> "failed"
|
|
||||||
else -> throw AssertionError()
|
|
||||||
},
|
|
||||||
"timestamp" to timestamp
|
"timestamp" to timestamp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
internal fun PendingContactState.output() = when(this) {
|
||||||
|
WAITING_FOR_CONNECTION -> "waiting_for_connection"
|
||||||
|
CONNECTED -> "connected"
|
||||||
|
ADDING_CONTACT -> "adding_contact"
|
||||||
|
FAILED -> "failed"
|
||||||
|
else -> throw AssertionError()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun PendingContactStateChangedEvent.output() = JsonDict(
|
||||||
|
"pendingContactId" to id.bytes,
|
||||||
|
"state" to pendingContactState.output()
|
||||||
|
)
|
||||||
|
|
||||||
|
internal fun PendingContactRemovedEvent.output() = JsonDict(
|
||||||
|
"pendingContactId" to id.bytes
|
||||||
|
)
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import org.briarproject.bramble.api.sync.Message
|
|||||||
import org.briarproject.bramble.api.system.Clock
|
import org.briarproject.bramble.api.system.Clock
|
||||||
import org.briarproject.bramble.test.TestUtils.*
|
import org.briarproject.bramble.test.TestUtils.*
|
||||||
import org.briarproject.bramble.util.StringUtils.getRandomString
|
import org.briarproject.bramble.util.StringUtils.getRandomString
|
||||||
|
import org.briarproject.briar.headless.event.WebSocketController
|
||||||
import org.skyscreamer.jsonassert.JSONAssert.assertEquals
|
import org.skyscreamer.jsonassert.JSONAssert.assertEquals
|
||||||
import org.skyscreamer.jsonassert.JSONCompareMode.STRICT
|
import org.skyscreamer.jsonassert.JSONCompareMode.STRICT
|
||||||
import javax.servlet.http.HttpServletRequest
|
import javax.servlet.http.HttpServletRequest
|
||||||
@@ -26,6 +27,8 @@ abstract class ControllerTest {
|
|||||||
protected val clock = mockk<Clock>()
|
protected val clock = mockk<Clock>()
|
||||||
protected val ctx = mockk<Context>()
|
protected val ctx = mockk<Context>()
|
||||||
|
|
||||||
|
protected val webSocketController = mockk<WebSocketController>()
|
||||||
|
|
||||||
private val request = mockk<HttpServletRequest>(relaxed = true)
|
private val request = mockk<HttpServletRequest>(relaxed = true)
|
||||||
private val response = mockk<HttpServletResponse>(relaxed = true)
|
private val response = mockk<HttpServletResponse>(relaxed = true)
|
||||||
private val outputCtx = ContextUtil.init(request, response)
|
private val outputCtx = ContextUtil.init(request, response)
|
||||||
|
|||||||
@@ -5,9 +5,14 @@ import io.javalin.json.JavalinJson.toJson
|
|||||||
import io.mockk.Runs
|
import io.mockk.Runs
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.just
|
import io.mockk.just
|
||||||
|
import io.mockk.runs
|
||||||
import org.briarproject.bramble.api.contact.Contact
|
import org.briarproject.bramble.api.contact.Contact
|
||||||
import org.briarproject.bramble.api.contact.ContactId
|
import org.briarproject.bramble.api.contact.ContactId
|
||||||
import org.briarproject.bramble.api.contact.PendingContactId
|
import org.briarproject.bramble.api.contact.PendingContactId
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContactState.FAILED
|
||||||
|
import org.briarproject.bramble.api.contact.event.ContactAddedRemotelyEvent
|
||||||
|
import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent
|
||||||
|
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent
|
||||||
import org.briarproject.bramble.api.db.NoSuchContactException
|
import org.briarproject.bramble.api.db.NoSuchContactException
|
||||||
import org.briarproject.bramble.api.db.NoSuchPendingContactException
|
import org.briarproject.bramble.api.db.NoSuchPendingContactException
|
||||||
import org.briarproject.bramble.identity.output
|
import org.briarproject.bramble.identity.output
|
||||||
@@ -20,9 +25,11 @@ import org.junit.jupiter.api.Test
|
|||||||
|
|
||||||
internal class ContactControllerTest : ControllerTest() {
|
internal class ContactControllerTest : ControllerTest() {
|
||||||
|
|
||||||
private val controller = ContactControllerImpl(contactManager, objectMapper)
|
|
||||||
private val pendingContact = getPendingContact()
|
private val pendingContact = getPendingContact()
|
||||||
|
|
||||||
|
private val controller =
|
||||||
|
ContactControllerImpl(contactManager, objectMapper, webSocketController)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testEmptyContactList() {
|
fun testEmptyContactList() {
|
||||||
every { contactManager.contacts } returns emptyList<Contact>()
|
every { contactManager.contacts } returns emptyList<Contact>()
|
||||||
@@ -134,6 +141,48 @@ internal class ContactControllerTest : ControllerTest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testContactAddedRemotelyEvent() {
|
||||||
|
val event = ContactAddedRemotelyEvent(contact)
|
||||||
|
|
||||||
|
every {
|
||||||
|
webSocketController.sendEvent(
|
||||||
|
EVENT_CONTACT_ADDED_REMOTELY,
|
||||||
|
event.output()
|
||||||
|
)
|
||||||
|
} just runs
|
||||||
|
|
||||||
|
controller.eventOccurred(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testPendingContactStateChangedEvent() {
|
||||||
|
val event = PendingContactStateChangedEvent(pendingContact.id, FAILED)
|
||||||
|
|
||||||
|
every {
|
||||||
|
webSocketController.sendEvent(
|
||||||
|
EVENT_PENDING_CONTACT_STATE_CHANGED,
|
||||||
|
event.output()
|
||||||
|
)
|
||||||
|
} just runs
|
||||||
|
|
||||||
|
controller.eventOccurred(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testPendingContactRemovedEvent() {
|
||||||
|
val event = PendingContactRemovedEvent(pendingContact.id)
|
||||||
|
|
||||||
|
every {
|
||||||
|
webSocketController.sendEvent(
|
||||||
|
EVENT_PENDING_CONTACT_REMOVED,
|
||||||
|
event.output()
|
||||||
|
)
|
||||||
|
} just runs
|
||||||
|
|
||||||
|
controller.eventOccurred(event)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testOutputContact() {
|
fun testOutputContact() {
|
||||||
val json = """
|
val json = """
|
||||||
@@ -159,6 +208,12 @@ internal class ContactControllerTest : ControllerTest() {
|
|||||||
assertJsonEquals(json, author.output())
|
assertJsonEquals(json, author.output())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testOutputContactAddedRemotelyEvent() {
|
||||||
|
val event = ContactAddedRemotelyEvent(contact)
|
||||||
|
assertJsonEquals(toJson(contact.output()), event.output())
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testOutputPendingContact() {
|
fun testOutputPendingContact() {
|
||||||
val json = """
|
val json = """
|
||||||
@@ -172,4 +227,27 @@ internal class ContactControllerTest : ControllerTest() {
|
|||||||
assertJsonEquals(json, pendingContact.output())
|
assertJsonEquals(json, pendingContact.output())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testOutputPendingContactStateChangedEvent() {
|
||||||
|
val event = PendingContactStateChangedEvent(pendingContact.id, FAILED)
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"pendingContactId": ${toJson(pendingContact.id.bytes)},
|
||||||
|
"state": "failed"
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
assertJsonEquals(json, event.output())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testOutputPendingContactRemovedEvent() {
|
||||||
|
val event = PendingContactRemovedEvent(pendingContact.id)
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"pendingContactId": ${toJson(pendingContact.id.bytes)}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
assertJsonEquals(json, event.output())
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import org.briarproject.briar.api.messaging.PrivateMessageFactory
|
|||||||
import org.briarproject.briar.api.messaging.PrivateMessageHeader
|
import org.briarproject.briar.api.messaging.PrivateMessageHeader
|
||||||
import org.briarproject.briar.api.messaging.event.PrivateMessageReceivedEvent
|
import org.briarproject.briar.api.messaging.event.PrivateMessageReceivedEvent
|
||||||
import org.briarproject.briar.headless.ControllerTest
|
import org.briarproject.briar.headless.ControllerTest
|
||||||
import org.briarproject.briar.headless.event.WebSocketController
|
|
||||||
import org.briarproject.briar.headless.event.output
|
import org.briarproject.briar.headless.event.output
|
||||||
import org.briarproject.briar.headless.json.JsonDict
|
import org.briarproject.briar.headless.json.JsonDict
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
@@ -35,7 +34,6 @@ internal class MessagingControllerImplTest : ControllerTest() {
|
|||||||
private val messagingManager = mockk<MessagingManager>()
|
private val messagingManager = mockk<MessagingManager>()
|
||||||
private val conversationManager = mockk<ConversationManager>()
|
private val conversationManager = mockk<ConversationManager>()
|
||||||
private val privateMessageFactory = mockk<PrivateMessageFactory>()
|
private val privateMessageFactory = mockk<PrivateMessageFactory>()
|
||||||
private val webSocketController = mockk<WebSocketController>()
|
|
||||||
private val dbExecutor = ImmediateExecutor()
|
private val dbExecutor = ImmediateExecutor()
|
||||||
|
|
||||||
private val controller = MessagingControllerImpl(
|
private val controller = MessagingControllerImpl(
|
||||||
|
|||||||
Reference in New Issue
Block a user