CORDA-3182: Added additional property on VaultQueryCriteria for querying by account (#5423)

* Added an additional property on VaultQueryCriteria to specify an externalId/accountId when performing a vault query.
Added logic in hibernate query criteria parser to handle the exernalId join and lookup.
Added a test.

* Fixed error in test.
Fixed backwards incompatible changes.

* Updated changelog.

* Updated docs to remove incorrect instructions for querying by external ID.
This commit is contained in:
Roger Willis 2019-09-03 11:55:26 +01:00 committed by GitHub
parent c2057e0893
commit 99f4e4aac2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 113 additions and 23 deletions

View File

@ -81,6 +81,7 @@ sealed class QueryCriteria : GenericQueryCriteria<QueryCriteria, IQueryCriteriaP
open val constraints: Set<Vault.ConstraintInfo> = emptySet()
open val participants: List<AbstractParty>? = null
abstract val contractStateTypes: Set<Class<out ContractState>>?
open val externalIds: List<UUID> = emptyList()
override fun visit(parser: IQueryCriteriaParser): Collection<Predicate> {
return parser.parseCriteria(this)
}
@ -99,8 +100,24 @@ sealed class QueryCriteria : GenericQueryCriteria<QueryCriteria, IQueryCriteriaP
override val relevancyStatus: Vault.RelevancyStatus = Vault.RelevancyStatus.ALL,
override val constraintTypes: Set<Vault.ConstraintInfo.Type> = emptySet(),
override val constraints: Set<Vault.ConstraintInfo> = emptySet(),
override val participants: List<AbstractParty>? = null
override val participants: List<AbstractParty>? = null,
override val externalIds: List<UUID> = emptyList()
) : CommonQueryCriteria() {
// V4 constructors.
@DeprecatedConstructorForDeserialization(version = 7)
constructor(
status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
contractStateTypes: Set<Class<out ContractState>>? = null,
stateRefs: List<StateRef>? = null,
notary: List<AbstractParty>? = null,
softLockingCondition: SoftLockingCondition? = null,
timeCondition: TimeCondition? = null,
relevancyStatus: Vault.RelevancyStatus = Vault.RelevancyStatus.ALL,
constraintTypes: Set<Vault.ConstraintInfo.Type> = emptySet(),
constraints: Set<Vault.ConstraintInfo> = emptySet(),
participants: List<AbstractParty>? = null
) : this(status, contractStateTypes, stateRefs, notary, softLockingCondition, timeCondition, relevancyStatus, constraintTypes, constraints, participants, emptyList())
// V3 c'tors
// These have to be manually specified as @JvmOverloads for some reason causes declaration clashes
@DeprecatedConstructorForDeserialization(version = 6)
@ -144,6 +161,34 @@ sealed class QueryCriteria : GenericQueryCriteria<QueryCriteria, IQueryCriteriaP
fun withConstraintTypes(constraintTypes: Set<Vault.ConstraintInfo.Type>): VaultQueryCriteria = copy(constraintTypes = constraintTypes)
fun withConstraints(constraints: Set<Vault.ConstraintInfo>): VaultQueryCriteria = copy(constraints = constraints)
fun withParticipants(participants: List<AbstractParty>): VaultQueryCriteria = copy(participants = participants)
fun withExternalIds(externalIds: List<UUID>): VaultQueryCriteria = copy(externalIds = externalIds)
fun copy(
status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
contractStateTypes: Set<Class<out ContractState>>? = null,
stateRefs: List<StateRef>? = null,
notary: List<AbstractParty>? = null,
softLockingCondition: SoftLockingCondition? = null,
timeCondition: TimeCondition? = null,
relevancyStatus: Vault.RelevancyStatus = Vault.RelevancyStatus.ALL,
constraintTypes: Set<Vault.ConstraintInfo.Type> = emptySet(),
constraints: Set<Vault.ConstraintInfo> = emptySet(),
participants: List<AbstractParty>? = null
): VaultQueryCriteria {
return VaultQueryCriteria(
status,
contractStateTypes,
stateRefs,
notary,
softLockingCondition,
timeCondition,
relevancyStatus,
constraintTypes,
constraints,
participants,
externalIds
)
}
fun copy(
status: Vault.StateStatus = this.status,
@ -163,7 +208,8 @@ sealed class QueryCriteria : GenericQueryCriteria<QueryCriteria, IQueryCriteriaP
relevancyStatus,
constraintTypes,
constraints,
participants
participants,
externalIds
)
}
}

View File

