CE Update Event: any way to pass before/after property values to a workflow? - filenet-p8

I've configured a FileNet workflow subscription on Add, Update and Delete events. The workflow calls a Java component to send a notification message (to a third party).
We would like to see "before" and "after" property values in the notification message for "Update" events.
The "Event" object that triggers the subscription has a "Modified Properties" member, so I was hoping I could just create a corresponding "ModifiedProperties" string array in the workflow, and have the subscription map "Update.ModifiedProperties = ModifiedProperties". Unfortunately, the Event's "ModifiedProperties" only gives the NEW value, not the "before" value.
<= So I don't see any way to get "before/after" values directly from the subscription...
It looks like the "UpdateEvent" object also has an "OriginalObject" member ... and I might be able to use the Java API to get the "before" value from the OriginalObject.
Q: Does this sound plausible method for getting the before/after document property values?
Q: Any ideas how to pass the "OriginalObject" object from the subscription to the workflow, so the Java component can use it?
The target platform is P8 5.2.1; I'm developing on P8 5.5.

You are right, the only way to the original values is through the OriginalObject object. And the quickest way to get data to a workflow is using a subscribable object.
Therefore, a solution to your problem is to define a custom object containing the properties describing the new and the old property values. You create this custom object in a custom event handler triggered on an update event from the document. Here you can populate the properties of the custom object using the original object:
Document document = (Document) event.get_OriginalObject();;
Iterator<?> iterator = event.get_ModifiedProperties().iterator();
while (iterator.hasNext()) {
String modifiedProperty = (String) iterator.next();
// TODO: Fetch the values from the original object
// and set them on the custom object. The details depend
// on the data structure you choose.
}
Next you create a Workflow subscription triggered on the creation of the custom object. You can map the properties of your custom object to the data fields of your workflow. In the workflow that is started you can define an attachment and specify that the custom object is the initiating attachment. Using the CE_Operation queue methods you can now and delete the custom object when your processing is finished.

if(objEvent instanceof UpdateEvent) { try { String strModifiedProperties = ""; UpdateEvent updateEvent = (UpdateEvent) objEvent; StringList propertyNames = updateEvent.get_ModifiedProperties(); Iterator iterModifiedProps = propertyNames.iterator(); while(iterModifiedProps.hasNext()) { String modifiedProperty = (String) iterModifiedProps.next(); strModifiedProperties = strModifiedProperties+modifiedProperty+","; } strModifiedProperties = strModifiedProperties.substring(0, strModifiedProperties.lastIndexOf(",")); } catch (Exception e) { System.out.println("onEvent : Exception while executing UpdateEvent: "+e.getMessage()); } }

Related

spfx - onpropertychange event

I have created cascading dropdown. I need to load dropdown based on parent dropdown selection. I am trying to use onpropertychange event. but I am getting error on super.onpropertychange saying {Property 'onPropertyChange' does not exist on type 'BaseClientSideWebPart'.}
please let us know what I havve missed.
protected onPropertyChange(propertyPath: string, newValue: any):void{
if(propertyPath === "listDropDown"){
// Change only when drop down changes
super.onPropertyChange(propertyPath,newValue);
// Clears the existing data
this.properties.ItemsDropDown = undefined;
this.onPropertyChange('ItemsDropDown', this.properties.ItemsDropDown);
// Get/Load new items data
this.GetItems();
}
else {
// Render the property field
super.onPropertyChange(propertyPath, newValue);
}
}
Instead of onPropertyChange, perhaps you mean onPropertyFieldChanged from the BaseWebPart class?
The error message is accurate - web parts don't have a method called onPropertyChange. The above sounds like the closest match for what you are trying to do. Note that it takes not two arguments, but three: propertyPath, oldValue, and newValue.

How to include multiple statements in the body of flatMap or flatMapMany for Mono or FLux in Spring Reactor?

