Update case 4: Resolved value cannot be easily resolved by id alone
In some situations, the target of a foreign key field cannot be easly determined using id only. There are several reasons this could occur:
- The entity might be effdated. It is difficult to load effdated entities using id alone.
- The id might be unique only within the context of its parent. For example, a coverage’s id is a coverage pattern, but the same coverage (with the same id) can exist under multiple vehicles or buildings on the same policy. The id alone does not uniquely identify the desired coverage.
- The id might be computed, which would make it possible but impractical to load the entity directly from the database. For example, for account contacts, the id is the id of the linked Contact entity. Querying on the AccountContact is possible, but it would require at least a join on the Contact table.
- The id might not related to an entity. For example, the Cloud API PolicyContact maps to a PolicyContactWrapper, which is a POGO object and not a database entity.
For example, suppose you are using the Personal Auto line of business in the base
configuration of PolicyCenter. In this line, there is a PersonalVehicle
entity which has an array of VehicleDrivers
. You want to be able to
identify one of the drivers as the "primary driver" of the vehicle. To do this, you add
a PrimaryDriver_Ext
edge foreign key to
PersonalVehicle
that points to the primary driver. However,
VehicleDriver
is an effdated entity, and therefore the correct
object to reference cannot be loaded from the bundle or database using only its id.
The KeyableBeanJsonValueResolver
resolves foreign keys using only the
target's id. This will not work for the circumstances mentioned in the previous list. To
load these types of entities, you must use the
CollectionBasedJsonValueResolver
.
The CollectionBasedJsonValueResolver
The CollectionBasedJsonValueResolver
can identify targets when the
target cannot be easily identified by id alone, and when necessary, it can ensure
that the root object and target object share a common ancestor. It has two
getters:
get ResolvedValueType
- Returns the type of the target objectget AncestorType
- Returns the type of the common ancestor
It also has two methods for identifying the target:
getPossibleResolvedValues
- Returns the collection that contains the target objectidMatchExpression
- Returns a predicate that, when run on the collection, will identify the target
The CollectionBasedJsonValueResolver
is abstract and therefore is
never used directly. If you want to use it, you must implement a concrete subclass
of this resolver. In the subclass, you must override the previously mentioned
getters and methods. You then reference this subclass in the appropriate updater
file.
Extending the CollectionBasedJsonValueResolver
When you extend the CollectionBasedJsonValueResolver
, do the
following:
- Guidewire recommends putting the class in an appropriate package in the
gw.rest.ext.<product>
package. Guidewire also recommends naming the package after the target entity. For example, a resolver used for foreign keys that referencePersonalDriver
instances could be in thegw.rest.ext.pc.policyperiod.pa.v1.driver
package. - Guidewire recommends you name the class
<ResolvedEntity>JsonValueResolver
. - The class must extend
CollectionBasedJsonValueResolver<V, A>
, where:- "
V
" is the entity type of the target. (For example,VehicleDriver
.) - "
A
" is the entity type of the ancestor that owns the relevant collection. (For example,PersonalVehicle
.)
- "
The class must include overrides of the following getters and methods using the following syntax:
protected override property get ResolvedValueType(): Class<TargetEntityType> {
return TargetEntityType
}
protected override property get AncestorType(): Class<AncestorEntityType> {
return AncestorEntityType
}
protected override function getPossibleResolvedValues(ancestor : AncestorEntity) : Collection<TargetEntity> {
return ancestor.ArrayContainingPotentialResolvedValue
}
protected override function idMatchExpression(id: String) : Predicate<TargetEntity> {
return <predicate containing the set of elements with matching ids>
}
where:
- TargetEntityType is the target entity type (such as
VehicleDriver
) - AncestorEntityType is the ancestor entity type (such as
PersonalVehicle
) - ArrayContaininPotentialResolvedValue is the array that contains the
target object (such as
Drivers
)
For example, suppose you are implementing the previously mentioned use case where you
want to have a PrimaryDriver_Ext
field on
PersonalVehicle
that points to an instance of
VehicleDriver
. The value resolver for this business requirement
would be as follows.
package gw.rest.ext.pc.policyperiod.pa.v1.driver
uses gw.rest.core.pl.framework.v1.refs.CollectionBasedJsonValueResolver
uses java.util.function.Predicate
class PADriverJsonValueResolver extends CollectionBasedJsonValueResolver <VehicleDriver, PersonalVehicle> {
protected override property get ResolvedValueType(): Class<VehicleDriver> {
return VehicleDriver
}
protected override property get AncestorType(): Class<PersonalVehicle> {
return PersonalVehicle
}
protected override function getPossibleResolvedValues(ancestor : PersonalVehicle) : Collection<VehicleDriver> {
return ancestor.Drivers
}
protected override function idMatchExpression(id: String) : Predicate<VehicleDriver> {
return \elt -> elt.RestV1EffDatedId == id
}
}
The isEntityViewable method
An extension of the CollectionBasedJsonValueResolver
can also
include an isEntityViewable
method. This method can be used to
control whether the resolved value can be viewed. For example, you could use it to
enforce business logic that states a foreign key can point to a object only if the
object is in a draft or open state.
The implementation of an isEntityViewable
method for the
CollectionBasedJsonValueResolver
is the same as that of the
AbstractKeyableBeanJsonValueResolver
. For more information, see
Update case 3: Accessibility of resolved value is conditional.
Extending the updater
valueResolver
property whose
typeName
is set to the concrete subclass of
CollectionBasedJsonValueResolver
. For example, the updater from
the previous example would be as
follows:"updaters": {
"PersonalVehicle": {
"properties": {
...
"primaryDriver_Ext": {
"path": "PersonalVehicle.PrimaryDriver_Ext",
"valueResolver": {
"typeName": "gw.rest.ext.pc.policyperiod.pa.v1.driver.PADriverJsonValueResolver",
"resolvedValueToAncestorPath": "resolvedValue.Vehicle"
}
},
...
With effdated entities, the object that has the foreign key property (the root object) and the object that the foreign key points to (the target object) must have a common ancestor.
- You will always need a
resolvedValueToAncestorPath
property, as shown above, to identify how to map from the target object to the common ancestor. - If the common ancestor is some object above the root object, then you also need
a
rootToAncestorPath
value that identifies how to navigate from the root to the common ancestor.
In this example, the root object (PersonalVehicle
) is the common
ancestor. Hence, the rootToAncestorPath
is not required.