Community Tip - You can Bookmark boards, posts or articles that you'd like to access again easily! X
Version: Windchill 12.1
Use Case: Using Windchill REST Services to creating a Referenced By link between a Part and a Document
Description:
I have followed this guide to successfully create a customization to create a WTPartDescribeLink using WRS: https://www.ptc.com/en/support/article/CS285572?source=search
The guide also mentions how to apply the same steps for WTPartReferenceLink, but I am receiving an error and would appreciate some guidance.
{
"ReferencedBy@odata.bind": "PTC.ProdMgmt.CustomCompanyParts('OR:wt.part.WTPart:226444')",
"References@odata.bind": "PTC.DocMgmt.Documents('OR:wt.doc.WTDocument:226427')"
}
The error:
{
"error": {
"code": null,
"message": "Unable to create entity - Cannot cast wt.doc.WTDocumentMaster to wt.vc.wip.Workable"
}
}
I would really appreciate some help resolving this issue.
Attached is my code for this, which was uploaded to "/codebase/rest/ptc/domain/ProdMgmt/v7/entity". I took the working copy used in the tutorial for PartDescribesLink and modified it for this. I must be missing something!
Part of my struggle is that I do not know how to troubleshoot this. I attempted to add logging statements following this guide like so:
var mylogger = log4javascript.getLogger("PTC.mypackage");
mylogger.trace("...");
However, this returns the error "log4javascript is not defined". I suspect there's more to do than what the guide says. I feel like I'm flying blind, I am editing some code with Notepad++ and having to restart the server takes 15 minutes anytime I want to test something.
Perhaps this is an easy fix, but I looked at other related topics for my issue, but no resolution was posted, so any additional help would be very much appreciated!
Solved! Go to Solution.
Here is the solution code that you will need:
PartReferenceLink.js
function getRelatedEntityCollection(navigationData) {
var HashMap = Java.type('java.util.HashMap');
var WTArrayList = Java.type('wt.fc.collections.WTArrayList');
var Collections = Java.type('java.util.Collections');
var VersionControlHelper = Java.type("wt.vc.VersionControlHelper");
var SessionHelper = Java.type('wt.session.SessionHelper');
var AccessControlHelper = Java.type('wt.access.AccessControlHelper');
var AccessPermission = Java.type('wt.access.AccessPermission');
var ODataApplicationException = Java.type('org.apache.olingo.server.api.ODataApplicationException');
var HttpStatusCode = Java.type('org.apache.olingo.commons.api.http.HttpStatusCode');
var WTMessage = Java.type('wt.util.WTMessage');
var AccessResource = Java.type('wt.access.accessResource');
var targetName = navigationData.getTargetSetName();
var sourceLinks = new WTArrayList(navigationData.getSourceObjects());
var user = SessionHelper.getPrincipal();
if ("References".equals(targetName)) {
var map = new HashMap();
try {
SessionHelper.manager.setAdministrator();
for (var i = 0, l = sourceLinks.size(); i < l; i++) {
var sourceLink = sourceLinks.getPersistable(i);
var document = sourceLink.getRoleBObject();
var latestDocument = VersionControlHelper.service.getLatestIteration(document, false);
var hasAccessToReferencedBy = AccessControlHelper.manager.hasAccess(user, latestDocument, AccessPermission.READ);
if (hasAccessToReferencedBy) {
map.put(sourceLink, Collections.singletonList(latestDocument));
} else {
var errorMsg = WTMessage.getLocalizedMessage(AccessResource.class.getName(),
AccessResource.SECURED_INFORMATION, null, navigationData.getLocale());
throw new ODataApplicationException(errorMsg, HttpStatusCode.FORBIDDEN.getStatusCode(),
navigationData.getLocale());
}
}
} finally {
// de-elevate administrator
SessionHelper.manager.setPrincipal(user.getName());
}
return map;
}
return new HashMap();
}
function processAdditionalAttributes(entity, object, data, processor) {
var ArrayList = Java.type('java.util.ArrayList');
var WTPart = Java.type('wt.part.WTPart');
var WTDocument = Java.type('wt.doc.WTDocument');
var NmOid = Java.type('com.ptc.netmarkets.model.NmOid');
var EntityIDUtils = Java.type('com.ptc.odata.core.entity.processor.EntityIDUtils');
var AttributeData = Java.type('com.ptc.odata.windchill.entity.processor.AttributeData');
var EntityOperation = Java.type('com.ptc.odata.core.entity.processor.EntityOperation');
var ODataApplicationException = Java.type('org.apache.olingo.server.api.ODataApplicationException');
var HttpStatusCode = Java.type('org.apache.olingo.commons.api.http.HttpStatusCode');
var WTMessage = Java.type('wt.util.WTMessage');
var ProdMgmtRB = Java.type('com.ptc.odata.windchill.part.entity.ProdMgmtRB');
var AssociationConstraintHelper = Java.type('com.ptc.core.meta.type.mgmt.server.impl.association.AssociationConstraintHelper');
var ClientTypedUtility = Java.type('wt.type.ClientTypedUtility');
var attributes = new ArrayList(2);
if (data.getOperation() == EntityOperation.CREATE) {
var referencesIdStr = EntityIDUtils.getInstance().getBindingLinkId(data, entity, "References");
var referencesOid = referencesIdStr != null ? NmOid.newNmOid(referencesIdStr) : null;
if (referencesOid != null && referencesOid.isA(WTDocument.class)) {
attributes.add(new AttributeData("references", referencesOid.getRefObject().getMaster()));
}
var referencePart = data.getSourceObjects().iterator().next();
attributes.add(new AttributeData("referencedBy", referencePart));
// make sure our objects are valid for the link constraints
if (!AssociationConstraintHelper.service.isValidAssociation(
referencePart,
ClientTypedUtility.getTypeIdentifier("wt.part.WTPartReferenceLink"),
referencesOid.getRefObject())) {
var errorMsg = "Invalid types for link, does not meet relationship constraints";
throw new ODataApplicationException(errorMsg, HttpStatusCode.FORBIDDEN.getStatusCode(), data.getLocale())
}
}
return attributes;
}
function toObjects(entities, processorData) {
var ObjectAttributeProcessor = Java.type('com.ptc.odata.windchill.entity.processor.ObjectAttributeProcessor');
var ODataApplicationException = Java.type('org.apache.olingo.server.api.ODataApplicationException');
var HttpStatusCode = Java.type('org.apache.olingo.commons.api.http.HttpStatusCode');
var WTMessage = Java.type('wt.util.WTMessage');
var ProdMgmtRB = Java.type('com.ptc.odata.windchill.part.entity.ProdMgmtRB');
// create the object the normal way
var entityToObjectMap = ObjectAttributeProcessor.newInstance().toObjects(entities, processorData);
return entityToObjectMap;
}
function checkForCheckout(newObject, createdEntity, processorData) {
var ODataApplicationException = Java.type('org.apache.olingo.server.api.ODataApplicationException');
var HttpStatusCode = Java.type('org.apache.olingo.commons.api.http.HttpStatusCode');
var WTMessage = Java.type('wt.util.WTMessage');
var ProdMgmtRB = Java.type('com.ptc.odata.windchill.part.entity.ProdMgmtRB');
var WorkInProgressHelper = Java.type('wt.vc.wip.WorkInProgressHelper');
var SessionHelper = Java.type('wt.session.SessionHelper');
var referencePart = newObject.getReferencedBy();
// check that the part is checked out and it is checked out to us
if (!WorkInProgressHelper.isCheckedOut(referencePart, SessionHelper.getPrincipal()) ||
!WorkInProgressHelper.isReservedWorkingCopy(referencePart)) {
var errorMsg = "Part must be checked out and be the working copy to add, update, or delete WTPartReferenceLink";
throw new ODataApplicationException(errorMsg, HttpStatusCode.FORBIDDEN.getStatusCode(), processorData
.getLocale())
} else {
processorData.setContinueProcessing(true);
return newObject;
}
}
function storeNewObject(objectToSave, entity, processorData) {
return checkForCheckout(objectToSave, entity, processorData);
}
function saveObject(objectToSave, entity, processorData) {
return checkForCheckout(objectToSave, entity, processorData);
}
PartReferenceLink.json
{
"name":"PartReferenceLink",
"collectionName":"PartReferenceLinks",
"type":"wcType",
"wcType":"wt.part.WTPartReferenceLink",
"description":"Part reference link",
"operations":"CREATE,UPDATE,DELETE",
"includeInServiceDocument":false,
"defaultForWcType":true,
"parent":{
"name":"WindchillEntities"
},
"attributes":[],
"inherits":[
{
"name": "softattributable",
"version": "5"
}
],
"navigations":[
{
"name":"ReferencedBy",
"target":"Parts",
"type":"Part",
"isCollection":false,
"partner":"",
"internalNameForDisplayName":"referencedBy"
},
{
"name":"References",
"target":"PTC.DocMgmt.Documents",
"type":"PTC.DocMgmt.Document",
"isCollection":false,
"partner":"",
"internalNameForDisplayName":"references"
}
]
}
Now you can hit this endpoint:
POST /ProdMgmt/Parts('{PartId}')/References
{
"ReferencedBy@odata.bind": "PTC.ProdMgmt.MyCustomPartEntity('{PartId}')",
"References@odata.bind": "PTC.DocMgmt.Documents('{DocumentId}')"
}
You can add it to your CCD package by putting the files in this big nested folder structure:
/my_CustomModule/main/resources/rest/custom/domain/ProdMgmt/v7/entity/
I didn’t try the code but did you change the preference Part To Document Association Logic.
I have not, where would I find that?
In Windchill > Utilities > Preferences, I think it needs to be set to Yes
Here's a snippet of log4j logging
package ext.MA.tools;
import org.apache.logging.log4j.Logger;
import wt.log4j.LogR;
public class ConversionHelper {
private static final String CLASSNAME = ConversionHelper.class.getName();
private static final Logger LOGGER = LogR.getLoggerInternal(CLASSNAME);
public static Boolean getBooleanFromBLOB(final Object o, final String s) throws WTException {
LOGGER.debug("Workflow variable '" + s + "' was not found in ProcessData.");
}
}
In js it would be something like this,
//include the library, e.g. add it via cdn
<script src="https://cdnjs.cloudflare.com/ajax/libs/log4javascript/1.4.13/log4javascript.js"></script>
const logger = log4javascript.getLogger("ConversionHelper");
// Add a browser console appender
logger.addAppender(new log4javascript.BrowserConsoleAppender());
// Set the level to debug
logger.setLevel(log4javascript.Level.DEBUG);
logger.debug(`Workflow variable '${s}' was not found in ProcessData.`);
Hi @CB_12625553,
I wanted to see if you got the help you needed.
If so, please mark the appropriate reply as the Accepted Solution. It will help other members who may have the same question.
Of course, if you have more to share on your issue, please pursue the conversation.
Thanks,
Anurag
Logging in WRS extensions (javascript files) can be done as follows:
var LogManager = Java.type('wt.log4j.LogManager');
var Logger = Java.type('wt.log4j.Logger');
var logger = LogManager.getLogger("rest.custom.domain.mycustompackage");
logger.debug("CUSTOMIZATION START ...");
With Windchill REST Services 12.1.2.14, the Product Management v7 domain:
Here is the solution code that you will need:
PartReferenceLink.js
function getRelatedEntityCollection(navigationData) {
var HashMap = Java.type('java.util.HashMap');
var WTArrayList = Java.type('wt.fc.collections.WTArrayList');
var Collections = Java.type('java.util.Collections');
var VersionControlHelper = Java.type("wt.vc.VersionControlHelper");
var SessionHelper = Java.type('wt.session.SessionHelper');
var AccessControlHelper = Java.type('wt.access.AccessControlHelper');
var AccessPermission = Java.type('wt.access.AccessPermission');
var ODataApplicationException = Java.type('org.apache.olingo.server.api.ODataApplicationException');
var HttpStatusCode = Java.type('org.apache.olingo.commons.api.http.HttpStatusCode');
var WTMessage = Java.type('wt.util.WTMessage');
var AccessResource = Java.type('wt.access.accessResource');
var targetName = navigationData.getTargetSetName();
var sourceLinks = new WTArrayList(navigationData.getSourceObjects());
var user = SessionHelper.getPrincipal();
if ("References".equals(targetName)) {
var map = new HashMap();
try {
SessionHelper.manager.setAdministrator();
for (var i = 0, l = sourceLinks.size(); i < l; i++) {
var sourceLink = sourceLinks.getPersistable(i);
var document = sourceLink.getRoleBObject();
var latestDocument = VersionControlHelper.service.getLatestIteration(document, false);
var hasAccessToReferencedBy = AccessControlHelper.manager.hasAccess(user, latestDocument, AccessPermission.READ);
if (hasAccessToReferencedBy) {
map.put(sourceLink, Collections.singletonList(latestDocument));
} else {
var errorMsg = WTMessage.getLocalizedMessage(AccessResource.class.getName(),
AccessResource.SECURED_INFORMATION, null, navigationData.getLocale());
throw new ODataApplicationException(errorMsg, HttpStatusCode.FORBIDDEN.getStatusCode(),
navigationData.getLocale());
}
}
} finally {
// de-elevate administrator
SessionHelper.manager.setPrincipal(user.getName());
}
return map;
}
return new HashMap();
}
function processAdditionalAttributes(entity, object, data, processor) {
var ArrayList = Java.type('java.util.ArrayList');
var WTPart = Java.type('wt.part.WTPart');
var WTDocument = Java.type('wt.doc.WTDocument');
var NmOid = Java.type('com.ptc.netmarkets.model.NmOid');
var EntityIDUtils = Java.type('com.ptc.odata.core.entity.processor.EntityIDUtils');
var AttributeData = Java.type('com.ptc.odata.windchill.entity.processor.AttributeData');
var EntityOperation = Java.type('com.ptc.odata.core.entity.processor.EntityOperation');
var ODataApplicationException = Java.type('org.apache.olingo.server.api.ODataApplicationException');
var HttpStatusCode = Java.type('org.apache.olingo.commons.api.http.HttpStatusCode');
var WTMessage = Java.type('wt.util.WTMessage');
var ProdMgmtRB = Java.type('com.ptc.odata.windchill.part.entity.ProdMgmtRB');
var AssociationConstraintHelper = Java.type('com.ptc.core.meta.type.mgmt.server.impl.association.AssociationConstraintHelper');
var ClientTypedUtility = Java.type('wt.type.ClientTypedUtility');
var attributes = new ArrayList(2);
if (data.getOperation() == EntityOperation.CREATE) {
var referencesIdStr = EntityIDUtils.getInstance().getBindingLinkId(data, entity, "References");
var referencesOid = referencesIdStr != null ? NmOid.newNmOid(referencesIdStr) : null;
if (referencesOid != null && referencesOid.isA(WTDocument.class)) {
attributes.add(new AttributeData("references", referencesOid.getRefObject().getMaster()));
}
var referencePart = data.getSourceObjects().iterator().next();
attributes.add(new AttributeData("referencedBy", referencePart));
// make sure our objects are valid for the link constraints
if (!AssociationConstraintHelper.service.isValidAssociation(
referencePart,
ClientTypedUtility.getTypeIdentifier("wt.part.WTPartReferenceLink"),
referencesOid.getRefObject())) {
var errorMsg = "Invalid types for link, does not meet relationship constraints";
throw new ODataApplicationException(errorMsg, HttpStatusCode.FORBIDDEN.getStatusCode(), data.getLocale())
}
}
return attributes;
}
function toObjects(entities, processorData) {
var ObjectAttributeProcessor = Java.type('com.ptc.odata.windchill.entity.processor.ObjectAttributeProcessor');
var ODataApplicationException = Java.type('org.apache.olingo.server.api.ODataApplicationException');
var HttpStatusCode = Java.type('org.apache.olingo.commons.api.http.HttpStatusCode');
var WTMessage = Java.type('wt.util.WTMessage');
var ProdMgmtRB = Java.type('com.ptc.odata.windchill.part.entity.ProdMgmtRB');
// create the object the normal way
var entityToObjectMap = ObjectAttributeProcessor.newInstance().toObjects(entities, processorData);
return entityToObjectMap;
}
function checkForCheckout(newObject, createdEntity, processorData) {
var ODataApplicationException = Java.type('org.apache.olingo.server.api.ODataApplicationException');
var HttpStatusCode = Java.type('org.apache.olingo.commons.api.http.HttpStatusCode');
var WTMessage = Java.type('wt.util.WTMessage');
var ProdMgmtRB = Java.type('com.ptc.odata.windchill.part.entity.ProdMgmtRB');
var WorkInProgressHelper = Java.type('wt.vc.wip.WorkInProgressHelper');
var SessionHelper = Java.type('wt.session.SessionHelper');
var referencePart = newObject.getReferencedBy();
// check that the part is checked out and it is checked out to us
if (!WorkInProgressHelper.isCheckedOut(referencePart, SessionHelper.getPrincipal()) ||
!WorkInProgressHelper.isReservedWorkingCopy(referencePart)) {
var errorMsg = "Part must be checked out and be the working copy to add, update, or delete WTPartReferenceLink";
throw new ODataApplicationException(errorMsg, HttpStatusCode.FORBIDDEN.getStatusCode(), processorData
.getLocale())
} else {
processorData.setContinueProcessing(true);
return newObject;
}
}
function storeNewObject(objectToSave, entity, processorData) {
return checkForCheckout(objectToSave, entity, processorData);
}
function saveObject(objectToSave, entity, processorData) {
return checkForCheckout(objectToSave, entity, processorData);
}
PartReferenceLink.json
{
"name":"PartReferenceLink",
"collectionName":"PartReferenceLinks",
"type":"wcType",
"wcType":"wt.part.WTPartReferenceLink",
"description":"Part reference link",
"operations":"CREATE,UPDATE,DELETE",
"includeInServiceDocument":false,
"defaultForWcType":true,
"parent":{
"name":"WindchillEntities"
},
"attributes":[],
"inherits":[
{
"name": "softattributable",
"version": "5"
}
],
"navigations":[
{
"name":"ReferencedBy",
"target":"Parts",
"type":"Part",
"isCollection":false,
"partner":"",
"internalNameForDisplayName":"referencedBy"
},
{
"name":"References",
"target":"PTC.DocMgmt.Documents",
"type":"PTC.DocMgmt.Document",
"isCollection":false,
"partner":"",
"internalNameForDisplayName":"references"
}
]
}
Now you can hit this endpoint:
POST /ProdMgmt/Parts('{PartId}')/References
{
"ReferencedBy@odata.bind": "PTC.ProdMgmt.MyCustomPartEntity('{PartId}')",
"References@odata.bind": "PTC.DocMgmt.Documents('{DocumentId}')"
}
You can add it to your CCD package by putting the files in this big nested folder structure:
/my_CustomModule/main/resources/rest/custom/domain/ProdMgmt/v7/entity/
Hi, I am trying to implement this. After putting the files, I am trying to make the request. I checked out the Part before making the request. Even with checked out part I am getting the error code 403 with following response body.
"
{ "error": { "code": null, "message": "Part must be checked out and be the working copy to add, update, or delete WTPartReferenceLink" } }
"
Also I used the request body as following
"
{
"ReferencedBy@odata.bind": "PTC.ProdMgmt.MechanicalPart('OR:wt.part.WTPart:349320081')",
"References@odata.bind": "PTC.DocMgmt.Documents('OR:wt.doc.WTDocument:349319669')"
}
"
Is there anything similar you have faced. I would be grateful for any help or suggestion. Thanks
Hello, instead of debugging above (are you using the OID of the working copy?), you might upgrade to the latest Windchill version. The functionality discussed here is now available in standard Windchill:
With Windchill REST Services 13.1.1.0, the Product Management v7 domain has been updated:
◦ Introduced the ability to create a part reference link between an existing part and a reference document.
◦ Introduced the ability to modify an existing part reference link.
◦ Introduced the ability to delete an existing part reference link.
Hello @MalteSiefkes, Yes, I am using the OID of the working copy and still getting the response
"
"Part must be checked out and be the working copy to add, update, or delete WTPartReferenceLink"
".
we are on Windchill REST Services 13.0.2.3, and in this release, this functionality is not available; therefore, it should be implemented. For upgrading to Windchill latest version I am not sure what the consequences will be. Therefore, I am trying to implement this into the current version, and any hints or help in this regard will be appreciated. Thanks
Hi @MalteSiefkes, thanks for the suggestion. I spent some time with the script above and successfully implemented the functionality.