Fixed Vault Query over RPC using custom attributes. (#1066)

* Implemented Kryo custom serializers for Field and KProperty types.

* Adjusted KPropertySerializer to use kotlin member properties upon read() due to failing RPC tests.
Added additional Kotlin and Java tests (CordaRPCClient, StandaaloneCordaRPCClient)
Annotated schemas to be CordaSerializable (required when referencing as KProperty in custom queries).
Cleanup some outstanding compiler warnings.

* Added client RPC Java integration and smoke tests to build.

* Clean up compiler warnings in Java tests.

* Fixed incorrect assertion expectation.

* Backed out Field and KProperty custom serializers.

* Backed out Field and KProperty custom serializers.

* Store VaultQueryCustom custom column references as name and class (from Java Field and Kotlin KProperty1 types respectively).
Custom serialization of Field and KProperty type no longer required.

* Removed blank lines as per RP review comments.
This commit is contained in:
josecoll 2017-07-17 18:20:02 +01:00 committed by GitHub
parent 8596f1cc17
commit 65ce5fec4b
12 changed files with 315 additions and 50 deletions

View File

@ -24,6 +24,11 @@ sourceSets {
runtimeClasspath += main.output + test.output
srcDir file('src/integration-test/kotlin')
}
java {
compileClasspath += main.output + test.output
runtimeClasspath += main.output + test.output
srcDir file('src/integration-test/java')
}
}
smokeTest {
kotlin {
@ -33,6 +38,11 @@ sourceSets {
runtimeClasspath += main.output
srcDir file('src/smoke-test/kotlin')
}
java {
compileClasspath += main.output
runtimeClasspath += main.output
srcDir file('src/smoke-test/java')
}
}
}

View File

@ -0,0 +1,94 @@
package net.corda.client.rpc;
import com.google.common.util.concurrent.*;
import net.corda.client.rpc.internal.*;
import net.corda.contracts.asset.*;
import net.corda.core.contracts.*;
import net.corda.core.messaging.*;
import net.corda.core.node.services.*;
import net.corda.core.node.services.vault.*;
import net.corda.core.utilities.*;
import net.corda.flows.*;
import net.corda.node.internal.*;
import net.corda.node.services.transactions.*;
import net.corda.nodeapi.*;
import net.corda.schemas.*;
import net.corda.testing.node.*;
import org.junit.*;
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.*;
import static kotlin.test.AssertionsKt.*;
import static net.corda.client.rpc.CordaRPCClientConfiguration.*;
import static net.corda.node.services.RPCUserServiceKt.*;
import static net.corda.testing.TestConstants.*;
public class CordaRPCJavaClientTest extends NodeBasedTest {
private List<String> perms = Arrays.asList(startFlowPermission(CashPaymentFlow.class), startFlowPermission(CashIssueFlow.class));
private Set<String> permSet = new HashSet<>(perms);
private User rpcUser = new User("user1", "test", permSet);
private Node node;
private CordaRPCClient client;
private RPCClient.RPCConnection<CordaRPCOps> connection = null;
private CordaRPCOps rpcProxy;
private void login(String username, String password) {
connection = client.start(username, password);
rpcProxy = connection.getProxy();
}
@Before
public void setUp() throws ExecutionException, InterruptedException {
Set<ServiceInfo> services = new HashSet<>(Collections.singletonList(new ServiceInfo(ValidatingNotaryService.Companion.getType(), null)));
ListenableFuture<Node> nodeFuture = startNode(getALICE().getName(), 1, services, Arrays.asList(rpcUser), Collections.emptyMap());
node = nodeFuture.get();
client = new CordaRPCClient(node.getConfiguration().getRpcAddress(), null, getDefault());
}
@After
public void done() throws IOException {
connection.close();
}
@Test
public void testLogin() {
login(rpcUser.getUsername(), rpcUser.getPassword());
}
@Test
public void testCashBalances() throws NoSuchFieldException, ExecutionException, InterruptedException {
login(rpcUser.getUsername(), rpcUser.getPassword());
Amount<Currency> dollars123 = new Amount<>(123, Currency.getInstance("USD"));
FlowHandle<AbstractCashFlow.Result> flowHandle = rpcProxy.startFlowDynamic(CashIssueFlow.class,
dollars123, OpaqueBytes.of("1".getBytes()),
node.info.getLegalIdentity(), node.info.getLegalIdentity());
System.out.println("Started issuing cash, waiting on result");
flowHandle.getReturnValue().get();
Amount<Currency> balance = getBalance(Currency.getInstance("USD"));
System.out.print("Balance: " + balance + "\n");
assertEquals(dollars123, balance, "matching");
}
private Amount<Currency> getBalance(Currency currency) throws NoSuchFieldException {
Field pennies = CashSchemaV1.PersistentCashState.class.getDeclaredField("pennies");
@SuppressWarnings("unchecked")
QueryCriteria sumCriteria = new QueryCriteria.VaultCustomQueryCriteria(Builder.sum(pennies));
Vault.Page<Cash.State> results = rpcProxy.vaultQueryByCriteria(sumCriteria, Cash.State.class);
if (results.getOtherResults().isEmpty()) {
return new Amount<>(0L, currency);
} else {
Assert.assertNotNull(results.getOtherResults());
Long quantity = (Long) results.getOtherResults().get(0);
return new Amount<>(quantity, currency);
}
}
}

