ICS: Conditional REST / JSON mapping in OIC

Mapping optional fields is quite fiddly in Integration Cloud Service (ICS) when using JSON. In this post I would like to show the steps i’ve taken to come to a solution, as I strongly believe that the road to a solution is also important to provide. Reading the whole post gives insight into the inner workings of ICS. If you only want the solution scroll down to the bottom of this post. There is also an example with Process Cloud Service (PCS) in this post.

Goal and problem description

The goal of my feature:
Do a PATCH to a REST Business Object in the VBCS database from inside a process

For this post:
Let’s say I have a reservation system for a restaurant and I want to PATCH the number of guests and/or the date/time of a reservation from a running Structured Process

So, I thought of what I would need for such a feature beforehand:

  1. We need the Business Object, it’s endpoint and the fieldnames (Reservation BO, fields: customerName (string), numberOfGuests (integer), inOrOutside (string))
  2. We need an integration that does a PATCH operation on the reservation BO, input is ID of the row in the URL and fields numberOfGuests and reservationTimestamp. We want to be able to do a PATCH /reservation/{ID}
  3. We would need to make the fields optional in the integration
  4. We need the activity that saves the Reservation Business Object key (the {id} part). For this example we assume that the part of the structured process to post the reservation is already there and maps back a reservationId in the processcontext
  5. We need to create an activity that calls the ICS integration to do that PATCH on the reservation BO.

Off I went to create all things above. After I finished 1 & 2 I decided to do some testing. I added a reservationBO manually and called my integration from postman. I did a PATCH to {id} used the following JSON body input integration trigger:

{
     "numberOfGuests": 2,
     "inOrOutside": "inside"
}

Used the following body to post to the DB invoke:

{
     "numberOfGuests": 2,
     "inOrOutside": "inside"
}

And used the following output for my whole integration (from the DB):

{
     "id": 0,
     "numberOfGuests": 2,
     "inOrOutside": "inside"
}

My PATCH worked. Success!!

Postman call

Off I went to make the fields optional. How to that you ask? Well we only can do XSLT mapping in ICS. We need to do an if statement AS PARENT of the node, thus:

<xsl:if xml:id="id_27" test="/nssrcmpr:execute/nssrcdfl:request-wrapper/nssrcdfl:numberOfGuests">
    <nstrgdfl:numberOfGuests xml:id="id_20">
          <xsl:value-of xml:id="id_21" select="/nssrcmpr:execute/nssrcdfl:request-wrapper/nssrcdfl:numberOfGuests"/>
    </nstrgdfl:numberOfGuests>
</xsl:if>

This should work, when a value is empty it will never map anything. I went to test the first optional field, sending only the amount of guests. This worked!! Nice.

Postman call
Database entries

Second test, sending only the inside or outside choice and.. my integration failed. I was baffled, what was going on?? I checked the monitoring and it, it gave no errors? The integration call was successfull. So what went wrong then..? This makes no sense!

Postman call

Or does it??

ICS converts EVERYTHING to XML. This is very important to understand when working with JSON. I traced the payload and found out that it still sends my numberOfGuests field but empty!!
Here is the payload:

<nstrgdfl:response-wrapper>
   <nstrgdfl:id>3</nstrgdfl:id>
   <nstrgdfl:numberOfGuests/>
   <nstrgdfl:inOrOutside>inside</nstrgdfl:inOrOutside>
</nstrgdfl:response-wrapper>

Why did it only fail then with numberOfGuests=empty and not with inOrOutside=empty? I checked the payload when only sending numberOfGuests filled and keeping inOrOutside empty and the result was the an empty node as well:

<nstrgdfl:response-wrapper>
   <nstrgdfl:id>3</nstrgdfl:id>
   <nstrgdfl:numberOfGuests>4</nstrgdfl:numberOfGuests>
   <nstrgdfl:inOrOutside/>
</nstrgdfl:response-wrapper>

So what’s the difference between numberOfGuests and inOrOutside? Then I saw it, postman gave an empty string return on the inOrOutside field instead of no field what one would expect. This is due to the empty XML node that the SAXParser of ICS writes. After checking the server logs the error there confirmed my suspicion, it could not convert to an integer! Is there a solution??

Solutions

There are two solutions. The first is converting the output of the REST database call AND the output of the complete service to JSON string instead of integer. The second is more elegant but more work, you need to do TWO if statements when mapping the output of the REST service to the output of the Integration. We also check numberOfGuests != ” as well because of the empty XML node, note that this is comparing a field to an empty string (also works if the field is NOT a string itself, however the SAXParser makes it an empty string regardless):

<xsl:if xml:id="id_41" test="$patchReservationDB/nsmpr0:executeResponse/ns6:response-wrapper/ns6:numberOfGuests">
    <xsl:if xml:id="id_44" test="$patchReservationDB/nsmpr0:executeResponse/ns6:response-wrapper/ns6:numberOfGuests != """>
          <nstrgdfl:numberOfGuests xml:id="id_29">
                <xsl:value-of xml:id="id_30" select="$patchReservationDB/nsmpr0:executeResponse/ns6:response-wrapper/ns6:numberOfGuests"/>
          </nstrgdfl:numberOfGuests>
    </xsl:if>
</xsl:if>

(There are actually three solutions, the last one is a database trigger that always fills the numberOfGuests with 0 instead of empty in the DB. In this example that is feasable, in the real life situation that this example is based on that was not an option)

Nice! So my integration works (from postman). Please note that these errors also occur when we have a GET with an optional integer in the resultset.

Postman call

So what happens when I call this from a PCS activity and map these fields optional? Patching both fields works fine, Patching numberOfGuests works fine, patching inOrOutside… does not, again the numberOfGuests is mapped empty in the input of the integration giving an error on the input side of PATCH on the REST DB call. The DB does not like an empty string in an integer field and gives an error (rightfully so).
So we need to include the empty check on the input side as well, giving:

<xsl:if xml:id="id_27" test="/nssrcmpr:execute/nssrcdfl:request-wrapper/nssrcdfl:numberOfGuests">
    <xsl:if xml:id="id_34" test="/nssrcmpr:execute/nssrcdfl:request-wrapper/nssrcdfl:numberOfGuests != """>
          <nstrgdfl:numberOfGuests xml:id="id_20">
                <xsl:value-of xml:id="id_21" select="/nssrcmpr:execute/nssrcdfl:request-wrapper/nssrcdfl:numberOfGuests"/>
          </nstrgdfl:numberOfGuests>
    </xsl:if>
</xsl:if>

Finally a working optional patch integration. This is very cumbersome to do for every field though if your entity has for example 15 optional fields, so I really hope there will be an out of the box solution for this in the future! If that happens I’ll will of course post that on this blog again so.. subscribe!

Leave a comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: