Support for Entity References
Overview
WRS supports requesting entity references in place of entities on GET requests via ‘$ref’. Entity references can be resolved on domain entity requests via ‘$entity’. The WRS framework also includes hooks that allow domain authors to implement modification of navigations via entity references.
The following ODATA operations on entity references are supported by the framework:
READ
CREATE
UPDATE
DELETE
READ
You can request entity references for entities, entity collections, and navigation expansions. Requests specifying reference collections support the same collection operations (filtering, sorting, count, etc.) as full entity collection requests.
Examples:
Get references for all parts residing in product GOLF_CART
GET …/Windchill/servlet/odata/ProdMgmt/Parts/$ref?$filter=Context/Name eq 'GOLF_CART'
Return a part with references to its usage links
GET …/Windchill/servlet/odata/ProdMgmt/Parts('OR:wt.part.WTPart:90247')?$expand=Uses/$ref
Modifying Associations via Entity References
Modification of associations via entity references requires domain authors to implement some hooks, as detailed in this section.
Modifying associations via entity references means either adding or removing relationships between existing entities in the context of a navigation. This primarily applies to navigations which are non-containment, where entities exist on their own and can be associated or disassociated via a navigation. However, the framework also supports deletion of contained entities via references, as you will see in the examples below.
Navigations which meet the following criteria can support modification via entity references:
1. The navigation is not annotated as read only —It does not have the PTC.ReadOnly annotation.
2. The navigation is not annotated as updateable via action —It does not have the PTC.UpdateableViaAction annotation.
For qualifying navigations, the framework will attempt to find and call hook methods which perform the relationship operation. If the hook is not implemented, the framework will return a '501 - Not Implemented' response. Similar to how domain authors must implement the getRelatedEntityCollection method for retrieving the data for a navigation, hooks must also be implemented for supporting modification of the associations logically defined by a navigation.
Similar to other hooks, domain authors can either implement the hooks via Javascript, or override the corresponding processor method. The documentation below shows the Javascript version of the hooks, but the logic would be the same if you choose to implement in the processor class instead.
The following methods have been added to NavigationProcessorData for accessing the entity reference parameters in the hooks:
public Collection<Object> getAssociationObjects()
Returns the backing object(s) involved in a request which is modifying an entity association via references. If you need to know the original URI associated to the backing object, use getAssociationParams() instead.
public Map<URI, Object> getAssociationParams()
Returns the map of entity association parameters to be used in a request modifying an entity association via references. The map is keyed by the URI specified in the request, and valued by the backing object.
* 
Some requests may implicitly define the entity being disassociated by specifying it via the URL path, rather than any explicit parameter via an $id query parameter or entity reference in the request body. The association parameters map will still contain the implicit entity reference URI as a key.
Editing Scenarios
Adding a single entity to multi-value association—A single entity reference parameter is specified in the request body. The parameter is populated on the navigation processor data instance passed to the hook. Note that the client could specify to associate an entity that is already associated; per the OData specification the response should still be a status code of '204 - No content'. Hooks however must check that the entity is not already associated.
Hook called: addEntityAssociation(NavigationProcessorData)
Return true if the hook processes the request; false otherwise
Example: Add part as affected object to problem report
POST /Windchill/servlet/odata/ChangeMgmt/ProblemReports(<oid>)/AffectedObjects/$ref
{
"@odata.id": "http://.../ProdMgmt/Parts(<partOid>)"
}

function addEntityAssociation(navData) {
if ("AffectedObjects".equals(navData.getUriResource().getSegmentValue())) {

// Get the source object
var changeIssue = navData.getSourceObjects().iterator().next();

// Get the entity being added
var changeable = navData.getAssociationObjects().iterator().next();

// Create link (...you should check to see if changeable is already associated...)
var PersistenceHelper = Java.type('wt.fc.PersistenceHelper');
var ReportedAgainst = Java.type('wt.change2.ReportedAgainst');
PersistenceHelper.manager.store(ReportedAgainst.newReportedAgainst(changeable, changeIssue));

return true;
}

return false;
}
Setting/updating single value association—A single entity reference parameter is specified in the request body. The parameter is populated on the navigation processor data instance passed to the hook. Note that the client could specify to associate an entity that is already associated; per the OData specification the response should still be a success '204 - No content'. Hooks however should check that the entity is not already associated.
The framework provides a default implementation for this request if the hook below is not implemented. The default implementation will be to issue a PATCH request on the entity and passing the parameter as the value for a binding link. If this behavior is sufficient, there is no need to implement the link below.
Hook called:
updateEntityAssociations(NavigationProcessorData)
Return true if the hook processes the request; false otherwise
Example: Set owner of variance
PUT /Windchill/servlet/odata/ChangeMgmt/Variances(<oid>)/VarianceOwners/$ref
{
"@odata.id": "http://.../PrincipalMgmt/Users(<userOid>)"
}