@ -654,24 +654,5 @@ a view which maps each state to one or more external IDs. The entity relationshi
.. image:: resources/state-to-external-id.png
When performing a vault query, it is now possible to query for states by external ID using a custom query criteria.
.. container:: codeset
.. sourcecode:: java
UUID id = someExternalId;
FieldInfo externalIdField = getField("externalId", VaultSchemaV1.StateToExternalId.class);
CriteriaExpression externalId = Builder.equal(externalIdField, id);
QueryCriteria query = new VaultCustomQueryCriteria(externalId);
Vault.Page<StateType> results = vaultService.queryBy(StateType.class, query);
.. sourcecode:: kotlin
val id: UUID = someExternalId
val externalId = builder { VaultSchemaV1.StateToExternalId::externalId.equal(id) }
val queryCriteria = QueryCriteria.VaultCustomQueryCriteria(externalId)
val results = vaultService.queryBy<StateType>(queryCriteria).states
The ``VaultCustomQueryCriteria`` can also be combined with other query criteria, like custom schemas, for instance. See the vault query API
examples above for how to combine ``QueryCriteria``.
When performing a vault query, it is now possible to query for states by external ID using the ``externalIds`` parameter in
``VaultQueryCriteria``.

View File

@ -7,6 +7,9 @@ release, see :doc:`app-upgrade-notes`.
Unreleased
----------
* Introduced a new parameter ``externalIds: List<UUID>`` to ``VaultQueryCriteria`` which allows CorDapp developers to constrain queries
to a specified set of external IDs.
* Introduced a new API on ``KeyManagementService`` which facilitates lookups of ``PublicKey`` s to ``externalId`` s (Account IDs).
* Introduced a new low level flow diagnostics tool: checkpoint agent (that can be used standalone or in conjunction with the ``dumpCheckpoints`` shell command).

View File

@ -659,6 +659,25 @@ class HibernateQueryCriteriaParser(val contractStateType: Class<out ContractStat
}
}
// External IDs.
if (criteria.externalIds.isNotEmpty()) {
val ids = criteria.externalIds
// Get the state to external id entity.
val persistentStateToExternalIdEntity = VaultSchemaV1.StateToExternalId::class.java
val entityRoot = rootEntities.getOrElse(persistentStateToExternalIdEntity) {
val entityRoot = criteriaQuery.from(persistentStateToExternalIdEntity)
rootEntities[persistentStateToExternalIdEntity] = entityRoot
entityRoot
}
// Add the join and external id predicates.
val externalIdJoin = criteriaBuilder.equal(vaultStates.get<VaultSchemaV1.VaultStates>("stateRef"), entityRoot.get<VaultSchemaV1.StateToExternalId>("compositeKey").get<PersistentStateRef>("stateRef"))
val externalIdPredicate = criteriaBuilder.and(entityRoot.get<VaultSchemaV1.StateToExternalId>("externalId").`in`(ids))
constraintPredicates.add(externalIdJoin)
constraintPredicates.add(externalIdPredicate)
}
return emptySet()
}

View File

@ -87,6 +87,47 @@ class ExternalIdMappingTest {
assertEquals(setOf(dummyStateOne, dummyStateTwo), resultTwo.map { it.state.data }.toSet())
}
@Test
fun `externalIds query criteria test`() {
val vaultService = services.vaultService
// Create new external ID and two keys mapped to it.
val id = UUID.randomUUID()
val idTwo = UUID.randomUUID()
val keyOne = services.keyManagementService.freshKeyAndCert(myself.identity, false, id)
val keyTwo = services.keyManagementService.freshKeyAndCert(myself.identity, false, id)
val keyThree = services.keyManagementService.freshKeyAndCert(myself.identity, false, idTwo)
// Create states with a public key assigned to the new external ID.
val dummyStateOne = createDummyState(listOf(AnonymousParty(keyOne.owningKey)))
val dummyStateTwo = createDummyState(listOf(AnonymousParty(keyTwo.owningKey)))
val dummyStateThree = createDummyState(listOf(AnonymousParty(keyThree.owningKey)))
// This query should return two states!
val result = database.transaction {
vaultService.queryBy<DummyState>(QueryCriteria.VaultQueryCriteria(externalIds = listOf(id))).states
}
assertEquals(setOf(dummyStateOne, dummyStateTwo), result.map { it.state.data }.toSet())
// Should return nothing.
val resultTwo = database.transaction {
vaultService.queryBy<DummyState>(QueryCriteria.VaultQueryCriteria(externalIds = listOf(UUID.randomUUID()))).states
}
assertEquals(emptyList(), resultTwo)
// Should return one state.
val resultThree = database.transaction {
vaultService.queryBy<DummyState>(QueryCriteria.VaultQueryCriteria(externalIds = listOf(idTwo))).states
}
assertEquals(setOf(dummyStateThree), resultThree.map { it.state.data }.toSet())
// Should return all states.
val resultFour = database.transaction {
vaultService.queryBy<DummyState>(QueryCriteria.VaultQueryCriteria(externalIds = listOf())).states
}
assertEquals(setOf(dummyStateOne, dummyStateTwo, dummyStateThree), resultFour.map { it.state.data }.toSet())
}
@Test
fun `One state can be mapped to multiple externalIds`() {
val vaultService = services.vaultService