mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-11 18:29:05 +01:00
[headless] expose contact connected state to REST API
This commit is contained in:
@@ -68,7 +68,8 @@ Returns a JSON array of contacts:
|
|||||||
"alias" : "A local nickname",
|
"alias" : "A local nickname",
|
||||||
"handshakePublicKey": "XnYRd7a7E4CTqgAvh4hCxh/YZ0EPscxknB9ZcEOpSzY=",
|
"handshakePublicKey": "XnYRd7a7E4CTqgAvh4hCxh/YZ0EPscxknB9ZcEOpSzY=",
|
||||||
"verified": true,
|
"verified": true,
|
||||||
"lastChatActivity": 1557838312175
|
"lastChatActivity": 1557838312175,
|
||||||
|
"connected": false
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -392,3 +393,19 @@ will no longer work on making this `pendingContact` become `contact`.
|
|||||||
"type": "event"
|
"type": "event"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### A contact connected or disconnected
|
||||||
|
|
||||||
|
When Briar establishes a connection to a contact (the contact comes online),
|
||||||
|
it sends a `ContactConnectedEvent`.
|
||||||
|
When the last connection is lost (the contact goes offline), it sends a `ContactDisconnectedEvent`.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"contactId": 1
|
||||||
|
},
|
||||||
|
"name": "ContactConnectedEvent",
|
||||||
|
"type": "event"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
|
|||||||
import io.javalin.http.BadRequestResponse
|
import io.javalin.http.BadRequestResponse
|
||||||
import io.javalin.http.Context
|
import io.javalin.http.Context
|
||||||
import io.javalin.http.NotFoundResponse
|
import io.javalin.http.NotFoundResponse
|
||||||
|
import org.briarproject.bramble.api.connection.ConnectionRegistry
|
||||||
import org.briarproject.bramble.api.contact.ContactManager
|
import org.briarproject.bramble.api.contact.ContactManager
|
||||||
import org.briarproject.bramble.api.contact.HandshakeLinkConstants.LINK_REGEX
|
import org.briarproject.bramble.api.contact.HandshakeLinkConstants.LINK_REGEX
|
||||||
import org.briarproject.bramble.api.contact.PendingContactId
|
import org.briarproject.bramble.api.contact.PendingContactId
|
||||||
@@ -16,6 +17,8 @@ import org.briarproject.bramble.api.db.NoSuchPendingContactException
|
|||||||
import org.briarproject.bramble.api.event.Event
|
import org.briarproject.bramble.api.event.Event
|
||||||
import org.briarproject.bramble.api.event.EventListener
|
import org.briarproject.bramble.api.event.EventListener
|
||||||
import org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH
|
import org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH
|
||||||
|
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent
|
||||||
|
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent
|
||||||
import org.briarproject.bramble.util.StringUtils.toUtf8
|
import org.briarproject.bramble.util.StringUtils.toUtf8
|
||||||
import org.briarproject.briar.api.conversation.ConversationManager
|
import org.briarproject.briar.api.conversation.ConversationManager
|
||||||
import org.briarproject.briar.headless.event.WebSocketController
|
import org.briarproject.briar.headless.event.WebSocketController
|
||||||
@@ -32,6 +35,8 @@ internal const val EVENT_CONTACT_ADDED = "ContactAddedEvent"
|
|||||||
internal const val EVENT_PENDING_CONTACT_STATE_CHANGED = "PendingContactStateChangedEvent"
|
internal const val EVENT_PENDING_CONTACT_STATE_CHANGED = "PendingContactStateChangedEvent"
|
||||||
internal const val EVENT_PENDING_CONTACT_ADDED = "PendingContactAddedEvent"
|
internal const val EVENT_PENDING_CONTACT_ADDED = "PendingContactAddedEvent"
|
||||||
internal const val EVENT_PENDING_CONTACT_REMOVED = "PendingContactRemovedEvent"
|
internal const val EVENT_PENDING_CONTACT_REMOVED = "PendingContactRemovedEvent"
|
||||||
|
internal const val EVENT_CONTACT_CONNECTED = "ContactConnectedEvent"
|
||||||
|
internal const val EVENT_CONTACT_DISCONNECTED = "ContactDisconnectedEvent"
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@Singleton
|
@Singleton
|
||||||
@@ -41,7 +46,8 @@ constructor(
|
|||||||
private val contactManager: ContactManager,
|
private val contactManager: ContactManager,
|
||||||
private val conversationManager: ConversationManager,
|
private val conversationManager: ConversationManager,
|
||||||
private val objectMapper: ObjectMapper,
|
private val objectMapper: ObjectMapper,
|
||||||
private val webSocket: WebSocketController
|
private val webSocket: WebSocketController,
|
||||||
|
private val connectionRegistry: ConnectionRegistry
|
||||||
) : ContactController, EventListener {
|
) : ContactController, EventListener {
|
||||||
|
|
||||||
override fun eventOccurred(e: Event) = when (e) {
|
override fun eventOccurred(e: Event) = when (e) {
|
||||||
@@ -57,6 +63,12 @@ constructor(
|
|||||||
is PendingContactRemovedEvent -> {
|
is PendingContactRemovedEvent -> {
|
||||||
webSocket.sendEvent(EVENT_PENDING_CONTACT_REMOVED, e.output())
|
webSocket.sendEvent(EVENT_PENDING_CONTACT_REMOVED, e.output())
|
||||||
}
|
}
|
||||||
|
is ContactConnectedEvent -> {
|
||||||
|
webSocket.sendEvent(EVENT_CONTACT_CONNECTED, e.output())
|
||||||
|
}
|
||||||
|
is ContactDisconnectedEvent -> {
|
||||||
|
webSocket.sendEvent(EVENT_CONTACT_DISCONNECTED, e.output())
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,7 +76,8 @@ constructor(
|
|||||||
override fun list(ctx: Context): Context {
|
override fun list(ctx: Context): Context {
|
||||||
val contacts = contactManager.contacts.map { contact ->
|
val contacts = contactManager.contacts.map { contact ->
|
||||||
val latestMsgTime = conversationManager.getGroupCount(contact.id).latestMsgTime
|
val latestMsgTime = conversationManager.getGroupCount(contact.id).latestMsgTime
|
||||||
contact.output(latestMsgTime)
|
val connected = connectionRegistry.isConnected(contact.id)
|
||||||
|
contact.output(latestMsgTime, connected)
|
||||||
}
|
}
|
||||||
return ctx.json(contacts)
|
return ctx.json(contacts)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,15 +2,17 @@ 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.ContactAddedEvent
|
import org.briarproject.bramble.api.contact.event.ContactAddedEvent
|
||||||
|
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent
|
||||||
|
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent
|
||||||
import org.briarproject.bramble.identity.output
|
import org.briarproject.bramble.identity.output
|
||||||
import org.briarproject.briar.api.conversation.ConversationManager
|
|
||||||
import org.briarproject.briar.headless.json.JsonDict
|
import org.briarproject.briar.headless.json.JsonDict
|
||||||
|
|
||||||
internal fun Contact.output(latestMsgTime: Long) = JsonDict(
|
internal fun Contact.output(latestMsgTime: Long, connected: Boolean) = JsonDict(
|
||||||
"contactId" to id.int,
|
"contactId" to id.int,
|
||||||
"author" to author.output(),
|
"author" to author.output(),
|
||||||
"verified" to isVerified,
|
"verified" to isVerified,
|
||||||
"lastChatActivity" to latestMsgTime
|
"lastChatActivity" to latestMsgTime,
|
||||||
|
"connected" to connected
|
||||||
).apply {
|
).apply {
|
||||||
alias?.let { put("alias", it) }
|
alias?.let { put("alias", it) }
|
||||||
handshakePublicKey?.let { put("handshakePublicKey", it.encoded) }
|
handshakePublicKey?.let { put("handshakePublicKey", it.encoded) }
|
||||||
@@ -20,3 +22,11 @@ internal fun ContactAddedEvent.output() = JsonDict(
|
|||||||
"contactId" to contactId.int,
|
"contactId" to contactId.int,
|
||||||
"verified" to isVerified
|
"verified" to isVerified
|
||||||
)
|
)
|
||||||
|
|
||||||
|
internal fun ContactConnectedEvent.output() = JsonDict(
|
||||||
|
"contactId" to contactId.int
|
||||||
|
)
|
||||||
|
|
||||||
|
internal fun ContactDisconnectedEvent.output() = JsonDict(
|
||||||
|
"contactId" to contactId.int
|
||||||
|
)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
|
|||||||
import io.javalin.http.Context
|
import io.javalin.http.Context
|
||||||
import io.javalin.http.util.ContextUtil
|
import io.javalin.http.util.ContextUtil
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
|
import org.briarproject.bramble.api.connection.ConnectionRegistry
|
||||||
import org.briarproject.bramble.api.contact.Contact
|
import org.briarproject.bramble.api.contact.Contact
|
||||||
import org.briarproject.bramble.api.contact.ContactManager
|
import org.briarproject.bramble.api.contact.ContactManager
|
||||||
import org.briarproject.bramble.api.identity.Author
|
import org.briarproject.bramble.api.identity.Author
|
||||||
@@ -26,6 +27,7 @@ abstract class ControllerTest {
|
|||||||
protected val contactManager = mockk<ContactManager>()
|
protected val contactManager = mockk<ContactManager>()
|
||||||
protected val conversationManager = mockk<ConversationManager>()
|
protected val conversationManager = mockk<ConversationManager>()
|
||||||
protected val identityManager = mockk<IdentityManager>()
|
protected val identityManager = mockk<IdentityManager>()
|
||||||
|
protected val connectionRegistry = mockk<ConnectionRegistry>()
|
||||||
protected val clock = mockk<Clock>()
|
protected val clock = mockk<Clock>()
|
||||||
protected val ctx = mockk<Context>()
|
protected val ctx = mockk<Context>()
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEven
|
|||||||
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.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH
|
import org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH
|
||||||
|
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent
|
||||||
|
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent
|
||||||
import org.briarproject.bramble.identity.output
|
import org.briarproject.bramble.identity.output
|
||||||
import org.briarproject.bramble.test.TestUtils.getPendingContact
|
import org.briarproject.bramble.test.TestUtils.getPendingContact
|
||||||
import org.briarproject.bramble.test.TestUtils.getRandomBytes
|
import org.briarproject.bramble.test.TestUtils.getRandomBytes
|
||||||
@@ -29,6 +31,7 @@ import org.briarproject.briar.headless.json.JsonDict
|
|||||||
import org.junit.jupiter.api.Assertions.assertNotNull
|
import org.junit.jupiter.api.Assertions.assertNotNull
|
||||||
import org.junit.jupiter.api.Assertions.assertThrows
|
import org.junit.jupiter.api.Assertions.assertThrows
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
internal class ContactControllerTest : ControllerTest() {
|
internal class ContactControllerTest : ControllerTest() {
|
||||||
|
|
||||||
@@ -38,7 +41,8 @@ internal class ContactControllerTest : ControllerTest() {
|
|||||||
contactManager,
|
contactManager,
|
||||||
conversationManager,
|
conversationManager,
|
||||||
objectMapper,
|
objectMapper,
|
||||||
webSocketController
|
webSocketController,
|
||||||
|
connectionRegistry
|
||||||
)
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -50,9 +54,11 @@ internal class ContactControllerTest : ControllerTest() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testList() {
|
fun testList() {
|
||||||
|
val connected = Random.nextBoolean()
|
||||||
every { contactManager.contacts } returns listOf(contact)
|
every { contactManager.contacts } returns listOf(contact)
|
||||||
every { conversationManager.getGroupCount(contact.id).latestMsgTime } returns timestamp
|
every { conversationManager.getGroupCount(contact.id).latestMsgTime } returns timestamp
|
||||||
every { ctx.json(listOf(contact.output(timestamp))) } returns ctx
|
every { connectionRegistry.isConnected(contact.id) } returns connected
|
||||||
|
every { ctx.json(listOf(contact.output(timestamp, connected))) } returns ctx
|
||||||
controller.list(ctx)
|
controller.list(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,8 +273,37 @@ internal class ContactControllerTest : ControllerTest() {
|
|||||||
controller.eventOccurred(event)
|
controller.eventOccurred(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testContactConnectedEvent() {
|
||||||
|
val event = ContactConnectedEvent(contact.id)
|
||||||
|
|
||||||
|
every {
|
||||||
|
webSocketController.sendEvent(
|
||||||
|
EVENT_CONTACT_CONNECTED,
|
||||||
|
event.output()
|
||||||
|
)
|
||||||
|
} just runs
|
||||||
|
|
||||||
|
controller.eventOccurred(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testContactDisconnectedEvent() {
|
||||||
|
val event = ContactDisconnectedEvent(contact.id)
|
||||||
|
|
||||||
|
every {
|
||||||
|
webSocketController.sendEvent(
|
||||||
|
EVENT_CONTACT_DISCONNECTED,
|
||||||
|
event.output()
|
||||||
|
)
|
||||||
|
} just runs
|
||||||
|
|
||||||
|
controller.eventOccurred(event)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testOutputContact() {
|
fun testOutputContact() {
|
||||||
|
val connected = Random.nextBoolean()
|
||||||
assertNotNull(contact.handshakePublicKey)
|
assertNotNull(contact.handshakePublicKey)
|
||||||
val json = """
|
val json = """
|
||||||
{
|
{
|
||||||
@@ -277,10 +312,11 @@ internal class ContactControllerTest : ControllerTest() {
|
|||||||
"alias" : "${contact.alias}",
|
"alias" : "${contact.alias}",
|
||||||
"handshakePublicKey": ${toJson(contact.handshakePublicKey!!.encoded)},
|
"handshakePublicKey": ${toJson(contact.handshakePublicKey!!.encoded)},
|
||||||
"verified": ${contact.isVerified},
|
"verified": ${contact.isVerified},
|
||||||
"lastChatActivity": $timestamp
|
"lastChatActivity": $timestamp,
|
||||||
|
"connected": $connected
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
assertJsonEquals(json, contact.output(timestamp))
|
assertJsonEquals(json, contact.output(timestamp, connected))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -358,4 +394,26 @@ internal class ContactControllerTest : ControllerTest() {
|
|||||||
assertJsonEquals(json, event.output())
|
assertJsonEquals(json, event.output())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testOutputContactConnectedEvent() {
|
||||||
|
val event = ContactConnectedEvent(contact.id)
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"contactId": ${contact.id.int}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
assertJsonEquals(json, event.output())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testOutputContactDisconnectedEvent() {
|
||||||
|
val event = ContactDisconnectedEvent(contact.id)
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"contactId": ${contact.id.int}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
assertJsonEquals(json, event.output())
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user