I have below code:
Mono<Property> property = propertyService.findById(id);
String title;
Flux<Photo> photos = property.flatMapMany(prop ->
{
title = prop.title + '-' + prop.type;
return photoService.findByPropertyId(prop.getId());
}
);
model.addAttribute("prop", property);
model.addAttribute("title", title);
model.addAttribute("photos", photos);
// ajx is query param coming from request
if(ajx != null && !ajx.isEmpty() && ajx.equals("1"))
return Mono.just("fragments/propertyfrag");
else
return Mono.just("property");
The code shows what I want to achieve but it does not even compile. It gives error saying title and type on prop are not visible.
Note that the last statement is reference to thymeleaf template named property. Withn thyeleaf template I have access to variable prop as if it was not reactive but plain prop object that enables me to directly access parameters on prop object. Does that mean within thymeleaf template property.block() has been performed?
In actual code there is some business logic that I need to do after getting title variable in above code and therefore I cannot avail the use of prop passed as model attribute to thymleaf template to directly get title within thymeleaf.
How to solve this problem?
Keep in mind your Flux<Photo> is an asynchronous process, so it cannot update the title variable outside of it in this imperative style. Note that your Flux<Photo> is also never subscribed to or composed upon, so it will effectively never be invoked...
To answer your other question, yes in Spring Framework 5 having a Mono in the Map passed to Thymeleaf will resolve that Mono lazily and inject the resulting value in the Thymeleaf model.
Pending more information on the use of photos flux, for the title generation you probably need to compose more:
propertyService.findById(id)
.doOnNext(prop -> model.addAttribute("prop", prop)) //reactively add the prop to the model
.flatMapMany(prop -> {
String title = prop.title + '-' + prop.type;
if(validate(title)) //do some validation
return photoService.findByPropertyId(prop.getId());
else
return Mono.error(new IllegalArgumentException("validation failed"));
}) //not sure what you do with the `Photo`s there :/
//for now let's ignore the flux photos and at the end simply emit a String to change the view:
.thenReturn("property"); //then(Mono.just("property")) in older versions of reactor 3.1.x

Spring REST and PATCH method

