Azure APIM-Not able to access json body values in liquid template - json

I'm trying to build an API in azure and trying to modify the incoming json request using liquid template via set body policy. But the json elements are coming as null
Incoming json request
{
"GetOrderDetailsResponse": {
"OrderId": 1,
"OrderDate": "2018-08-13",
"OrderLines": {
"OrderLine": [
{
"Product": "Pizza Margherita",
"Amount": 5
},
{
"Product": "Pizza Calzone",
"Amount": 2
},
{
"Product": "Pizza Funghi",
"Amount": 1
}
]
}
}
}
Policy code
<policies>
<inbound>
<base />
<return-response response-variable-name="existing response variable">
<set-status code="200" reason="OK" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body template="liquid">
{
"orderId" : "{{body.GetOrderDetailsResponse.OrderId}}",
"orderDate" : "{{body.GetOrderDetailsResponse.OrderDate | Date: "dd/MM/yyyy"}}",
"orderLines" : [
{% JSONArrayFor orderline in body.GetOrderDetailsResponse.OrderLines %}
{
"product" : "{{orderline.Product}}",
"amount" : "{{orderline.Amount}}"
}
{% endJSONArrayFor %}
]
}
</set-body>
</return-response>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
<!--Create JSON Response via liquid template-->
</outbound>
<on-error>
<base />
</on-error>
</policies>
Response is blank
{
"orderId": "",
"orderDate": "",
"orderLines": []
}
I'm new to this,please advise if i'm missing something obvious

As mentioned here in the official docs, the Content-Type header must be set to application/json for the body to parsed and available to the liquid template.
In this case, you could either make sure the request sent has it set or manually set it in the inbound scope (not inside return-response) using the set-header policy

Related

Parsing JSON Array in Azure APIM policy

I need some help Parsing JSON Array in Azure APIM policy. My input to my API is a JSON Array but I want the output of the API to be JSON without the "[]", I just can't get the policy to remove them.
Here is the JSON Array input to my API:
[
{
"id": "myId",
"topic": "myTopic",
"subject": "/apis/test;Rev=1",
"data": {
"resourceUri": "myResourceUri"
},
"eventType": "Microsoft.ApiManagement.APIUpdated",
"dataVersion": "1",
"metadataVersion": "1",
"eventTime": "2022-09-08T14:22:46.7708654Z"
}
]
But I would like the Output of the policy to remove the square brackets [], like this:
{
"id": "myId",
"topic": "myTopic",
"subject": "/apis/test;Rev=1",
"data": {
"resourceUri": "myResourceUri"
},
"eventType": "Microsoft.ApiManagement.APIUpdated",
"dataVersion": "1",
"metadataVersion": "1",
"eventTime": "2022-09-08T14:22:46.7708654Z"
}
Here is my policy (which I copied from a tutorial and manipulated):
<policies>
<inbound>
<base />
<set-variable value="#(context.Request.Headers["Aeg-Event-Type"].Contains("SubscriptionValidation"))" name="isEventGridSubscriptionValidation" />
<set-variable value="#(context.Request.Headers["Aeg-Event-Type"].Contains("Notification"))" name="isEventGridNotification" />
<choose>
<when condition="#(context.Variables.GetValueOrDefault<bool>("isEventGridSubscriptionValidation"))">
<return-response>
<set-status code="200" reason="OK" />
<set-body>#{
var events = context.Request.Body.As<string>();
JArray a = JArray.Parse(events);
var eventGridData = a.First["data"];
var validationCode = eventGridData["validationCode"];
var jOutput =
new JObject(
new JProperty("validationResponse", validationCode)
);
return jOutput.ToString();
}</set-body>
</return-response>
</when>
<when condition="#(context.Variables.GetValueOrDefault<bool>("isEventGridNotification"))">
<send-one-way-request mode="new">
<set-url>https://hooks.slack.com/services/mySlackHandle</set-url>
<set-method>POST</set-method>
<set-body>#{
var events = context.Request.Body.As<string>();
JArray a = JArray.Parse(events);
var eventGridData = a.First["data"];
var song = eventGridData["song"];
return new JObject(
new JProperty("text", String.Format(" {1}",
song, a))).ToString();
}</set-body>
</send-one-way-request>
<return-response>
<set-status code="200" reason="OK" />
</return-response>
</when>
</choose>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
ANY HELP GREATLY APPRECIATED! Extra points if you can remove the reference to "song", which I cannot without breaking the policy...
You have to convert your json array to a json object like this:
<set-body>#(Newtonsoft.Json.JsonConvert.SerializeObject((JObject)(context.Request.Body.As<JArray>(preserveContent: true))[0]))</set-body>
Your can remove the serialization if you want to keep the body as a json object. But the trick is to capture the body as a JArray and then use the [0] to choose the "first" object in the array and thereafter convert your body to a JObject.

output of the json not formatted in APIM developer portal

