Friday, 21 July 2017

Process Cloud Service and how to loop and select elements from a list

For more than half a year I've been 'dying' to write posts about some of the constructs I've developed in my last Process Cloud Service project. At last I have the opportunity. And I hope I'll be able to write some more. But for starters, one of the problems I encountered is that I needed to process a list of something(s) and select elements from it. Or even better, how to build up a new list based on an input-list and some rules. Oh and do looping, or actually determine how to finish a loop based on a list of elements. Without a count() function in the PCS Expressions...

Questions

If you have met these kinds of problems and the tool at hand is PCS, then you probably ran in (some of) the following questions:
  • Why don't we try to solve this in SOA CS or possibly ICS?
  • Where are the script tasks we have in BPM Suite? (Sorry, this is an obvious one, but still)
  • How to count the elements of a list? Or, where are the functions in PCS?
  • How to add elements to a list?
  • Etc.
To address a few of those...

Many of these things we ran into are actually orchestration issues. And as with the all-time discussions on when to use SOA and when BPM, we advise doing complex service orchestration  (with message processing) in SOA CS, or if possible in ICS. But when we started with this project, the tools we were given were PCS and ICS. And where ICS lacked the more advanced logic processing in the orchestration integrations, at the time (it's improved over time). And sometimes it really is fun to try to accomplish things that were mentioned not being possible. Go where no man has gone before, that sort of stuff.

Script tasks? I guess PCS Product Management gets tired of answering this question. But the real thing is: we do need to do determinations based on the outcome of services. But also doing logic before doing an activity. In the input data association of an activity, you can only assign into the input-arguments of the activity. You can't update process variables. You can do that in the output data-association. But not all activities have a output-data-associations. And there are cases where you don't have an applicable previous activity. For instance in loops.

The case

Let's get into a case. Let's say you have a list of product-subscriptions that your customer has acquired from you. And for some reason some of those products need a review. In our case the customer faces a takeover, and is part of a hierarchy. So you want to fetch all the applicable products in the company hierarchy and add all of those in a list of Review Product Tasks.

The list of products is acquired by calling an ICS service that fetches a list of products from SalesCloudService and calls Oracle Policy Automation to filter the applicable products from the list. That's the list we need to add to the list that should contain the Review Product Tasks for products acquired from all the companies in the hierarchy. The result list looks like:
The cocNumber is the Chamber of Commerce number of the company. For the rest we keep things simple.

The part of interest of the process is:
In the basis, its not that hard. Except for:
  • I don't have an other activity with an output data-association in the loop to do calculations to base my test upon.
  • Again, we don't have script tasks with functions. So I miss-use a decision function as a script task. Which does the job quite well by the way.
  • Oh, and the nice thing of the Business Rules Engine (the same as used in SOASuite and BPM Suite) is that it does have functions. But we'll get into the magic of that in a minute.
The first activity is the service call to the ICS service.  In the output we copy the response message to a DO and it initializes the productIdx to 1. It's a nice example of the fact that we can do (simple) calculations and Data Object manipulations in the output data-association. Those aren't confined to the arguments of the activity.
Within the response message we have a list of products, but of course, those are in the structure of the service definition. We need to copy the elements over to the input of the decision function.

The input of the Add Review Product Task decision function looks like:

The output is:

So in goes the list of products and the product to add. The Decision Functions results into an updated list.
But, how to do that?
First, let's look in to the input data association:

The first rule is to copy the reviewProductTasksDO (the list we're building) to the corresponding input argument. Then the particular product is created by entering the different elements. As said, some where in the structure of the response message of the ICS service, we have a list of products. Using the productIdx variable we select one if the products and that indexed product is copied element by element.

Now, the rule is:

The first 2 conditions are actually declarations of identifiers we refer to in the rest of the rule: reviewProductTask, being the single object as input  and reviewProductTaskList containing the list of reviewProductTasks. Then we only want to add the reviewProductTask if it does not exist, to prevent the rule getting into a infinite loop. Since reviewProductTaskList is in fact an object that has an attribute being a list of reviewProductTask elements, that list contains a set of functions. One of those is the contains() function:
reviewProductTaskList.reviewProductTask.contains(reviewProductTask)

The goal of this function is quite obvious, it returns true() or false(). If the contains() function returns true, then a a call action is performed. And there the magic happens:
reviewProductTaskList.reviewProductTask.append

The reviewProductTaskList.reviewProductTask list also has an append() function. The input of that is the reviewProductTask object variable, the same that went into contains() as a parameter. The real nice thing? This also works in BPM Suite and SOA Suite! Remember? The Business Rules Engine in those products is the exact same thing! To me this was a bit of a revelation. I wasn't aware of the list functions in BRE.

So, now the rule is fired and the list is extended. We need to assign the result to the particular DataObject. So the output data association is:
Quite obvious, again, but the output argument, being the reviewProductTasksOut object, needs to be assigned to the reviewProductTasksDO Data Object. But also the productIdx needs to be increased.

The Decision Function activity loops back to the Exclusive Gateway. How to determine if we're done? Remember we don't have a count() function in the expression builder? The trick is done in the No-transition:





The condition is:
getCriticalProductsResponseMessage4TakeOverCpyDO.data.products.product[productIdx] == null 
When the productIdx is increased and it gets a value greater then the number of product elements in the list, then indexing that (non-existent) product will lead to null. And that can be tested.

And again, it's too obvious, and yet it took me a while to get to this.

The check if we need to get another company in the hierarchy is comparable, only it is indicated by the ICS service. If it is the top-level company, then it has no reference to a parent:
getCriticalProductsResponseMessage4TakeOverCpyDO.data.parentAccountPartyId != null

Conclusion

It might seem so obvious when you read this. And I suppose it is. But, some of those constructs and tests/conditions are not so apparent from the UI. So I hope I have been able to get some of you at the right track.

In one of the follow-up blog-posts I'd like to address Correlations.

No comments :