I'm using SpringBoot and Spring REST.
I would like to understand the HTTP PATCH method to update properties of my Model
Is there any good tutorial explaining how to make it works ?
HTTP PATCH method and body to be Send
Controller method and how to manage the update operation
I've noticed that many of the provided answers are all JSON patching or incomplete answers. Below is a full explanation and example of what you need with functioning real world code
First, PATCH is a selective PUT. You use it to update any number of fields for an object or list of objects. In a PUT you typically send the entire object with whatever updates.
PATCH /object/7
{
"objId":7,
"objName": "New name"
}
PUT /object/7
{
"objId":7,
"objName": "New name",
"objectUpdates": true,
"objectStatus": "ongoing",
"scoring": null,
"objectChildren":[
{
"childId": 1
},
............
}
This allows you to update records without huge amounts of endpoints. For example, with above, to update scoring you need object/{id}/scoring, then to update name you need object/{id}/name. Literally one endpoint for every item or you require the front end to post the entire object for every update. If you have a huge object, this can take a lot of network time or mobile data that is unnecessary. The patch lets you have 1 endpoint with the minimal object property sends that a mobile platform should use.
here is an example of a real world use for patch:
#ApiOperation(value = "Patch an existing claim with partial update")
#RequestMapping(value = CLAIMS_V1 + "/{claimId}", method = RequestMethod.PATCH)
ResponseEntity<Claim> patchClaim(#PathVariable Long claimId, #RequestBody Map<String, Object> fields) {
// Sanitize and validate the data
if (claimId <= 0 || fields == null || fields.isEmpty() || !fields.get("claimId").equals(claimId)){
return new ResponseEntity<>(HttpStatus.BAD_REQUEST); // 400 Invalid claim object received or invalid id or id does not match object
}
Claim claim = claimService.get(claimId);
// Does the object exist?
if( claim == null){
return new ResponseEntity<>(HttpStatus.NOT_FOUND); // 404 Claim object does not exist
}
// Remove id from request, we don't ever want to change the id.
// This is not necessary, you can just do it to save time on the reflection
// loop used below since we checked the id above
fields.remove("claimId");
fields.forEach((k, v) -> {
// use reflection to get field k on object and set it to value v
// Change Claim.class to whatver your object is: Object.class
Field field = ReflectionUtils.findField(Claim.class, k); // find field in the object class
field.setAccessible(true);
ReflectionUtils.setField(field, claim, v); // set given field for defined object to value V
});
claimService.saveOrUpdate(claim);
return new ResponseEntity<>(claim, HttpStatus.OK);
}
The above can be confusing for some people as newer devs don't normally deal with reflection like that. Basically, whatever you pass this function in the body, it will find the associated claim using the given ID, then ONLY update the fields you pass in as a key value pair.
Example body:
PATCH /claims/7
{
"claimId":7,
"claimTypeId": 1,
"claimStatus": null
}
The above will update claimTypeId and claimStatus to the given values for claim 7, leaving all other values untouched.
So the return would be something like:
{
"claimId": 7,
"claimSrcAcctId": 12345678,
"claimTypeId": 1,
"claimDescription": "The vehicle is damaged beyond repair",
"claimDateSubmitted": "2019-01-11 17:43:43",
"claimStatus": null,
"claimDateUpdated": "2019-04-09 13:43:07",
"claimAcctAddress": "123 Sesame St, Charlotte, NC 28282",
"claimContactName": "Steve Smith",
"claimContactPhone": "777-555-1111",
"claimContactEmail": "steve.smith#domain.com",
"claimWitness": true,
"claimWitnessFirstName": "Stan",
"claimWitnessLastName": "Smith",
"claimWitnessPhone": "777-777-7777",
"claimDate": "2019-01-11 17:43:43",
"claimDateEnd": "2019-01-11 12:43:43",
"claimInvestigation": null,
"scoring": null
}
As you can see, the full object would come back without changing any data other than what you want to change. I know there is a bit of repetition in the explanation here, I just wanted to outline it clearly.
There is nothing inherently different in PATCH method as far as Spring is concerned from PUT and POST. The challenge is what you pass in your PATCH request and how you map the data in the Controller. If you map to your value bean using #RequestBody, you'll have to figure what is actually set and what null values mean. Others options would be limit PATCH requests to one property and specify it in url or map the values to a Map.
See also Spring MVC PATCH method: partial updates
Create a rest template using -
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
RestTemplate rest = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
now make the PATCH call
ResponseEntity<Map<String, Object>> response = rest.exchange(api, HttpMethod.PATCH, request,
responseType);

ExtJS find events handlers registered with an object

I would like to find which event handlers are registered over an object (in my concrete case it's the pagetree in the TYPO3 CMS backend).
Is there a method to get all event handlers ?
You can simply walk through its events property. Most of the members will be just true, but those actually representing attached events will be objects. Type something like:
var es = Ext.getCmp('my-tree-id').events;
for (var k in es) {
if (Ext.isObject(es[k])) {
console.log(es[k]);
}
}
If your object is a Ext.dom.Element, you would check Ext.cache['elementId'].events instead.

CRM: Get children throws exception on no children

We're using MSCRM Dynamics, and we're trying to get all the children of a particular user. (A user has a manager, the manager has 'children'.) The following works, but throws an exception if the user has no children. This seems logical at first, maybe, but why not just return an empty set? And of all things, it throws a SoapException with a cryptic message of "Invalid Argument" (which is wrong) and the .Detail.InnerText says "0x80040203 The value passed for ConditionOperator.In is empty Platform". If you look at the corresponding Response class, it has a collection -- why not just leave it empty?
// Create the request object.
RetrieveAllChildUsersSystemUserRequest retrieve =
new RetrieveAllChildUsersSystemUserRequest();
// Create the column set object that indicates the fields to be retrieved.
ColumnSet cols = new ColumnSet();
cols.EntityName = "systemuserid";
// Set the column set.
retrieve.ColumnSet = cols;
// Set the ID of the parent user.
retrieve.EntityId = context.UserId;
RetrieveAllChildUsersSystemUserResponse retrieved =
new RetrieveAllChildUsersSystemUserResponse();
/// Execute the request.
/// Catches if user does not have children
/// (Check to see if user is manager)
try
{
retrieved =
(RetrieveAllChildUsersSystemUserResponse)crmService.Execute(retrieve);
}
catch (System.Web.Services.Protocols.SoapException e)
{
throw new Exception(string.Format("{0}", e.Detail.InnerText));
}
I agree, it should probably just return an empty result. My guess would be under the hood, some step of executing the request or preparing the response is translated into a QueryExpression. QueryExpressions blow up if you use a ConditionExpression that uses ConditionOperator.In and you pass it an empty list. So it may be something like it gets a list of child systemuser guids, which in some cases is an empty list, then tries to retrieve all attributes of the system users in that list using another QueryExpression and that is what throws the exception.
You can probably design a QueryExpression or FetchXML of your own that will net you the same results without the side effect of it throwing an exception when the list is empty, or just catch the exception and check for that particular error code and swallow it.

Resources