output of the json not formatted in APIM developer portal and HTTP response gives as follows also
HTTP/1.1 200 OK
api-supported-versions: 1.0,2.0
content-length: 1979
content-type: application/json; charset=utf-8
date: Tue, 06 Sep 2022 11:50:38 GMT
strict-transport-security: max-age=15552001; includeSubDomains; preload
in above it say content-type: application/json but out put as below (in the developer portal responses section content type also "application/json" )
{
"totalRowCount": 1,
"data": [
{
"Code": "",
"prCode": "CW1208",
"Name": "CW1208 Quarterly Milestones",
"description": "",
"CategoryCode": "Efficiency",
"dataSource": "",
"custodian": "",
"TimeframeCode": "Lag",
"TypeName": "Single",
"active": true,
"InputTypeCode": "Input",
"rOfficer": "Stephen MCKAY",
"rOfficerCode": "Stephen ",
"AggregationMethodCode": "Average",
"reportingPeriod": "Quarter(s)",
"responsibleOfficer": "Stephen ",
"unit": "%",
"method": "MOREISBETTER",
"datefrom": "2021-04-01T00:00:00",
"dateto": "2021-06-30T00:00:00",
"target": 100.00,
"actual": 0.00,
"variance": 0.00,
"performance": 0.000000,
"trafficLight": "OFFTRACK",
"comment": "Quarterly comment.."
}
]
}
Any idea how to fix this?
This is how I added the response
in the developer portal example output
all operation policies
<set-header name="Token" exists-action="skip">
<value>#(context.Request.OriginalUrl.Query.GetValueOrDefault("Token"))</value>
</set-header>
<choose>
<!--QA-->
<when condition="#(context.Subscription.PrimaryKey.Equals("8ug4ac4a02"))">
<set-backend-service base-url="https://xxxxx.com/API/api/v1/" />
</when>
<!--Other Clients-->
<otherwise>
<set-backend-service base-url="https://xxxxemo.xxxx.com/api/v1/" />
</otherwise>
</choose>
<base />
</inbound>
<backend>
<base />
</backend>
<outbound>
<set-header name="X-Powered-By" exists-action="delete" />
<set-header name="X-AspNet-Version" exists-action="delete" />
<set-body template="none"></set-body>
<choose>
<when condition="#(context.Response.StatusCode == 404)">
<set-status code="200" reason="No Records Found" />
<set-body template="none">{"totalRowCount":0,"data":[{"":""}]}</set-body>
</when>
</choose>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
Please set the response content type to application/json.
Outbound policy:
<outbound>
<set-header name="X-Powered-By" exists-action="delete" />
<set-header name="X-AspNet-Version" exists-action="delete" />
<set-body template="none"></set-body>
<choose>
<when condition="#(context.Response.StatusCode == 404)">
<set-status code="200" reason="No Records Found" />
<!--
<set-body template="none">{"totalRowCount":0,"data":[{"":""}]}</set-body>
-->
<set-body>#{
var body = new JObject();
body["totalRowCount"] = 0;
var data = new JArray();
var dataValue = new JObject();
dataValue[""] = "";
data.Add(dataValue);
body["data"] = data;
return body.ToString();
}</set-body>
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
</when>
</choose>
<base />
</outbound>
Result:

Apply compression to a mocked response

Is it possible to apply compression (gzip) to a mocked response? Or is it limited to the outbound section? I tried to apply mocking in the outbound section but then I would need to "skip" the entire <backend> section as there is no backend yet..
This policy below works and I get a nice sample json back, but it's not compressed.
<policies>
<inbound>
<base />
<set-header name="Content-Encoding" exists-action="override">
<value>gzip</value>
</set-header>
<mock-response status-code="200" content-type="application/json" />
</inbound>
<backend />
<outbound />
<on-error>
<base />
</on-error>
</policies>
I actually got it to work using <return-response> instead. So if anyone in the future stumbles upon this, here is the complete policy:
<policies>
<inbound>
<base />
<return-response>
<set-status code="200" reason="OK" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-header name="Content-Encoding" exists-action="override">
<value>gzip</value>
</set-header>
<set-body>{
"success": true,
"data": {
"foo": [
{
"id": 1,
"bar": "xxx"
},
{
"id": 2,
"bar": "yyy"
},
{
"id": 3,
"bar": "zzz"
}
]
}
}</set-body>
</return-response>
</inbound>
<backend />
<outbound />
<on-error>
<base />
</on-error>
</policies>

Azure Api Management returns detail error when backed is off include oath token