View File

@ -1,13 +1,17 @@
package net.corda.client.rpc
import net.corda.contracts.asset.Cash
import net.corda.core.contracts.Amount
import net.corda.core.contracts.DOLLARS
import net.corda.core.contracts.USD
import net.corda.core.crypto.random63BitValue
import net.corda.core.flows.FlowInitiator
import net.corda.core.getOrThrow
import net.corda.core.messaging.*
import net.corda.core.node.services.ServiceInfo
import net.corda.core.crypto.random63BitValue
import net.corda.core.node.services.vault.QueryCriteria
import net.corda.core.node.services.vault.builder
import net.corda.core.utilities.OpaqueBytes
import net.corda.testing.ALICE
import net.corda.flows.CashException
import net.corda.flows.CashIssueFlow
import net.corda.flows.CashPaymentFlow
@ -15,10 +19,13 @@ import net.corda.node.internal.Node
import net.corda.node.services.startFlowPermission
import net.corda.node.services.transactions.ValidatingNotaryService
import net.corda.nodeapi.User
import net.corda.schemas.CashSchemaV1
import net.corda.testing.ALICE
import net.corda.testing.node.NodeBasedTest
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
import org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.junit.After
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import java.util.*
@ -117,10 +124,23 @@ class CordaRPCClientTest : NodeBasedTest() {
println("Started issuing cash, waiting on result")
flowHandle.returnValue.get()
val finishCash = proxy.getCashBalances()
println("Cash Balances: $finishCash")
assertEquals(1, finishCash.size)
assertEquals(123.DOLLARS, finishCash.get(Currency.getInstance("USD")))
val cashDollars = getBalance(USD, proxy)
println("Balance: $cashDollars")
assertEquals(123.DOLLARS, cashDollars)
}
private fun getBalance(currency: Currency, proxy: CordaRPCOps): Amount<Currency> {
val sum = builder { CashSchemaV1.PersistentCashState::pennies.sum() }
val sumCriteria = QueryCriteria.VaultCustomQueryCriteria(sum)
val results = proxy.vaultQueryBy<Cash.State>(sumCriteria)
if (results.otherResults.isEmpty()) {
return Amount(0L, currency)
} else {
Assert.assertNotNull(results.otherResults)
val quantity = results.otherResults[0] as Long
return Amount(quantity, currency)
}
}
@Test

View File

