Friday, June 19, 2020

OIC: Identity Propagation In Structured Process

When a process calls a service you sometimes have a requirement that some user identity needs to be propagated to the service call. This article describes how you can propagate the identity (but alas not the principle) of a user on behalf of whom a service call is executed.

Updated on June 22 to add a missing, last step.

When calling a service in a structured process you sometimes must pass on the identity of the user that called the service. This could be the case when that service call is done to a SaaS application and it is required to track on behalf of whom that service is called. The identity (user name) only is not enough when authentication must happen using the principle (security token) of the user, but there are applications that can handle this using some combination of a system user (or client id plus secret) with an on behalf of user. And there are situations where having an on behalf of user only is enough, like when storing data in a database table with audit columns (you don’t want all the end users also to be database users so passing on the user’s principle to the DB would not make sense).

It is not always trivial who that on behalf of user should be. Take for example the following process model:

  • On the top a flow implementing the 4-eye principle. The first user does something after which the second user reviews it. Which identity should be passed on, that of the first or the second user?
  • Below that a flow where the user activity is optional. When that user activity is not executed, what identity should be passed on?
  • After that an exception flow to handle a fault with calling the service. This is handled by some Applications Administrator that may choose to retry. Which identity should be passed on, that of the Process Owner or the administrator?
There is no good or bad answer, it depends. You can decide to keep it simple though and choose for always using the identity of the last user that executed a Human Task no matter where in the process that is. And if there is none, then use the identity of the “system user” that started the process instance.

Now how to achieve this? In short:
  • In the Start event of the process set some “user” payload element to the creator of the process.
  • Whenever a Human Task is finished, replace that with the id of the user that did that.
  • When the service is called, pass it on as a custom header element.
Putting the id as plain text in the header is not exactly 100% hack-proof but spoofing it is also not trivial. Considering that calls are done over SSL it just might be secure enough.

In Detail

I work with one single process payload data object as much as possible. Let’s call that processPayload. You can add an element to that called onBehalfOfuser. In the Start event you can instantiate that with the creator predefined variable:

In the output mapping of every Human Task map the to the processPayload.onBehalfOfUser:

In case of an OIC Integration (or ORDS REST Handler) you can pass that on as a custom header element. In case of an Integration you configure that in the trigger activity.

In case of an ORDS REST Handler you can configure that as an HTTP header parameter:

When calling the service, you map the payload.onBehalfOfUser to the header parameter, which either will have the value of the creator of the process, or that user id of the last Human Task that has been executed.

Finally, to make the Integration backward compatible you can use an choose-when-otherwise construction to default the identity with the invokedBy meta data element:

Friday, May 29, 2020

OIC: How to Find Human Task by Correlation and How to Abort a Parallel Task

This article explains how you can find an instance of a Human Task of process instance in the Oracle Integration Cloud (OIC) without knowing its task number, and how you can use that for example to withdraw a parallel task. You can use the same mechanism for other use cases as well, like to get a specific task instance for a specific process instance in a custom Workspace, etc.

When a Human Task is scheduled in a process there is no out-of-the-box way for the process instance to “know” its task number, because scheduling a task concerns an asynchronous call (so you don’t get an immediate response with the task number). So, although the task number is visible in the process flow trace (as shown below), the process instance itself does not know it.

The sample process below has two parallel tasks. The outcome of Parallel Task 2 is either CONTINUE or DONE. When DONE, Parallel Task 1 must be withdrawn. I will use this as a use case to illustrate how setting some “correlation id” on a task can be used to achieve that. 

For those of you who remember the on-premise BPM Suite may be aware of the Update Task activity you could use to do just that, but such an activity does not exist in OIC. Instead you will have to use the PUT operation of the /ic/api/process/v1/tasks/{id} API. But as you can see this requires an id, which is the task number that you don’t have. To get that you can use the GET operation on /ic/api/process/v1/tasks first. However, there can be many instances of the same process, implying many instances of the Parallel Task 1, so how to find that one you are looking for?