I am working on API Management, the backend is ecured with oauth client credentials. If backend is off, I get the response of 200 and detail error as below.
{
"error": {
"name": "StatusCodeError",
"statusCode": 404,
"message": "HTTP Error 404. The requested resource is not found.",
"options": {
"url": "https://...net.au/api/case/mycases/",
"method": "GET",
"headers": {
"Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhb....."
},
"simple": true,
"resolveWithFullResponse": false,
"transform2xxOnly": false
},
"response": {
"statusCode": 404,
"body": "HTTP Error 404. The requested resource is not found.",
"headers": {
"content-length": "315",
"content-type": "text/html; charset=us-ascii",
"server": "Microsoft-HTTPAPI/2.0 Microsoft-HTTPAPI/2.0",
"date": "Fri, 14 Jun 2019 02:12:36 GMT",
"connection": "close"
},
"request": {
"uri": {
"protocol": "https:",
"slashes": true,
"auth": null,
"host": ".....",
"port": 443,
"hostname": "....net.au",
"hash": null,
"search": null,
"query": null,
"pathname": "/api/case/mycases/",
"path": "/api/case/mycases/",
"href": "https://...."
},
"method": "GET",
"headers": {
"Authorization": "Bearer eyJ0eXAiO....."
}
}
}
},
"status": 501
}
I only want to return the response like that when an api is called. and hide all extra details include access token.
{
"error": {
"name": "StatusCodeError",
"statusCode": 404,
"message": "HTTP Error 404. The requested resource is not found.",
}
}
as per the answer below, I have updated my policy, I am getting desired response when backend is offline but empty response when backend is online.
<choose>
<when condition="#{
var token = context.Response.Body.As<JToken>();
if (token is JObject){
return true;
}
return false;
}">
<return-response>
<set-status code="404" reason="NotFound" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>{
"error": {
"name": "StatusCodeError",
"statusCode": 404,
"message": "HTTP Error 404. The requested resource is not found.",
}
}</set-body>
</return-response>
</when>
</choose>
There are two cases when you can get 404 response from APIM. One is when you try to call an API/operation that APIM does not know of, in that case APIM will generate 404 response and it will be very short, much like your second one.
The other case is when APIM identifies API/operation, makes a call to backend and backend responds with 404. In that scenario APIM does not treat this as a problem, and merely relays backend response to client. What you have in your first example looks like something backend would reply with. You can confirm that by making a call from test console in Azure portal and inspecting provided trace.
So what you want to do is to replace body of 404 response with one of your choosing, that can easily be done with policies. At any scope put in something along these lines:
<outbound>
<base/>
<choose>
<when condition="#(context.Response.StatusCode == 404)">
<return-response>
<set-status code="404" reason="NotFound">
<set-header name="Content-Type">
<value>application/json</value>
</set-header>
<set-body>{
"error": {
"name": "StatusCodeError",
"statusCode": 404,
"message": "HTTP Error 404. The requested resource is not found.",
}
}</set-body>
</return-response>
</when>
</choose>
</outbound>

Replace value in Json in Dataweave

The mule payload is in flowVars (infodata)
[
{
"status": "submitted",
"identity": "",
"papers": {
"code1": "12csd3cbsdj",
"code2": "skd02nd28dn",
"date": "2016-06-22",
"party": {
"due_date": "2016-06-22",
"personel": {
"email": "tt#test.com",
"value": {
"amount": "0.10",
"inflation": "HIGH"
}
}
}
}
}
]
Inside Dataweave,
(1) how to remove the square brackets?
(2) how to replace the value of amount and inflation dynamically (from flowVars)?
Question 2:You can directly use flowVars inside dataweave or if the values are in url you can dynamically set values using inboundProperties. Refer: https://docs.mulesoft.com/mule-user-guide/v/3.7/dataweave-reference-documentation
I have used set variable where you can dynamically exact it.Test Url used in this flow: http://localhost:8083/test?inflation=HIGH
<flow name="testFlow">
<http:listener config-ref="HTTP_Listener_Configuration" path="/" doc:name="HTTP">
</http:listener>
<logger level="INFO" doc:name="Logger"/>
<set-variable variableName="dynamicValueAmount" value="#['2']" doc:name="Variable"/>
<dw:transform-message doc:name="Transform Message">
<dw:set-payload><![CDATA[%dw 1.0
%output application/json
---
[
{
"status": "submitted",
"identity": "",
"papers": {
"party": {
"due_date": "2016-06-22",
"personel": {
"email": "tt#test.com",
"value": {
"amount": flowVars.dynamicValueAmount,
"inflation": inboundProperties.'http.query.params'.inflation
}}}}}
]]]></dw:set-payload>
</dw:transform-message>
<object-to-string-transformer doc:name="Object to String"/>
<set-payload value="#[ message.payload =org.mule.util.StringUtils.replace(message.payload,"[","{");message.payload =org.mule.util.StringUtils.replace(message.payload,"]","}")]" doc:name="Set Payload"/>
<logger level="INFO" doc:name="Logger"/>
</flow>
Regarding Question one, i have externally used replace function in set Payload( Working fine - other way). I believe it can be achieve in standard way by using Dataweave itself. Let wait for the answers.
I have resolved question #1
%dw 1.0
%output application/java
---
{
data:flowVars.infodata replace /(\[|\])/ with ""
}
I'm still trying to understand how to dynamically change the content of the payload for question #2.
Looking at the sample flowVar content, it looks like an array of json object. Is that correct? If it is array then inside dataweave, you can either iterate over flowVars.infodata map {} or just get the first object data: flowVars.infodata[0].
Not sure what you really mean by dynamically changing content. #star has shown a way how you can reference any variables inside your dataweave code. May be you can add some of your code that you are looking to edit?