@ -0,0 +1,99 @@
package net.corda.java.rpc;
import net.corda.client.rpc.*;
import net.corda.contracts.asset.*;
import net.corda.core.contracts.*;
import net.corda.core.messaging.*;
import net.corda.core.node.*;
import net.corda.core.node.services.*;
import net.corda.core.node.services.vault.*;
import net.corda.core.utilities.*;
import net.corda.flows.*;
import net.corda.nodeapi.*;
import net.corda.schemas.*;
import net.corda.smoketesting.*;
import org.bouncycastle.asn1.x500.*;
import org.junit.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import static kotlin.test.AssertionsKt.assertEquals;
public class StandaloneCordaRPCJavaClientTest {
private List<String> perms = Collections.singletonList("ALL");
private Set<String> permSet = new HashSet<>(perms);
private User rpcUser = new User("user1", "test", permSet);
private AtomicInteger port = new AtomicInteger(15000);
private NodeProcess notary;
private CordaRPCOps rpcProxy;
private CordaRPCConnection connection;
private NodeInfo notaryNode;
private NodeConfig notaryConfig = new NodeConfig(
new X500Name("CN=Notary Service,O=R3,OU=corda,L=Zurich,C=CH"),
port.getAndIncrement(),
port.getAndIncrement(),
port.getAndIncrement(),
Collections.singletonList("corda.notary.validating"),
Arrays.asList(rpcUser),
null
);
@Before
public void setUp() {
notary = new NodeProcess.Factory().create(notaryConfig);
connection = notary.connect();
rpcProxy = connection.getProxy();
notaryNode = fetchNotaryIdentity();
}
@After
public void done() {
try {
connection.close();
} finally {
notary.close();
}
}
private NodeInfo fetchNotaryIdentity() {
DataFeed<List<NodeInfo>, NetworkMapCache.MapChange> nodeDataFeed = rpcProxy.networkMapFeed();
return nodeDataFeed.getSnapshot().get(0);
}
@Test
public void testCashBalances() throws NoSuchFieldException, ExecutionException, InterruptedException {
Amount<Currency> dollars123 = new Amount<>(123, Currency.getInstance("USD"));
FlowHandle<AbstractCashFlow.Result> flowHandle = rpcProxy.startFlowDynamic(CashIssueFlow.class,
dollars123, OpaqueBytes.of("1".getBytes()),
notaryNode.getLegalIdentity(), notaryNode.getLegalIdentity());
System.out.println("Started issuing cash, waiting on result");
flowHandle.getReturnValue().get();
Amount<Currency> balance = getBalance(Currency.getInstance("USD"));
System.out.print("Balance: " + balance + "\n");
assertEquals(dollars123, balance, "matching");
}
private Amount<Currency> getBalance(Currency currency) throws NoSuchFieldException {
Field pennies = CashSchemaV1.PersistentCashState.class.getDeclaredField("pennies");
@SuppressWarnings("unchecked")
QueryCriteria sumCriteria = new QueryCriteria.VaultCustomQueryCriteria(Builder.sum(pennies));
Vault.Page<Cash.State> results = rpcProxy.vaultQueryByCriteria(sumCriteria, Cash.State.class);
if (results.getOtherResults().isEmpty()) {
return new Amount<>(0L, currency);
} else {
Assert.assertNotNull(results.getOtherResults());
Long quantity = (Long) results.getOtherResults().get(0);
return new Amount<>(quantity, currency);
}
}
}

View File

