diff --git a/node/src/main/kotlin/net/corda/node/shell/InteractiveShell.kt b/node/src/main/kotlin/net/corda/node/shell/InteractiveShell.kt index 8a84d9a354..23c67e9c9b 100644 --- a/node/src/main/kotlin/net/corda/node/shell/InteractiveShell.kt +++ b/node/src/main/kotlin/net/corda/node/shell/InteractiveShell.kt @@ -9,6 +9,8 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLFactory import com.google.common.io.Closeables import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.SettableFuture +import net.corda.core.* +import net.corda.core.contracts.UniqueIdentifier import net.corda.core.flows.FlowInitiator import net.corda.core.flows.FlowLogic import net.corda.core.internal.* @@ -182,6 +184,8 @@ object InteractiveShell { JacksonSupport.createInMemoryMapper(node.services.identityService, YAMLFactory(), true).apply { val rpcModule = SimpleModule() rpcModule.addDeserializer(InputStream::class.java, InputStreamDeserializer) + rpcModule.addDeserializer(UniqueIdentifier::class.java, UniqueIdentifierDeserializer) + rpcModule.addDeserializer(UUID::class.java, UUIDDeserializer) registerModule(rpcModule) } } @@ -497,5 +501,37 @@ object InteractiveShell { } } + /** + * String value deserialized to [UniqueIdentifier]. + * Any string value used as [UniqueIdentifier.externalId]. + * If string contains underscore(i.e. externalId_uuid) then split with it. + * Index 0 as [UniqueIdentifier.externalId] + * Index 1 as [UniqueIdentifier.id] + * */ + object UniqueIdentifierDeserializer : JsonDeserializer() { + override fun deserialize(p: JsonParser, ctxt: DeserializationContext): UniqueIdentifier { + //Check if externalId and UUID may be separated by underscore. + if (p.text.contains("_")) { + val ids = p.text.split("_") + //Create UUID object from string. + val uuid: UUID = UUID.fromString(ids[1]) + //Create UniqueIdentifier object using externalId and UUID. + return UniqueIdentifier(ids[0], uuid) + } + //Any other string used as externalId. + return UniqueIdentifier(p.text) + } + } + + /** + * String value deserialized to [UUID]. + * */ + object UUIDDeserializer : JsonDeserializer() { + override fun deserialize(p: JsonParser, ctxt: DeserializationContext): UUID { + //Create UUID object from string. + return UUID.fromString(p.text) + } + } + //endregion } diff --git a/node/src/test/kotlin/net/corda/node/shell/CustomTypeJsonParsingTests.kt b/node/src/test/kotlin/net/corda/node/shell/CustomTypeJsonParsingTests.kt new file mode 100644 index 0000000000..1622f839f8 --- /dev/null +++ b/node/src/test/kotlin/net/corda/node/shell/CustomTypeJsonParsingTests.kt @@ -0,0 +1,71 @@ +package net.corda.node.shell + +import com.fasterxml.jackson.databind.JsonMappingException +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.module.SimpleModule +import com.fasterxml.jackson.module.kotlin.readValue +import net.corda.core.contracts.UniqueIdentifier +import org.junit.Before +import org.junit.Test +import java.util.* +import kotlin.test.assertEquals + +class CustomTypeJsonParsingTests { + lateinit var objectMapper: ObjectMapper + + //Dummy classes for testing. + data class State(val linearId: UniqueIdentifier) { + constructor() : this(UniqueIdentifier("required-for-json-deserializer")) + } + + data class UuidState(val uuid: UUID) { + //Default constructor required for json deserializer. + constructor() : this(UUID.randomUUID()) + } + + @Before + fun setup() { + objectMapper = ObjectMapper() + val simpleModule = SimpleModule() + simpleModule.addDeserializer(UniqueIdentifier::class.java, InteractiveShell.UniqueIdentifierDeserializer) + simpleModule.addDeserializer(UUID::class.java, InteractiveShell.UUIDDeserializer) + objectMapper.registerModule(simpleModule) + } + + @Test + fun `Deserializing UniqueIdentifier by parsing string`() { + val json = """{"linearId":"26b37265-a1fd-4c77-b2e0-715917ef619f"}""" + val state = objectMapper.readValue(json) + + assertEquals("26b37265-a1fd-4c77-b2e0-715917ef619f", state.linearId.externalId) + } + + @Test + fun `Deserializing UniqueIdentifier by parsing string with underscore`() { + val json = """{"linearId":"extkey564_26b37265-a1fd-4c77-b2e0-715917ef619f"}""" + val state = objectMapper.readValue(json) + + assertEquals("extkey564", state.linearId.externalId) + assertEquals("26b37265-a1fd-4c77-b2e0-715917ef619f", state.linearId.id.toString()) + } + + @Test(expected = JsonMappingException::class) + fun `Deserializing by parsing string contain invalid uuid with underscore`() { + val json = """{"linearId":"extkey564_26b37265-a1fd-4c77-b2e0"}""" + objectMapper.readValue(json) + } + + @Test + fun `Deserializing UUID by parsing string`() { + val json = """{"uuid":"26b37265-a1fd-4c77-b2e0-715917ef619f"}""" + val state = objectMapper.readValue(json) + + assertEquals("26b37265-a1fd-4c77-b2e0-715917ef619f", state.uuid.toString()) + } + + @Test(expected = JsonMappingException::class) + fun `Deserializing UUID by parsing invalid uuid string`() { + val json = """{"uuid":"26b37265-a1fd-4c77-b2e0"}""" + objectMapper.readValue(json) + } +} \ No newline at end of file