mirror of
https://github.com/corda/corda.git
synced 2024-12-18 20:47:57 +00:00
CORDA-880 Make contractsdsl java interop functions behave same as inline functions (#2275)
* Make java interop functions same as inline functions and add tests * Fixing docs * Move unspecifiedCountry to internal. (#2274) * Review changes * Call java interop functions from inline functions * Use correct test assertion
This commit is contained in:
parent
313d21f068
commit
cb703476a0
@ -12,7 +12,6 @@ import java.util.*
|
||||
* Defines a simple domain specific language for the specification of financial contracts. Currently covers:
|
||||
*
|
||||
* - Some utilities for working with commands.
|
||||
* - An Amount type that represents a positive quantity of a specific token.
|
||||
* - A simple language extension for specifying requirements in English, along with logic to enforce them.
|
||||
*/
|
||||
|
||||
@ -30,37 +29,44 @@ inline fun <R> requireThat(body: Requirements.() -> R) = Requirements.body()
|
||||
|
||||
//// Authenticated commands ///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// TODO: Provide a version of select that interops with Java
|
||||
|
||||
/** Filters the command list by type, party and public key all at once. */
|
||||
inline fun <reified T : CommandData> Collection<CommandWithParties<CommandData>>.select(signer: PublicKey? = null,
|
||||
party: AbstractParty? = null) =
|
||||
filter { it.value is T }.
|
||||
select(T::class.java, signer, party)
|
||||
|
||||
/** Filters the command list by type, party and public key all at once. */
|
||||
fun <C : CommandData> Collection<CommandWithParties<CommandData>>.select(klass: Class<C>,
|
||||
signer: PublicKey? = null,
|
||||
party: AbstractParty? = null) =
|
||||
mapNotNull { if (klass.isInstance(it.value)) uncheckedCast<CommandWithParties<CommandData>, CommandWithParties<C>>(it) else null }.
|
||||
filter { if (signer == null) true else signer in it.signers }.
|
||||
filter { if (party == null) true else party in it.signingParties }.
|
||||
map { CommandWithParties(it.signers, it.signingParties, it.value as T) }
|
||||
|
||||
// TODO: Provide a version of select that interops with Java
|
||||
map { CommandWithParties(it.signers, it.signingParties, it.value) }
|
||||
|
||||
/** Filters the command list by type, parties and public keys all at once. */
|
||||
inline fun <reified T : CommandData> Collection<CommandWithParties<CommandData>>.select(signers: Collection<PublicKey>?,
|
||||
parties: Collection<Party>?) =
|
||||
filter { it.value is T }.
|
||||
select(T::class.java, signers, parties)
|
||||
|
||||
/** Filters the command list by type, parties and public keys all at once. */
|
||||
fun <C : CommandData> Collection<CommandWithParties<CommandData>>.select(klass: Class<C>,
|
||||
signers: Collection<PublicKey>?,
|
||||
parties: Collection<Party>?) =
|
||||
mapNotNull { if (klass.isInstance(it.value)) uncheckedCast<CommandWithParties<CommandData>, CommandWithParties<C>>(it) else null }.
|
||||
filter { if (signers == null) true else it.signers.containsAll(signers) }.
|
||||
filter { if (parties == null) true else it.signingParties.containsAll(parties) }.
|
||||
map { CommandWithParties(it.signers, it.signingParties, it.value as T) }
|
||||
map { CommandWithParties(it.signers, it.signingParties, it.value) }
|
||||
|
||||
/** Ensures that a transaction has only one command that is of the given type, otherwise throws an exception. */
|
||||
inline fun <reified T : CommandData> Collection<CommandWithParties<CommandData>>.requireSingleCommand() = try {
|
||||
select<T>().single()
|
||||
inline fun <reified T : CommandData> Collection<CommandWithParties<CommandData>>.requireSingleCommand() = requireSingleCommand(T::class.java)
|
||||
|
||||
/** Ensures that a transaction has only one command that is of the given type, otherwise throws an exception. */
|
||||
fun <C : CommandData> Collection<CommandWithParties<CommandData>>.requireSingleCommand(klass: Class<C>) = try {
|
||||
select(klass).single()
|
||||
} catch (e: NoSuchElementException) {
|
||||
throw IllegalStateException("Required ${T::class.qualifiedName} command") // Better error message.
|
||||
throw IllegalStateException("Required ${klass.kotlin.qualifiedName} command") // Better error message.
|
||||
}
|
||||
|
||||
/** Ensures that a transaction has only one command that is of the given type, otherwise throws an exception. */
|
||||
fun <C : CommandData> Collection<CommandWithParties<CommandData>>.requireSingleCommand(klass: Class<C>) =
|
||||
mapNotNull { if (klass.isInstance(it.value)) uncheckedCast<CommandWithParties<CommandData>, CommandWithParties<C>>(it) else null }.single()
|
||||
|
||||
/**
|
||||
* Simple functionality for verifying a move command. Verifies that each input has a signature from its owning key.
|
||||
*
|
||||
|
@ -0,0 +1,179 @@
|
||||
package net.corda.core.contracts
|
||||
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.testing.TestIdentity
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
import java.security.PublicKey
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class ContractsDSLTests {
|
||||
class UnwantedCommand : CommandData
|
||||
|
||||
interface TestCommands : CommandData {
|
||||
class CommandOne : TypeOnlyCommandData(), TestCommands
|
||||
class CommandTwo : TypeOnlyCommandData(), TestCommands
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB"))
|
||||
val miniCorp = TestIdentity(CordaX500Name("MiniCorp", "London", "GB"))
|
||||
|
||||
val validCommandOne = CommandWithParties(listOf(megaCorp.publicKey, miniCorp.publicKey), listOf(megaCorp.party, miniCorp.party), TestCommands.CommandOne())
|
||||
val validCommandTwo = CommandWithParties(listOf(megaCorp.publicKey), listOf(megaCorp.party), TestCommands.CommandTwo())
|
||||
val invalidCommand = CommandWithParties(emptyList(), emptyList(), UnwantedCommand())
|
||||
}
|
||||
|
||||
@RunWith(Parameterized::class)
|
||||
class RequireSingleCommandTests(private val testFunction: (Collection<CommandWithParties<CommandData>>) -> CommandWithParties<CommandData>, description: String) {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@Parameterized.Parameters(name = "{1}")
|
||||
fun data(): Collection<Array<Any>> = listOf(
|
||||
arrayOf<Any>({ commands: Collection<CommandWithParties<CommandData>> -> commands.requireSingleCommand<TestCommands>() }, "Inline version"),
|
||||
arrayOf<Any>({ commands: Collection<CommandWithParties<CommandData>> -> commands.requireSingleCommand(TestCommands::class.java) }, "Interop version")
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check function returns one value`() {
|
||||
val commands = listOf(validCommandOne, invalidCommand)
|
||||
val returnedCommand = testFunction(commands)
|
||||
assertEquals(returnedCommand, validCommandOne, "they should be the same")
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException::class)
|
||||
fun `check error is thrown if more than one valid command`() {
|
||||
val commands = listOf(validCommandOne, validCommandTwo)
|
||||
testFunction(commands)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check error is thrown when command is of wrong type`() {
|
||||
val commands = listOf(invalidCommand)
|
||||
Assertions.assertThatThrownBy { testFunction(commands) }
|
||||
.isInstanceOf(IllegalStateException::class.java)
|
||||
.hasMessage("Required net.corda.core.contracts.ContractsDSLTests.TestCommands command")
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(Parameterized::class)
|
||||
class SelectWithSingleInputsTests(private val testFunction: (Collection<CommandWithParties<CommandData>>, PublicKey?, AbstractParty?) -> Iterable<CommandWithParties<CommandData>>, description: String) {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@Parameterized.Parameters(name = "{1}")
|
||||
fun data(): Collection<Array<Any>> = listOf(
|
||||
arrayOf<Any>({ commands: Collection<CommandWithParties<CommandData>>, signer: PublicKey?, party: AbstractParty? -> commands.select<TestCommands>(signer, party) }, "Inline version"),
|
||||
arrayOf<Any>({ commands: Collection<CommandWithParties<CommandData>>, signer: PublicKey?, party: AbstractParty? -> commands.select(TestCommands::class.java, signer, party) }, "Interop version")
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check that function returns all values`() {
|
||||
val commands = listOf(validCommandOne, validCommandTwo)
|
||||
testFunction(commands, null, null)
|
||||
assertEquals(2, commands.size)
|
||||
assertTrue(commands.contains(validCommandOne))
|
||||
assertTrue(commands.contains(validCommandTwo))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check that function does not return invalid command types`() {
|
||||
val commands = listOf(validCommandOne, invalidCommand)
|
||||
val filteredCommands = testFunction(commands, null, null).toList()
|
||||
assertEquals(1, filteredCommands.size)
|
||||
assertTrue(filteredCommands.contains(validCommandOne))
|
||||
assertFalse(filteredCommands.contains(invalidCommand))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check that function returns commands from valid signers`() {
|
||||
val commands = listOf(validCommandOne, validCommandTwo)
|
||||
val filteredCommands = testFunction(commands, miniCorp.publicKey, null).toList()
|
||||
assertEquals(1, filteredCommands.size)
|
||||
assertTrue(filteredCommands.contains(validCommandOne))
|
||||
assertFalse(filteredCommands.contains(validCommandTwo))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check that function returns commands from valid parties`() {
|
||||
val commands = listOf(validCommandOne, validCommandTwo)
|
||||
val filteredCommands = testFunction(commands, null, miniCorp.party).toList()
|
||||
assertEquals(1, filteredCommands.size)
|
||||
assertTrue(filteredCommands.contains(validCommandOne))
|
||||
assertFalse(filteredCommands.contains(validCommandTwo))
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(Parameterized::class)
|
||||
class SelectWithMultipleInputsTests(private val testFunction: (Collection<CommandWithParties<CommandData>>, Collection<PublicKey>?, Collection<Party>?) -> Iterable<CommandWithParties<CommandData>>, description: String) {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@Parameterized.Parameters(name = "{1}")
|
||||
fun data(): Collection<Array<Any>> = listOf(
|
||||
arrayOf<Any>({ commands: Collection<CommandWithParties<CommandData>>, signers: Collection<PublicKey>?, party: Collection<Party>? -> commands.select<TestCommands>(signers, party) }, "Inline version"),
|
||||
arrayOf<Any>({ commands: Collection<CommandWithParties<CommandData>>, signers: Collection<PublicKey>?, party: Collection<Party>? -> commands.select(TestCommands::class.java, signers, party) }, "Interop version")
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check that function returns all values`() {
|
||||
val commands = listOf(validCommandOne, validCommandTwo)
|
||||
testFunction(commands, null, null)
|
||||
assertEquals(2, commands.size)
|
||||
assertTrue(commands.contains(validCommandOne))
|
||||
assertTrue(commands.contains(validCommandTwo))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check that function does not return invalid command types`() {
|
||||
val commands = listOf(validCommandOne, invalidCommand)
|
||||
val filteredCommands = testFunction(commands, null, null).toList()
|
||||
assertEquals(1, filteredCommands.size)
|
||||
assertTrue(filteredCommands.contains(validCommandOne))
|
||||
assertFalse(filteredCommands.contains(invalidCommand))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check that function returns commands from valid signers`() {
|
||||
val commands = listOf(validCommandOne, validCommandTwo)
|
||||
val filteredCommands = testFunction(commands, listOf(megaCorp.publicKey), null).toList()
|
||||
assertEquals(2, filteredCommands.size)
|
||||
assertTrue(filteredCommands.contains(validCommandOne))
|
||||
assertTrue(filteredCommands.contains(validCommandTwo))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check that function returns commands from all valid signers`() {
|
||||
val commands = listOf(validCommandOne, validCommandTwo)
|
||||
val filteredCommands = testFunction(commands, listOf(miniCorp.publicKey, megaCorp.publicKey), null).toList()
|
||||
assertEquals(1, filteredCommands.size)
|
||||
assertTrue(filteredCommands.contains(validCommandOne))
|
||||
assertFalse(filteredCommands.contains(validCommandTwo))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check that function returns commands from valid parties`() {
|
||||
val commands = listOf(validCommandOne, validCommandTwo)
|
||||
val filteredCommands = testFunction(commands, null, listOf(megaCorp.party)).toList()
|
||||
assertEquals(2, filteredCommands.size)
|
||||
assertTrue(filteredCommands.contains(validCommandOne))
|
||||
assertTrue(filteredCommands.contains(validCommandTwo))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check that function returns commands from all valid parties`() {
|
||||
val commands = listOf(validCommandOne, validCommandTwo)
|
||||
val filteredCommands = testFunction(commands, null, listOf(miniCorp.party, megaCorp.party)).toList()
|
||||
assertEquals(1, filteredCommands.size)
|
||||
assertTrue(filteredCommands.contains(validCommandOne))
|
||||
assertFalse(filteredCommands.contains(validCommandTwo))
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user