@ -6,9 +6,7 @@ import net.corda.client.rpc.CordaRPCConnection
import net.corda.client.rpc.notUsed
import net.corda.contracts.asset.Cash
import net.corda.core.InputStreamAndHash
import net.corda.core.contracts.DOLLARS
import net.corda.core.contracts.POUNDS
import net.corda.core.contracts.SWISS_FRANCS
import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash
import net.corda.core.getOrThrow
import net.corda.core.messaging.*
@ -21,6 +19,7 @@ import net.corda.core.utilities.loggerFor
import net.corda.flows.CashIssueFlow
import net.corda.flows.CashPaymentFlow
import net.corda.nodeapi.User
import net.corda.schemas.CashSchemaV1
import net.corda.smoketesting.NodeConfig
import net.corda.smoketesting.NodeProcess
import org.apache.commons.io.output.NullOutputStream
@ -35,6 +34,7 @@ import java.util.concurrent.atomic.AtomicInteger
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotEquals
import kotlin.test.assertTrue
class StandaloneCordaRPClientTest {
private companion object {
@ -177,10 +177,44 @@ class StandaloneCordaRPClientTest {
assertEquals(3, moreResults.totalStatesAvailable) // 629 - 100 + 100
// Check that this cash exists in the vault
val cashBalance = rpcProxy.getCashBalances()
log.info("Cash Balances: $cashBalance")
assertEquals(1, cashBalance.size)
assertEquals(629.POUNDS, cashBalance[Currency.getInstance("GBP")])
val cashBalances = rpcProxy.getCashBalances()
log.info("Cash Balances: $cashBalances")
assertEquals(1, cashBalances.size)
assertEquals(629.POUNDS, cashBalances[Currency.getInstance("GBP")])
}
@Test
fun `test cash balances`() {
val startCash = rpcProxy.getCashBalances()
assertTrue(startCash.isEmpty(), "Should not start with any cash")
val flowHandle = rpcProxy.startFlow(::CashIssueFlow,
629.DOLLARS, OpaqueBytes.of(0),
notaryNode.legalIdentity, notaryNode.legalIdentity
)
println("Started issuing cash, waiting on result")
flowHandle.returnValue.get()
val balance = getBalance(USD)
println("Balance: " + balance)
assertEquals(629.DOLLARS, balance)
}
private fun getBalance(currency: Currency): Amount<Currency> {
val sum = builder { CashSchemaV1.PersistentCashState::pennies.sum() }
val sumCriteria = QueryCriteria.VaultCustomQueryCriteria(sum)
val ccyIndex = builder { CashSchemaV1.PersistentCashState::currency.equal(currency.currencyCode) }
val ccyCriteria = QueryCriteria.VaultCustomQueryCriteria(ccyIndex)
val results = rpcProxy.vaultQueryBy<Cash.State>(sumCriteria.and(ccyCriteria))
if (results.otherResults.isEmpty()) {
return Amount(0L, currency)
} else {
@Suppress("UNCHECKED_CAST")
val quantity = results.otherResults[0] as Long
return Amount(quantity, currency)
}
}
private fun fetchNotaryIdentity(): NodeInfo {

View File

@ -6,7 +6,7 @@ import net.corda.core.schemas.PersistentState
import net.corda.core.serialization.CordaSerializable
import java.lang.reflect.Field
import kotlin.reflect.KProperty1
import kotlin.reflect.jvm.javaField
import kotlin.reflect.jvm.javaGetter
@CordaSerializable
enum class BinaryLogicalOperator {
@ -66,9 +66,9 @@ sealed class CriteriaExpression<O, out T> {
}
@CordaSerializable
sealed class Column<O, out C> {
data class Java<O, out C>(val field: Field) : Column<O, C>()
data class Kotlin<O, out C>(val property: KProperty1<O, C?>) : Column<O, C>()
class Column<O, out C>(val name: String, val declaringClass: Class<*>) {
constructor(field: Field) : this(field.name, field.declaringClass)
constructor(property: KProperty1<O, C?>) : this(property.name, property.javaGetter!!.declaringClass)
}
@CordaSerializable
@ -92,19 +92,8 @@ fun <O, R> resolveEnclosingObjectFromExpression(expression: CriteriaExpression<O
}
@Suppress("UNCHECKED_CAST")
fun <O, C> resolveEnclosingObjectFromColumn(column: Column<O, C>): Class<O> {
return when (column) {
is Column.Java -> column.field.declaringClass as Class<O>
is Column.Kotlin -> column.property.javaField!!.declaringClass as Class<O>
}
}
fun <O, C> getColumnName(column: Column<O, C>): String {
return when (column) {
is Column.Java -> column.field.name
is Column.Kotlin -> column.property.name
}
}
fun <O, C> resolveEnclosingObjectFromColumn(column: Column<O, C>): Class<O> = column.declaringClass as Class<O>
fun <O, C> getColumnName(column: Column<O, C>): String = column.name
/**
* Pagination and Ordering
@ -210,14 +199,14 @@ sealed class SortAttribute {
object Builder {
fun <R : Comparable<R>> compare(operator: BinaryComparisonOperator, value: R) = ColumnPredicate.BinaryComparison(operator, value)
fun <O, R> KProperty1<O, R?>.predicate(predicate: ColumnPredicate<R>) = CriteriaExpression.ColumnPredicateExpression(Column.Kotlin(this), predicate)
fun <O, R> KProperty1<O, R?>.predicate(predicate: ColumnPredicate<R>) = CriteriaExpression.ColumnPredicateExpression(Column(this), predicate)
fun <R> Field.predicate(predicate: ColumnPredicate<R>) = CriteriaExpression.ColumnPredicateExpression(Column.Java<Any, R>(this), predicate)
fun <R> Field.predicate(predicate: ColumnPredicate<R>) = CriteriaExpression.ColumnPredicateExpression(Column<Any, R>(this), predicate)
fun <O, R> KProperty1<O, R?>.functionPredicate(predicate: ColumnPredicate<R>, groupByColumns: List<Column.Kotlin<O, R>>? = null, orderBy: Sort.Direction? = null)
= CriteriaExpression.AggregateFunctionExpression(Column.Kotlin(this), predicate, groupByColumns, orderBy)
fun <R> Field.functionPredicate(predicate: ColumnPredicate<R>, groupByColumns: List<Column.Java<Any, R>>? = null, orderBy: Sort.Direction? = null)
= CriteriaExpression.AggregateFunctionExpression(Column.Java<Any, R>(this), predicate, groupByColumns, orderBy)
fun <O, R> KProperty1<O, R?>.functionPredicate(predicate: ColumnPredicate<R>, groupByColumns: List<Column<O, R>>? = null, orderBy: Sort.Direction? = null)
= CriteriaExpression.AggregateFunctionExpression(Column(this), predicate, groupByColumns, orderBy)
fun <R> Field.functionPredicate(predicate: ColumnPredicate<R>, groupByColumns: List<Column<Any, R>>? = null, orderBy: Sort.Direction? = null)
= CriteriaExpression.AggregateFunctionExpression(Column<Any, R>(this), predicate, groupByColumns, orderBy)
fun <O, R : Comparable<R>> KProperty1<O, R?>.comparePredicate(operator: BinaryComparisonOperator, value: R) = predicate(compare(operator, value))
fun <R : Comparable<R>> Field.comparePredicate(operator: BinaryComparisonOperator, value: R) = predicate(compare(operator, value))
@ -264,34 +253,34 @@ object Builder {
/** aggregate functions */
fun <O, R> KProperty1<O, R?>.sum(groupByColumns: List<KProperty1<O, R>>? = null, orderBy: Sort.Direction? = null) =
functionPredicate(ColumnPredicate.AggregateFunction(AggregateFunctionType.SUM), groupByColumns?.map { Column.Kotlin(it) }, orderBy)
functionPredicate(ColumnPredicate.AggregateFunction(AggregateFunctionType.SUM), groupByColumns?.map { Column(it) }, orderBy)
@JvmStatic @JvmOverloads
fun <R> Field.sum(groupByColumns: List<Field>? = null, orderBy: Sort.Direction? = null) =
functionPredicate(ColumnPredicate.AggregateFunction<R>(AggregateFunctionType.SUM), groupByColumns?.map { Column.Java<Any,R>(it) }, orderBy)
functionPredicate(ColumnPredicate.AggregateFunction<R>(AggregateFunctionType.SUM), groupByColumns?.map { Column<Any,R>(it) }, orderBy)
fun <O, R> KProperty1<O, R?>.count() = functionPredicate(ColumnPredicate.AggregateFunction(AggregateFunctionType.COUNT))
@JvmStatic fun Field.count() = functionPredicate(ColumnPredicate.AggregateFunction<Any>(AggregateFunctionType.COUNT))
fun <O, R> KProperty1<O, R?>.avg(groupByColumns: List<KProperty1<O, R>>? = null, orderBy: Sort.Direction? = null) =
functionPredicate(ColumnPredicate.AggregateFunction(AggregateFunctionType.AVG), groupByColumns?.map { Column.Kotlin(it) }, orderBy)
functionPredicate(ColumnPredicate.AggregateFunction(AggregateFunctionType.AVG), groupByColumns?.map { Column(it) }, orderBy)
@JvmStatic
@JvmOverloads
fun <R> Field.avg(groupByColumns: List<Field>? = null, orderBy: Sort.Direction? = null) =
functionPredicate(ColumnPredicate.AggregateFunction<R>(AggregateFunctionType.AVG), groupByColumns?.map { Column.Java<Any,R>(it) }, orderBy)
functionPredicate(ColumnPredicate.AggregateFunction<R>(AggregateFunctionType.AVG), groupByColumns?.map { Column<Any,R>(it) }, orderBy)
fun <O, R> KProperty1<O, R?>.min(groupByColumns: List<KProperty1<O, R>>? = null, orderBy: Sort.Direction? = null) =
functionPredicate(ColumnPredicate.AggregateFunction(AggregateFunctionType.MIN), groupByColumns?.map { Column.Kotlin(it) }, orderBy)
functionPredicate(ColumnPredicate.AggregateFunction(AggregateFunctionType.MIN), groupByColumns?.map { Column(it) }, orderBy)
@JvmStatic
@JvmOverloads
fun <R> Field.min(groupByColumns: List<Field>? = null, orderBy: Sort.Direction? = null) =
functionPredicate(ColumnPredicate.AggregateFunction<R>(AggregateFunctionType.MIN), groupByColumns?.map { Column.Java<Any,R>(it) }, orderBy)
functionPredicate(ColumnPredicate.AggregateFunction<R>(AggregateFunctionType.MIN), groupByColumns?.map { Column<Any,R>(it) }, orderBy)
fun <O, R> KProperty1<O, R?>.max(groupByColumns: List<KProperty1<O, R>>? = null, orderBy: Sort.Direction? = null) =
functionPredicate(ColumnPredicate.AggregateFunction(AggregateFunctionType.MAX), groupByColumns?.map { Column.Kotlin(it) }, orderBy)
functionPredicate(ColumnPredicate.AggregateFunction(AggregateFunctionType.MAX), groupByColumns?.map { Column(it) }, orderBy)
@JvmStatic
@JvmOverloads
fun <R> Field.max(groupByColumns: List<Field>? = null, orderBy: Sort.Direction? = null) =
functionPredicate(ColumnPredicate.AggregateFunction<R>(AggregateFunctionType.MAX), groupByColumns?.map { Column.Java<Any,R>(it) }, orderBy)
functionPredicate(ColumnPredicate.AggregateFunction<R>(AggregateFunctionType.MAX), groupByColumns?.map { Column<Any,R>(it) }, orderBy)
}
inline fun <A> builder(block: Builder.() -> A) = block(Builder)

View File

@ -3,6 +3,7 @@ package net.corda.core.schemas
import io.requery.Persistable
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateRef
import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.toHexString
import java.io.Serializable
import javax.persistence.Column
@ -49,7 +50,7 @@ open class MappedSchema(schemaFamily: Class<*>,
* A super class for all mapped states exported to a schema that ensures the [StateRef] appears on the database row. The
* [StateRef] will be set to the correct value by the framework (there's no need to set during mapping generation by the state itself).
*/
@MappedSuperclass open class PersistentState(@EmbeddedId var stateRef: PersistentStateRef? = null) : StatePersistable
@MappedSuperclass @CordaSerializable open class PersistentState(@EmbeddedId var stateRef: PersistentStateRef? = null) : StatePersistable
/**
* Embedded [StateRef] representation used in state mapping.

View File

@ -2,6 +2,7 @@ package net.corda.schemas
import net.corda.core.schemas.MappedSchema
import net.corda.core.schemas.PersistentState
import net.corda.core.serialization.CordaSerializable
import javax.persistence.*
/**
@ -13,6 +14,7 @@ object CashSchema
* First version of a cash contract ORM schema that maps all fields of the [Cash] contract state as it stood
* at the time of writing.
*/
@CordaSerializable
object CashSchemaV1 : MappedSchema(schemaFamily = CashSchema.javaClass, version = 1, mappedTypes = listOf(PersistentCashState::class.java)) {
@Entity
@Table(name = "contract_cash_states",

View File

@ -2,6 +2,7 @@ package net.corda.schemas
import net.corda.core.schemas.MappedSchema
import net.corda.core.schemas.PersistentState
import net.corda.core.serialization.CordaSerializable
import java.time.Instant
import javax.persistence.Column
import javax.persistence.Entity
@ -17,6 +18,7 @@ object CommercialPaperSchema
* First version of a commercial paper contract ORM schema that maps all fields of the [CommercialPaper] contract state
* as it stood at the time of writing.
*/
@CordaSerializable
object CommercialPaperSchemaV1 : MappedSchema(schemaFamily = CommercialPaperSchema.javaClass, version = 1, mappedTypes = listOf(PersistentCommercialPaperState::class.java)) {
@Entity
@Table(name = "cp_states",

View File

@ -41,6 +41,11 @@ sourceSets {
runtimeClasspath += main.output + test.output
srcDir file('src/integration-test/kotlin')
}
java {
compileClasspath += main.output + test.output
runtimeClasspath += main.output + test.output
srcDir file('src/integration-test/java')
}
resources {
srcDir file('src/integration-test/resources')
}
@ -53,6 +58,11 @@ sourceSets {
runtimeClasspath += main.output
srcDir file('src/smoke-test/kotlin')
}
java {
compileClasspath += main.output
runtimeClasspath += main.output
srcDir file('src/smoke-test/java')
}
}
}

View File

@ -74,8 +74,8 @@ class HibernateQueryCriteriaParser(val contractType: Class<out ContractState>,
val timeCondition = criteria.timeCondition
val timeInstantType = timeCondition!!.type
val timeColumn = when (timeInstantType) {
QueryCriteria.TimeInstantType.RECORDED -> Column.Kotlin(VaultSchemaV1.VaultStates::recordedTime)
QueryCriteria.TimeInstantType.CONSUMED -> Column.Kotlin(VaultSchemaV1.VaultStates::consumedTime)
QueryCriteria.TimeInstantType.RECORDED -> Column(VaultSchemaV1.VaultStates::recordedTime)
QueryCriteria.TimeInstantType.CONSUMED -> Column(VaultSchemaV1.VaultStates::consumedTime)
}
val expression = CriteriaExpression.ColumnPredicateExpression(timeColumn, timeCondition.predicate)
predicateSet.add(parseExpression(vaultStates, expression) as Predicate)
@ -93,9 +93,10 @@ class HibernateQueryCriteriaParser(val contractType: Class<out ContractState>,
}
}
is ColumnPredicate.BinaryComparison -> {
column as Path<Comparable<Any?>?>
@Suppress("UNCHECKED_CAST")
val literal = columnPredicate.rightLiteral as Comparable<Any?>?
@Suppress("UNCHECKED_CAST")
column as Path<Comparable<Any?>?>
when (columnPredicate.operator) {
BinaryComparisonOperator.GREATER_THAN -> criteriaBuilder.greaterThan(column, literal)
BinaryComparisonOperator.GREATER_THAN_OR_EQUAL -> criteriaBuilder.greaterThanOrEqualTo(column, literal)
@ -104,6 +105,7 @@ class HibernateQueryCriteriaParser(val contractType: Class<out ContractState>,
}
}
is ColumnPredicate.Likeness -> {
@Suppress("UNCHECKED_CAST")
column as Path<String?>
when (columnPredicate.operator) {
LikenessOperator.LIKE -> criteriaBuilder.like(column, columnPredicate.rightLiteral)
@ -190,8 +192,8 @@ class HibernateQueryCriteriaParser(val contractType: Class<out ContractState>,
// add optional group by clauses
expression.groupByColumns?.let { columns ->
val groupByExpressions =
columns.map { column ->
val path = root.get<Any?>(getColumnName(column))
columns.map { _column ->
val path = root.get<Any?>(getColumnName(_column))
aggregateExpressions.add(path)
path
}

View File

@ -6,6 +6,7 @@ import net.corda.core.node.services.Vault
import net.corda.core.schemas.CommonSchemaV1
import net.corda.core.schemas.MappedSchema
import net.corda.core.schemas.PersistentState
import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.OpaqueBytes
import java.time.Instant
import java.util.*
@ -19,6 +20,7 @@ object VaultSchema
/**
* First version of the Vault ORM schema
*/
@CordaSerializable
object VaultSchemaV1 : MappedSchema(schemaFamily = VaultSchema.javaClass, version = 1,
mappedTypes = listOf(VaultStates::class.java, VaultLinearStates::class.java, VaultFungibleStates::class.java, CommonSchemaV1.Party::class.java)) {
@Entity