How to validate Multiple Methods using JSON/XSD validator in WSO2 - wso2

I need to validate in-coming JSON request structure in WSO2 I was able to do that but there are 2 different methods GET and POST in the same context so validation applies to both the methods but I dont need validation for GET method. How to restrict validation for only one method.
I applied filters but it was not working can someone help me on how to achieve this.
Tool: WSO2 API manger 2.6.0.
I am trying with below code but unable to publish it. Please help me on this.
<property name="Resource" expression="get-property('axis2', 'REST_URL_POSTFIX')"/>
<property name="requestMethod" expression="get-property('axis2', 'HTTP_METHOD')"/>
<filter source="$ctx:Resource" regex="/books">
<filter source="$ctx:requestMethod" regex="POST">
<than>
<validate>
<schema key="conf:/LocalBkStore.json"/>
<on-fail>
<payloadFactory media-type="json">
<format>...</format>
<args>
<arg evaluator="xml" expression="$ctx:ERROR_MESSAGE"/>
</args>
</payloadFactory>
<property name="HTTP_SC" value="500" scope="axis2"/>
<respond/>
</on-fail>
</validate>
<than>
</filter>
</filter>

Can achieve by adding a custom in-flow sequence in <apim-home>/repository/resources/customsequences/in as below,
<sequence xmlns="http://ws.apache.org/ns/synapse" name="detect_get_post">
<property name="requestURL" expression="get-property('axis2', 'REST_URL_POSTFIX')"/>
<property name="requestMethod" expression="get-property('axis2', 'HTTP_METHOD')"/>
<filter source="$ctx:requestURL" regex="/books">
<then>
<filter source="$ctx:requestMethod" regex="POST">
<then>
//POST request having the URL path /books and you can do the validation here
</then>
</filter>
</then>
</filter>
You can copy and paste the above code snippet and add the validation part where I have added a comment.
In the code snippet you have added, after the first filter mediator there should be a then tag and also there is a spelling mistake in the then tag of second filter mediator.

Related

How to use filter for more than one parameter of a JSON message

I'm using ESB-4.9.0 version.
An ESB mediation flow has to be continued based on two filtering logic. JSON message is transformed in the mediation flow. Currently, I'm using two Filter mediators to achieve this. Is there any possibility to use a single filter mediator to fulfill the same scenario?
Input JSON message
{
"filterId": "CorrectId",
"approvalStatus": "approved",
"lifeCycleStatus": "BRANCH_READY",
"channelData": [
{
"status": "pending",
"indexId": "correctIndexId",
"description": "Test Description"
}
]
}
The used ESB Synapse part
<filter description="" regex="CorrectId" source="json-eval($.filterId)">
<then>
<filter description="" regex="correctIndexId" source="json-eval($.indexId)">
<then>
<!-- continue the mediation flow-1-->
</then>
<else>
<!-- continue the mediation flow-2-->
</else>
</filter>
</then>
<else>
<drop/>
</else>
</filter>
Yes, this is possible with a filter mediator and the xpath concat function:
<resource methods="POST" uri-template="/onefilter">
<inSequence>
<property name="filterId" expression="json-eval($.filterId)"/>
<property name="correctIndexId" expression="json-eval($.channelData[0].indexId)"/>
<property name="combinedID" expression="fn:concat($ctx:filterId, $ctx:correctIndexId)"/>
<filter source="$ctx:combinedID" regex="CorrectIdcorrectIndexId">
<then>
<log level="custom">
<property name="message" value="continue 1"/>
</log>
<drop/>
</then>
<else>
<log level="custom">
<property name="message" value="continue 2"/>
</log>
<drop/>
</else>
</filter>
</inSequence>
By the way your code snippet would NEVER go into the continue 1 route because your JSON path is invalid.

What expression have I to use to perform a choice related to a property value in a WSO2 ESB filter mediator?

