Skip to content

Commit

Permalink
[#5] Add support for DIDComm Out Of Band Messages
Browse files Browse the repository at this point in the history
  • Loading branch information
tdiesler committed Dec 21, 2022
1 parent db3031f commit 1458cfe
Show file tree
Hide file tree
Showing 5 changed files with 212 additions and 0 deletions.
30 changes: 30 additions & 0 deletions core/src/main/kotlin/org/nessus/didcomm/model/MessageParser.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.nessus.didcomm.model

import com.google.gson.Gson
import org.didcommx.didcomm.message.Message

/**
* Parses a JSON string to a DIDComm Message
*/
class MessageParser {

companion object {
private val gson = Gson()

fun fromJson(json: String) : Message {
val jsonMap: MutableMap<String, Any> = mutableMapOf()
gson.fromJson(json, Map::class.java).forEach { en ->
val enval = en.value!!
when(val key: String = en.key.toString()) {
"created_time" -> jsonMap[key] = (enval as Double).toLong()
"expires_time" -> jsonMap[key] = (enval as Double).toLong()
"custom_headers" -> if ((enval as Map<String, Any>).isNotEmpty()) {
jsonMap[key] = enval
}
else -> jsonMap[key] = enval
}
}
return Message.parse(jsonMap)
}
}
}
31 changes: 31 additions & 0 deletions core/src/main/kotlin/org/nessus/didcomm/model/MessageWriter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.nessus.didcomm.model

import com.google.gson.FieldNamingPolicy
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import org.didcommx.didcomm.message.Message

/**
* Serializes a DIDComm Message to JSON
*/
class MessageWriter {

companion object {
private val gson: Gson = GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create()
private val prettyGson: Gson = GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.setPrettyPrinting()
.create()

fun toJson(msg: Message, pretty: Boolean = false) : String {
return toJson(msg as Any, pretty)
}

fun toJson(obj: Any, pretty: Boolean = false) : String {
val gson = if (pretty) prettyGson else gson
return gson.toJson(obj)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package org.nessus.didcomm.model

import com.google.gson.Gson

/**
* [Out of Band Invitation]https://identity.foundation/didcomm-messaging/spec/#out-of-band-messages
*
* {
* "type": "https://didcomm.org/out-of-band/2.0/invitation",
* "id": "<id used for context as pthid>",
* "from":"<sender's did>",
* "body": {
* "goal_code": "issue-vc",
* "goal": "To issue a Faber College Graduate credential",
* "accept": [
* "didcomm/v2",
* "didcomm/aip2;env=rfc587"
* ],
* },
* "attachments": [
* {
* "id": "request-0",
* "mime_type": "application/json",
* "data": {
* "json": "<json of protocol message>"
* }
* }
* ]
* }
*/
data class OutOfBandInvitationV2(

/**
* Message ID. The id attribute value MUST be unique to the sender, across all messages they send.
* This value MUST be used as the parent thread ID (pthid) for the response message that follows.
* REQUIRED
*/
val id: String,

/**
* The DID representing the sender to be used by recipients for future interactions.
* REQUIRED
*/
val from: String,

/**
* A self-attested code the receiver may want to display to the user or use in automatically deciding what to do with the out-of-band message.
* OPTIONAL
*/
val goalCode: String?,

/**
* A self-attested string that the receiver may want to display to the user about the context-specific goal of the out-of-band message.
* OPTIONAL
*/
val goal: String?,

/**
* An array of media types in the order of preference for sending a message to the endpoint.
* These identify a profile of DIDComm Messaging that the endpoint supports.
* OPTIONAL
*/
val accept: List<String>?,

/**
* An array of attachments that will contain the invitation messages in order of preference that the receiver can use in responding to the message.
* Each message in the array is a rough equivalent of the others, and all are in pursuit of the stated goal and goal_code.
* Only one of the messages should be chosen and acted upon.
* OPTIONAL
*/
val attachments: List<Any>?,
) {

companion object {

/**
* The header conveying the DIDComm MTURI.
* REQUIRED
*/
const val type: String = "https://didcomm.org/out-of-band/2.0/invitation"

fun fromBody(body: Map<String, Any?>): OutOfBandInvitationV2 {
val gson = Gson()
return gson.fromJson(gson.toJson(body), OutOfBandInvitationV2::class.java)
}
}
}
30 changes: 30 additions & 0 deletions core/src/test/kotlin/org/nessus/didcomm/test/model/Fixtures.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.nessus.didcomm.test.model

import org.didcommx.didcomm.message.Attachment
import org.didcommx.didcomm.message.Message
import org.nessus.didcomm.model.OutOfBandInvitationV2

class OutOfBand {

companion object {

const val ALICE_DID = "did:example:alice"
const val FABER_DID = "did:example:faber"

private const val ID = "1234567890"
private const val TYPE = OutOfBandInvitationV2.type

val OUT_OF_BAND_INVITATION = Message.builder(ID, mapOf(
"goal_code" to "issue-vc",
"goal" to "To issue a Faber College Graduate credential",
"accept" to listOf("didcomm/v2", "didcomm/aip2;env=rfc587")),
TYPE)
.from(FABER_DID)
.createdTime(1516269022)
.expiresTime(1516385931)
.attachments(listOf(
Attachment.builder(
"request-0", Attachment.Data.parse(mapOf("base64" to "qwerty"))).build()))
.build()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.nessus.didcomm.test.model

import mu.KotlinLogging
import org.didcommx.didcomm.message.Message
import org.junit.jupiter.api.Test
import org.nessus.didcomm.model.MessageParser
import org.nessus.didcomm.model.MessageWriter
import org.nessus.didcomm.model.OutOfBandInvitationV2
import kotlin.test.Ignore
import kotlin.test.assertEquals

@Ignore
class OutOfBandInvitationTest {

private val log = KotlinLogging.logger {}

@Test
fun testOutOfBandInvitation() {

val exp: Message = OutOfBand.OUT_OF_BAND_INVITATION
val expBody = OutOfBandInvitationV2.fromBody(exp.body)
val expJson: String = MessageWriter.toJson(exp)
log.info("exp: {}", expJson)

val was = MessageParser.fromJson(expJson)
log.info("was: {}", MessageWriter.toJson(was))
assertEquals(exp, was)

val wasBody = OutOfBandInvitationV2.fromBody(was.body)
log.info("body: {}", MessageWriter.toJson(wasBody))
assertEquals(expBody, wasBody)
}

}

0 comments on commit 1458cfe

Please sign in to comment.