function updateEntityAssociations(navData) {
if ("VarianceOwners".equals(navData.getUriResource().getSegmentValue())) {

// Get the source object
var WTVariance = Java.type('wt.change2.WTVariance');
var variance = navData.getSourceObjects().iterator().next();

// Get the entity being added
var WTUser = Java.type('wt.org.WTUser');
var user = navData.getAssociationObjects().iterator().next();

// Associate (if not already associated)
var WTPrincipalReference = Java.type('wt.org.WTPrincipalReference');
var userRef = WTPrincipalReference.newWTPrincipalReference(user);
if (! userRef.equals(variance.getVarianceOwner())) {
variance.setVarianceOwner(userRef);
var ChangeHelper2 = Java.type('wt.change2.ChangeHelper2');
ChangeHelper2.service.saveChangeIssue(variance);
}
return true;
}

return false;
}
Removing single entity from multi-value association—A single entity reference parameter is specified in the request URL. This can be specified either using a key predicate for the navigation in the path, or by specifying the entity URL via a '$id' query parameter. The entity object parameter will be populated on the navigation processor data instance passed to the hook. Note that the client could specify to disassociate an entity that is not associated; per the OData specification the response should still be a success '204 - No content'. Hooks however should check that the entity is not already disassociated.
If the navigation is a containment navigation, and no implementation has been provided via the hooks below, the framework implicitly deletes the contained entity as if the request had been a delete request on the contained itself. The same event hooks will be called for this deletion as would be if the client had issued the delete request directly on the contained entity. So, the framework will automatically handle deletion via entity references of contained entities, but domain authors would need to implement the hook(s) for non-containment navigations.
Hook called:
deleteEntityAssociation(NavigationProcessorData)
Return true if the hook processes the request; false otherwise
Example: Remove part as affected object to problem report
DELETE /Windchill/servlet/odata/ChangeMgmt/ProblemReports(< oid >)/AffectedObjects(< partOid >)/$ref

function deleteEntityAssociation(navData) {
if ("AffectedObjects".equals(navData.getUriResource().getSegmentValue())) {

// Get the source object
var changeIssue = navData.getSourceObjects().iterator().next();

// Get the entity being removed
var changeable = navData.getAssociationObjects().iterator().next();

// Remove link (...you should check to see if changeable is already associated...)
var PersistenceHelper = Java.type('wt.fc.PersistenceHelper');
var ReportedAgainst = Java.type('wt.change2.ReportedAgainst');
var linkToDelete = null;
var qryResult = PersistenceHelper.manager.navigate(changeIssue, "theChangeable2", ReportedAgainst.class, false);
while (qryResult.hasMoreElements()) {
var thisReportedAgainst = qryResult.nextElement();
if (changeable.equals(thisReportedAgainst.getChangeable2())) {
linkToDelete = thisReportedAgainst;
break;
}
}

if (linkToDelete != null)
PersistenceHelper.manager.delete(linkToDelete);

return true;

}

return false;
}
Removing all entities from either single or multi value association—No parameters are actually specified by the client as this is just a delete on the navigation reference (whether single value or multi-value). In this case the separate hook is called and the association objects or parameters will be empty navigation processor data. If the navigation is a containment navigation, and no implementation has been provided via the hooks below, the framework attempts to implicitly delete the contained entities in a multi-object delete call. This works similar to the add and update event flow. First the deleteEntityCollection hook will be called, and if the continueprocessing flag is set, the framework will then continue looking for and calling the multi-object deleteObjects hook. If neither is found, the PersistableEntityProcessor defaults to a multi-object persistence manager deletion operation. A default limit of 5000 will be applied and if the number of contained entities exceeds this limit, an exception is thrown by the framework. The limit is controlled by the property containedEntityDeleteLimit in the odata.properties file in codebase/com/ptc/odata/core.
Hook called:
deleteAllEntityAssociations(NavigationProcessorData)
Return true if the hook processes the request; false otherwise
Example: Remove all affected objects on problem report
DELETE /Windchill/servlet/odata/ChangeMgmt/ProblemReports(< oid >)/AffectedObjects/$ref

function deleteAllEntityAssociations(navData) {
if ("AffectedObjects".equals(navData.getUriResource().getSegmentValue())) {

// Get the source object
var changeIssue = navData.getSourceObjects().iterator().next();

// Remove all reported against links for source change issue
var WTSet = Java.type('wt.fc.collections.WTSet');
var WTHashSet = Java.type('wt.fc.collections.WTHashSet');
var ReportedAgainst = Java.type('wt.change2.ReportedAgainst');
var PersistenceHelper = Java.type('wt.fc.PersistenceHelper');

var linksToDelete = new WTHashSet();
var qryResult = PersistenceHelper.manager.navigate(changeIssue, "theChangeable2", ReportedAgainst.class, false);
while (qryResult.hasMoreElements()) {
linksToDelete.add(qryResult.nextElement());
}

if (! linksToDelete.isEmpty())
PersistenceHelper.manager.delete(linksToDelete);

return true;
}

return false;
}
Updating multi value association—This is currently not supported.