I am very new in WSO2 ESB and I have the following doubt about how to implement an "if(){...} else{...}" like structure in my ESB project.
So in the input flow of the application on which I am working I have this property mediator followed by a log mediator that simply print the value of this property, something like this:
<property expression="count(//ds:Sample)" name="total_samples" scope="default" type="STRING" xmlns:ds="http://ws.wso2.org/dataservice"/>
<log level="custom">
<property expression="$ctx:total_samples" name="total samples: "/>
</log>
This works fine.
This total_samples property contains the number of record obtained from a previous call of a DSS service (I am not putting here in the code).
So the value of this total_samples property could be:
0: if the the query implemented by the DSS service returned 0 records.
A numeric value >0: if this query returned some records.
Now what I need to do at this time is only to chain a n "if(){...} else{...}" structure that print different log message if the total_samples property value is 0 or whatever number >0.
It should be a ver simple task but I have some doubts about how achieve it:
FIRST DOUBT: Looking on the online documentation it seems to me that exists 2 mediator that can be used to perform choice in the WSB flow: the switch mediator and the filter mediator. They seems to me very similar. What are the difference between these mediators? And what is better for my purpose?
SECOND DOUBT: It seems to me that these mediators works only on XPATH expression (something like count(//ds:Sample)), can they work directly on my property (something like "$ctx:total_samples") ?
THIRD DOUBT: At this stage I have implemented something like this in my flow:
<property expression="count(//ds:Sample)" name="total_samples" scope="default" type="STRING" xmlns:ds="http://ws.wso2.org/dataservice"/>
<log level="custom">
<property expression="$ctx:total_samples" name="total samples: "/>
</log>
<filter xpath="EXPRESSION THAT DO SOMETHING LIKE: $ctx:total_samples == 0">
<then>
<log description="No Resource Log">
<property name="message" value=""EMPTY RESULTSET, NO RESOURCES TO PROCESS""/>
</log>
</then>
<else>
<log description="Found Resource Log">
<property name="message" value=""Resources have been found, will be processed""/>
</log>
</else>
</filter>
Ok so my problem is: What have I to use as expression to enter in the case if the $ctx:total_samples value is 0 in the following line?
<filter xpath="EXPRESSION THAT DO SOMETHING LIKE: $ctx:total_samples == 0">
A more generic solution:
<?xml version="1.0" encoding="UTF-8"?>
<proxy xmlns="http://ws.apache.org/ns/synapse"
name="testIfElse"
transports="https http"
startOnLoad="true">
<target>
<inSequence>
<payloadFactory media-type="xml">
<format>
<ds:Sample xmlns:ds="http://ws.wso2.org/dataservice">
<ds:INT_ID>1</ds:INT_ID>
<ds:INT_ID>2</ds:INT_ID>
<ds:INT_ID>3</ds:INT_ID>
</ds:Sample>
</format>
<args>
</args>
</payloadFactory>
<property expression="count(//ds:Sample/ds:INT_ID)" name="total_samples" scope="default" xmlns:ds="http://ws.wso2.org/dataservice" type="DOUBLE"/>
<property value="0" name="initial_value" scope="default" type="DOUBLE"/>
<property expression="fn:number($ctx:total_samples) > fn:number($ctx:initial_value)" name="result" scope="default"/>
<log level="custom">
<property expression="$ctx:initial_value" name="initial value: "/>
<property expression="fn:number($ctx:total_samples)" name="total samples: "/>
<property expression="$ctx:result" name="if total samples greater than initial value: "/>
</log>
<filter xpath="$ctx:result" regex="true">
<then>
<log description="Found Resource Log">
<property name="message" value=""Resources have been found, will be processed""/>
</log>
</then>
<else>
<log description="No Resource Log">
<property name="message" value=""EMPTY RESULTSET, NO RESOURCES TO PROCESS""/>
</log>
</else>
</filter>
</inSequence>
<outSequence>
<log level="full"/>
<drop/>
</outSequence>
<faultSequence/>
</target>
</proxy>
Use this expression
<filter xpath="fn:number(get-property('total_samples')) = fn:number(0)">
you are really asking three questions here so I'll try to answer all of them:
The Switch mediator allows for multiple cases, so for example you could have a case for count = 0, count = 1 and count > 1. The filter mediator on the other hand is like the classic if/else. If this than do x, else do .
The filter mediator can either work comparing some value with a regular expression using the 'source' and 'regex' attributes in which case it checks if they match. Or it uses the xpath attribute in which case it evaluates the result of the xpath expression as a boolean. In the xpath expression as well as the source you can refer directly to your property using $ctx. For example:
What you could do is
<filter xpath="fn:number($ctx:total_samples) = fn:number(0)">

How to customize error XML response from api manager to a simple JSON message?

When invoking a published API with invalid access token, a XML response was got.
900901
Invalid Credentials
Access failure for API: /sit/zxq/oapi/ut/1.0, version: 1.0 with key: b645348f2ca7fea5a9cf498e4085a471. Make sure your have given the correct access token
How could we customize such response to following JSON format?
{
"req_id": "REQ_ENT_1356985018299_9678",
"err_resp": {
"code": "28001",
"msg": "Invalid access token"
}
}
Thanks and looking forward to your expertise.
We've recently done something similar with our API Manager implementation. You can find the fault sequences in /repository/deployment/server/synapise-configs/default/sequences, match on the error code, and provide your own JSON content. You can also use a switch mediator (the fault sequences are just mediation sequences) to return the correct content type for various Accept header values. Just replace the payload written in the fault.xml file with the equivalent JSON content (or as suggested, write a switch to allow the support of both content types).
You need to target the error codes from https://docs.wso2.com/display/AM260/Error+Handling and update it to your custom JSON messages. For auth token related errors try modify _auth_failure_handler_ as below:
<sequence name="_auth_failure_handler_" xmlns="http://ws.apache.org/ns/synapse">
<property name="error_message_type" value="application/json"/>
<filter source="get-property('ERROR_CODE')" regex="405">
<then>
<sequence key="converter"/>
<drop/>
</then>
<else>
</else>
</filter>
<filter source="get-property('ERROR_CODE')" regex="900901">
<then>
<sequence key="invalidCredential"/>
<drop/>
</then>
<else>
</else>
</filter>
<filter source="get-property('ERROR_CODE')" regex="900902">
<then>
<sequence key="missingCredential"/>
<drop/>
</then>
<else>
</else>
</filter>
<sequence key="_cors_request_handler_"/>
For your case Invalid Credential has a 900901 code , so it will match and need to define invalidCredential.xml as below :
<?xml version="1.0" encoding="UTF-8"?>
<sequence xmlns="http://ws.apache.org/ns/synapse" name="invalidCredential">
<payloadFactory media-type="json">
<format>{ "req_id": "REQ_ENT_1356985018299_9678", "err_resp": { "code": "28001", "msg": "Invalid access token" } </format>
<!--Add your custom message and format here. This will be your output-->
</payloadFactory>
<property name="RESPONSE" value="true"/>
<header name="To" action="remove"/>
<property name="HTTP_SC" value="401" scope="axis2"/>
<property name="messageType" value="application/json" scope="axis2"/>
<send/>
</sequence>

WSO2 ESB: How to Aggregate Payloads from Nested Iterators

I'm attempting to produce a stream of comments from a Facebook page. Ultimately I'd like a response from WSO2 like this:
<comments>
<comment>
<post_id>123</post_id>
<comment_id>456</comment_id>
<from_name>Bob Brown</from_name>
<from_id>789</from_id>
<message>This is a comment on a post.</message>
<created_time>2015-12-17T15:25:30+0000</created_time>
</comment>
</comments>
I'm using the API module for WSO2 ESB to provide an abstraction layer over a Facebook page to get a simple stream of all the comments on a page after a given timestamp.
The logic I'm working on right now is taking all the posts on a given Facebook page (using the WSO2 Facebook Connector), iterating over all the posts (using an iterate mediator), checking if the post has comments (using the filter mediator), if there are comments I'm then iterating over the comments and restructuring them into a simple XML element (using the PayloadFactory mediator). This is where I'm getting stuck.
I've figured out that within an iterate mediator I can't update properties external to the iterator. My initial instinct was to enrich an external property with the comment payload generated in the second iterator as a child element, but no dice.
I'm now attempting to aggregate the outputs of the second iterator as shown below but I'm not able to aggregate the payloads:
<?xml version="1.0" encoding="UTF-8"?>
<inSequence xmlns="http://ws.apache.org/ns/synapse">
<property expression="$ctx:query.param.limit" name="limit"
scope="default" type="STRING"/>
<property expression="$ctx:query.param.since" name="since"
scope="default" type="STRING"/>
<property name="fields" scope="default" type="STRING" value="id,comments{id,message,from,created_time}"/>
<call-template target="facebook_getFeed">
<with-param name="pageId" value="{get-property('uri.var.page')}"/>
<with-param name="maxPosts" value="{get-property('limit')}"/>
<with-param name="since" value="{get-property('since')}"/>
<with-param name="fields" value="{get-property('fields')}"/>
</call-template>
<log level="custom">
<property name="S3_MESSAGE" value="Iterating through posts"/>
</log>
<property name="comment_stream" scope="default">
<comments xmlns=""/>
</property>
<iterate continueParent="true" expression="//jsonObject/data"
id="fb_api_comments_post_iterate" sequential="true" xmlns:ns1="http://cache.services">
<target>
<sequence>
<filter xmlns:ns="http://org.apache.synapse/xsd" xpath="count(//data/comments)>0">
<then>
<property expression="//data/id"
name="fb_post_id" scope="operation" type="STRING"/>
<iterate continueParent="true"
expression="//data/comments/data"
id="fb_api_comments_comment_iterate" sequential="true">
<target>
<sequence>
<log level="custom">
<property name="S3_MESSAGE" value="Transforming comment"/>
</log>
<payloadFactory media-type="xml">
<format>
<comments xmlns="">
<post_id>$1</post_id>
<comment_id>$2</comment_id>
<from_name>$3</from_name>
<from_id>$4</from_id>
<message>$5</message>
<created_time>$6</created_time>
</comments>
</format>
<args>
<arg evaluator="xml" expression="get-property('fb_post_id')"/>
<arg evaluator="xml" expression="//data/id"/>
<arg evaluator="xml" expression="//data/from/name"/>
<arg evaluator="xml" expression="//data/from/id"/>
<arg evaluator="xml" expression="//data/message"/>
<arg evaluator="xml" expression="//data/created_time"/>
</args>
</payloadFactory>
</sequence>
</target>
</iterate>
</then>
</filter>
</sequence>
</target>
</iterate>
<aggregate>
<correlateOn expression="fb_api_comments_comment_iterate"/>
<completeCondition>
<messageCount max="-1" min="-1"/>
</completeCondition>
<onComplete enclosingElementProperty="comment_stream" expression="//comments">
<loopback/>
</onComplete>
</aggregate>
</inSequence>
Any assistance would be greatly appreciated.
In your question, you mentioned property update was falling outside the iterate mediator. According to my knowledge this happens due to this reason.
Before calling each iteration cycle message context will be cloned and it will only be available in the context inside iterator target. Hence, we are unable to get the messages/properties updated inside the iterator which are in the default/synapse scope. If you want to store something global, use property with operation scope.
How solved this problem was,
Before iterator - assign the property to operation scope
Inside iterator - do modifications
Outside the iterator - assign the property to default scope (if you
want to take it back to default scope)
Use below structure as an example for the proxy service:
<!--assign the property to operation scope-->
<property name="ITERATOR_DATA_PAYLOAD"
expression="get-property('DATA_PAYLOAD')"
scope="operation"
type="OM"/>
<iterate xmlns:ns="http://org.apache.synapse/xsd"
continueParent="true"
expression="//bookstore/book"
sequential="true">
<target>
<sequence>
<!--if want to assign the property-->
<property name="DATA_PAYLOAD"
expression="get-property('operation','ITERATOR_DATA_PAYLOAD')"
type="OM"/>
</sequence>
</target>
</iterate>
<!--Outside the iterator-->
<property xmlns:ns="http://org.apache.synapse/xsd"
name="NEW_DATA_PAYLOAD"
expression="get-property('operation','ITERATOR_DATA_PAYLOAD')"
type="OM"/>
Whilst I'm happy for someone to post technically more correct answer, I bit the bullet because of time constraints and begrudgingly wrote some arbitrary JavaScript in a script mediator:
var payload = mc.getPayloadXML();
print("Starting FB Post Loop...");
var comments = new XML("<comments></comments>");
comments.comment = new XMLList();
for (var i = 0; i < payload[0].data.length(); i++){
print("Checking if post has comments...");
if(payload[0].data[i].comments.data.length() > 0){
print("Comments exist on this post...");
print("Post ID: " + payload[0].data[i].id);
for (var ii = 0; ii < payload[0].data[i].comments.data.length(); ii++){
print("Building comment entry...");
var comment = <comment/>;
comment.post_id = payload[0].data[i].id.toString();
comment.comment_id = payload[0].data[i].comments.data[ii].id.toString();
comment.from_id = payload[0].data[i].comments.data[ii].from.id.toString();
comment.from_name = payload[0].data[i].comments.data[ii].from.name.toString();
comment.message = payload[0].data[i].comments.data[ii].message.toString();
comment.created_time = payload[0].data[i].comments.data[ii].created_time.toString();
print(comment);
comments.comment += comment;
}
}
}
mc.setPayloadXML(comments);
IMHO having to fall back to custom code is fine, but often means a world of hurt from a maintainability perspective. The other side of the coin is that I'd genuinely like to leverage WSO2/Synapse mediators where possible, since that's what using the WSO2 ESB is all about!
Looking forward to reading/discussing other solutions.

WSO2 Api Manager Custom error messages

I want to customize the error messages sent by api manager, for instance when an access token is missing or expired. I've configured _auth_failure_handler_ to return messages in json as described here, and get responses like:
{"fault":{"code":"900902","message":"Missing Credentials","description":"Required OAuth credentials not provided"}}
I would like to modify the message description and remove the "code" altogether. Is there a way to do this? I've tried tweaking the sequence with no luck.
You need to target the error codes from https://docs.wso2.com/display/AM260/Error+Handling and update it to your custom JSON messages. For auth token related errors try modify _auth_failure_handler_ as below:
<sequence name="_auth_failure_handler_" xmlns="http://ws.apache.org/ns/synapse">
<property name="error_message_type" value="application/json"/>
<filter source="get-property('ERROR_CODE')" regex="405">
<then>
<sequence key="converter"/>
<drop/>
</then>
<else>
</else>
</filter>
<filter source="get-property('ERROR_CODE')" regex="900901">
<then>
<sequence key="invalidCredential"/>
<drop/>
</then>
<else>
</else>
</filter>
<filter source="get-property('ERROR_CODE')" regex="900902">
<then>
<sequence key="missingCredential"/>
<drop/>
</then>
<else>
</else>
</filter>
<sequence key="_cors_request_handler_"/>
For your case Missing Credential has a 900902 code , so it will match and need to define missingCredential.xml as below :
<?xml version="1.0" encoding="UTF-8"?>
<sequence xmlns="http://ws.apache.org/ns/synapse" name="missingCredential">
<payloadFactory media-type="json">
<format>{ "status": "Error","message": "Missing Credentials" }</format>
<!--Add your custom message and format here. This will be your output-->
</payloadFactory>
<property name="RESPONSE" value="true"/>
<header name="To" action="remove"/>
<property name="HTTP_SC" value="401" scope="axis2"/>
<property name="messageType" value="application/json" scope="axis2"/>
<send/>
</sequence>
It is not wise advice to modify the error code. Nevertheless, yes it is possible to modify the payload. Use the filter mediator and Json path and identify the data and use enrich mediator to modify the payload as you want.