The clue is that you must map some unique key to the task when scheduling Parallel Task 1 so that you can use the GET tasks operation to query tasks on that key. The GET supports a keyword query parameter to do this. You can include the unique key in the task title (which is indexed and used in the search by keyword), but that clutters the task title with some technical id.

Fortunately, Human Tasks have a specific request element you can use to do just that, which is the identificationKey. In the example below I a have mapped some idPrefix that is unique for the process instance, and post-fixed that with “-1” to make the identificationKey unique over all Human Tasks instances.

Now in the Get Task activity I use the GET operation to search tasks by keyword equal to my unique key:

The response is a list of tasks with an “items” element that will contain the (one) task instance I’m interested in. I created the Tasks Business Type for the response of the API. To get the instance of the task I will have to map the “items” of that response to an array of items first, for which I created the TaskItems Business Type, and then map the first item in the array to a Task Business Type I created. So, Tasks -> Task Items -> Task as shown below:

The mapping from the response of the API to my TaskItems is done using a Transformation. As I’m only interested in the task number I have left all other elements out of the TaskItems, except for the state:

To map first element of the TaskItems to a variable of my Task Business Types I also use a Transformation:

Now I have a hold on the task number so I can call the PUT operation to withdraw the task in the Withdraw Task activity:

When I start an instance of this process, and go the Workspace I will see both tasks:

I can then submit the instance of Parallel Task 2 by choosing DONE.

And as a result, the instance of Parallel Task 1 is withdrawn and the process ends:

In a real process application, I would have made this a bit more robust and would check if the Get Tasks returns a task instance and that the state of that task is ASSIGNED. I probably would even create a (reusable) Integration to withdraw any task from any process by identificationKey. But you get the draft.

Sunday, February 09, 2020

OCI: When and How to Create an Integration to Call a Service from a Process?

With the Oracle Integration Cloud, when you have to call a service from a Process you can choose to call an external service directly or you can put an Integration in between. This article gives some directives why you may want to do the latter, and how to prevent a pitfall that is easy to step in to.

To call a service you have to import the WSDL with it's XSD's. With that Business Types are auto-generated for all complexTypes in that XSD. Recently I was refactoring a case where this resulted in some 220 (!) Business Types being generated from 1 single external service, of which only a few were actually used. Granted, it concerned a service with a very complex interface, but for some reason all the external SOAP services we have to consume are moderate to very complex and easily generate 50+ Business Types. Not only that, they also use relatively long namespaces. Can you imagine what will happen when you have to call 5 of these services from the same Process application! You barely can see the forest from the trees, and you may find it pretty difficult to identify the correct Business Type to use for your request.

The following example shows the selection list showing the types to chose from when creating a data object for one of the most simple cases we have.

Another issue might be that your Process is tightly coupled to the external service. When it's interface changes you will have to re-import the WSDL. There is a risk that it will not be back-ward compatible which may result in a situation that you first have to completely remove the existing WSDL with all its resources, but can't do so until you have to removed all references to it from data objects (variables) that were defined using those Business Types. That can become quite a challenge.

All this can to a great length be prevented by creating an Integration as a proxy between the external service and the Process application. But you will solve nothing when you use the same WSDL for both the Invoke as well as the Trigger Connection. You must take the extra step to create another WSDL with an XSD for the trigger connection, which offers an as much simplified interface as possible to be consumed by the process. This is nothing less than an example of applying the basic principle of decoupling.

What I personally do is, starting from the parent element of the request and response type, copying the complex types I need from the XSD of the target service, snip all elements I don't need from it, simplify the data types (e.g. change normalizedString to string) and remove all attributes I don't need, or replace those that I do by an element instead. And so work my way down to the child elements as far as required. And then create a Trigger Connection based on the simplified WSDL to be used for the Integration that will be consumed by the Process, as shown in the following picture.

In the example of the 220 Business Types I mentioned before, I was able to reduce that to just 22 Business Types.