A few weeks back I wrote an article about how I can use APIM to transform data which in turn made the workflow in my Logic App a lot simpler.
In this article we will look at a similar approach I used where combinging APIM and Logic Apps made the overall workflow simpler.
The problem that I had is that in my Logic App I am integrating with an application which has a generic SOAP endpoint which I can send many different types of message to. I have decided to place APIM between the logic app and the end application for the following reasons:
- I can centralize my HTTP calls through APIM for that central view of API use
- I can abstract the security for the end application by talking managed identity from Logic Apps to APIM and then APIM handles the security to the backend app
There are also other reasons you could use. The architecture looks like this.
The next thing to think about is the construction of the request message. There are a few different ways you could do this such as using a Compose shape, Using an Integration Account liquid map or doing a Liquid map in APIM. In my case I chose to add an operation which would call APIM and I would send my json data and it would return me the SOAP message which I would then send to the Generic SOAP Post Wrapper. I chose to seperate out the map from the operation which goes to the backend because I want the support operator to be able to see what the SOAP message would look like because its pretty complicated and it will help troubleshooting.
In this post I want to focus on how we managed the response so Ill leave the above paragraph at this point but if any readers are interested to know more about the above do let me know and I can blog about that.
So at this point we have mapped our request and sent it to the Generic Soap Endpoint and I get back a SOAP message which tells me if the create/update worked. The application returns the same acknowledgement regardless of the type of data you send.
In my Logic App I want to check if there was an error and if there was I will make the Logic App fail.
The challenge with a soap service like this is a failure does the following:
- returns a 200 HTTP response code
- Only returns a SOAP fault in infrastructure level issues
- If the record wasnt updated the message body tells me of the problem
The challenge for a Logic App here is it will assume a success if the response is a 200 so I need to process the message body and do some work to work out if there was an error or if I am safe to continue. It would look a bit like below.
I want to try to do a few things to make my life a bit easier, I am able to easily add a policy such as the below to APIM which will give the the response as json rather than XML.
<outbound>
<base />
<xml-to-json kind="javascript-friendly" apply="always" consider-accept-header="false" />
</outbound>
The json I get back looks like this.
{
"version": "1.0",
"encoding": "utf-8",
"SOAP-ENV$Envelope": {
"xmlns$SOAP-ENV": "http://schemas.xmlsoap.org/soap/envelope/",
"xmlns$Encoding": "http://schemas.xmlsoap.org/soap/encoding/",
"xmlns$enab": "enablon",
"SOAP-ENV$Body": {
"SOAP-ENV$encodingStyle": "http://schemas.xmlsoap.org/soap/encoding/",
"enab$ImportDataResponse": {
"Response": {
"RowCount": {
"$t": "1"
},
"RowSuc": {
"$t": "1"
},
"ErrMsg": null
}
}
}
}
}
I now need to parse the json and then make a decision if the RowSuc value is equal to 1 (success) or anything else (error). I end up with workflow logic like the below image.
You can see I am throwing an error with my throw error helper logic app. This will make the Logic App bubble up an error which will either fail it or jump to my catch scope shape if your using a try catch pattern.
This works pretty well but I now have the problem that I have 6+ calls to this generic soap endpoint for different data upload scenarios and I really dont fancy doing this logic over and over which falls a bit unnecessary.
I decided that I would add an operation to APIM which I would allow the Logic App to pass back the response message that it gets from the generic SOAP call and this operation will encapsulate checking if the response indicates success or failure and then it will either return a 200 or 500. If a 500 is returned it will automatically make the Logic App throw an error. It would look like the below.
If I now modify the Logic App to work with the new Validation check for the response then it looks like the below.
The trade off here is that I am making a call back to APIM, but its going to be super quick and I have used 1 APIM action to get rid of a condition and 3 other actions. The simplification and benefits to make testing easier are good and also for my 6+ times I would have needed to repeat that validation Logic, I now just have it in one place.
APIM Policy
Below is the policy I used in APIM where you can see that I used a liquid map to transform the json to an easier to use format handling the nested messy json from converting the soap to xml. I then used 2 small set variable actions to set the response code and status that I want (on reflection I could probably have done that in the liquid map too).
I then return a response to either indicate success or trigger an error using the HTTP 500 response
<policies>
<inbound>
<base />
<set-body template="liquid">
{
"RowCount":"{{body["SOAP-ENV$Envelope"]["SOAP-ENV$Body"]["enab$ImportDataResponse"]["Response"]["RowCount"]["$t"]}}",
"RowSuccess":"{{body["SOAP-ENV$Envelope"]["SOAP-ENV$Body"]["enab$ImportDataResponse"]["Response"]["RowSuc"]["$t"]}}"
}
</set-body>
<set-variable name="body" value="@(context.Request.Body.As<string>(preserveContent:true))" />
<set-variable name="responseCode" value="@{
var body = (string)context.Variables["body"];
var request = JObject.Parse(body);
var rowSuccess = request["RowSuccess"].ToString();
var responseCode = 200;
if(rowSuccess != "1")
{
responseCode = 500;
}
return responseCode;
}" />
<set-variable name="responseStatus" value="@{
var body = (string)context.Variables["body"];
var request = JObject.Parse(body);
var rowSuccess = request["RowSuccess"].ToString();
var responseStatus = "OK";
if(rowSuccess != "1")
{
responseStatus = "Error";
}
return responseStatus;
}" />
<return-response>
<set-status code="@((int)context.Variables["responseCode"])" reason="@((string)context.Variables["responseStatus"])" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>@{
var response = context.Request.Body.As<string>(preserveContent: true);
return response.ToString();
}</set-body>
</return-response>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
</policies>
Summary
Hopefully this gives you some ideas how combining APIM and Logic Apps can make your Logic Apps simpler and more managable. What kind of patterns are you using?