I have a json array (see below) which is logged in my wso2 sequence.
Payload: [{"id":"346","sys":"8","name":"A bldg"},{"id":"345","sys":"8","name":"bbldg 3"}
}]
Now I want to iterate the array. How do I do that? I want to log name for each row for now. I tried few options but it is not working, any help appreciated!.
My sequence looks like this:
<sequence name="a3" trace="enable" xmlns="http://ws.apache.org/ns/synapse">
<property expression="json-eval($.payload)" name="array"
scope="default" type="STRING" xmlns:ns="http://org.apache.synapse/xsd"/>
<foreach expression="//array/*" sequence="" xmlns:ns="http://org.apache.synapse/xsd"/>
<respond/>
</sequence>
As you have a JSON input so when it comes inside an ESB the format is changed to xml by default, so as i see that the json input is [{"id":"346","sys":"8","name":"A bldg"},{"id":"345","sys":"8","name":"bbldg 3"} }] , ESB will add a root element while converting it into XML.
Now you can use an XSLT to fetch the name or you can use an iterate mediator, which ever suits you best.
Don't take the payload to a property instead directly iterate as below,
<foreach expression="//payload">
<sequence>
<log>
<property expression="//name" name="name" scope="default" type="STRING"/>
</log>
</sequence>
</foreach>
Related
I have made Filter mediator to check email subject has specific keyword or not using REGEX.
<property value="Test SR AWS onboarding of AWS server" name="emailSubject" scope="default" type="STRING" />
<filter regex=".*SRAWS.*|.*SR AWS.*|.*SRSAP.*|.*SR SAP.*|.*SRFW.*|.*SR FW.*|.*SRSEC.*|.*SR SEC.*|.*INAWS.*|.*INSAP.*|.*INFW.*|.*INSEC.*"
source="get-property('emailSubject')">
<then>
<log level="custom">
<property name="==Test Case ===" value="pass" />
</log>
</then>
<else>
<log level="custom">
<property name="==Test Case ===" value="Fail" />
</log>
</else>
</filter>
Plenty of keywords(more than 60) are required in my case. I have hard coded keyword in Code, Instead of this, i am trying to store these keyword in somewhere(eg. localentry) and try to match subject with this to make code as generic.
Localentry:
<?xml version="1.0" encoding="UTF-8"?>
<localEntry key="EmailTicketing_Keyword" xmlns="http://ws.apache.org/ns/synapse">
<SR>.*SRAWS.*|.*SR AWS.*|.*SRSAP.*|.*SR SAP.*|.*SRFW.*|.*SR FW.*|.*SRSEC.*|.*SR SEC.*</SR>
</localEntry>
Reading from Localentry:
<property expression="get-property('EmailTicketing_Keyword')" name="tokenconfig" scope="default" type="OM"/>
<property expression="$ctx:tokenconfig//*[local-name()='SR']" name="SR" scope="default" type="STRING"/>
I am unable to use above property( SR) to match with subject in Filter Mediator.
Is there any way to achieve my use case?
PS: new Keyword may be added in future, to avoid code level changes whenever key word change required i just add new keyword in localentry instead of code which will work fine since keyword change is generic,That's why i am trying this.
Your localEntry as XML wont work, because it starts as . (dot) and wstx parser will throw error. Use instead LocalEntry as Text:
<localEntry xmlns="http://ws.apache.org/ns/synapse" key="RegTicketing">
.*SRAWS.*|.*SR AWS.*|.*SRSAP.*|.*SR SAP.*|.*SRFW.*|.*SR FW.*|.*SRSEC.*|.*SR
SEC.*
</localEntry>
For using that as regexp You need use ScriptMediator as below:
<property name="tokenconfig" expression="get-property('RegTicketing')" scope="default" type="STRING"/>
<script language="js">
var regStr = mc.getProperty('tokenconfig').toString();
var testStr = mc.getProperty('emailSubject').toString();
var regExp = new RegExp(regStr);
mc.setProperty('testResult',regExp.test(testStr).toString());
</script>
And you can use that testResult in FilterMediator:
<filter xpath="$ctx:testResult='true'">
As per the documentation [1] of the filter mediator, the regex only accepts a string. Therefore according to this you will not be able to dynamically set the value of the regex expression in the filter mediator.
As an alternative approach you can use a class mediator for this. You can feed the content of the local entry and the email subject, then evaluate the regex expression within the class mediator.
[1]-https://docs.wso2.com/display/EI660/Filter+Mediator+
Solution:
<script language="js"><![CDATA[var log = mc.getServiceLog();
log.info("===Inside keyword Match Script Mediator===" );
var emailSubject = mc.getProperty('transemailSubject').toString();
log.info("EmailSub::" + emailSubject);
var keywordSR = mc.getProperty('SR').toString();
//log.info("SR Keywords::" + keywordSR);
var regExpSR = new RegExp(keywordSR);
mc.setProperty('SR_Match',regExpSR.test(emailSubject).toString());
log.info("SR_Match::" + mc.getProperty('SR_Match'));]]></script>
<filter xpath="$ctx:SR_Match='true'">
<then>
<log level="custom">
<property name="==Test Case ===" value="pass" />
</log>
</then>
<else>
<log level="custom">
<property name="==Test Case ===" value="Fail" />
</log>
</else>
</filter>
What I'm trying to achieve:
Using an Enterprise Integrator API, I want to combine the data from two data service endpoints (call it ORDERS and METADATAS) into a custom object like so:
[
{
Order: {},
Metadata: {}
}, ...
]
The API takes in a single parameter at the moment, USER_ID.
When I run the <call> mediator in blocking mode, I get the following problem:
Current Params: {USER_ID={1,1}, ID=test}
or
Current Params: {USER_ID={test,1}}
When in non-blocking mode, the first call works successfully and the second call can't proceed. It seems it can't access the USER_ID parameter from the URL, nor from the stored property. I thought properties were supposed to persist values?
It should be noted that the values in the logs are 100% accurate (USER_ID property is showing correct value).
INFO {org.apache.synapse.mediators.builtin.LogMediator} - _TEST = 1, _TEST2 = 1, _TEST3 = 1
Here's what I've got thus far for the inSequence:
<resource methods="GET" uri-template="/test?USER_ID={USER_ID}">
<inSequence>
<property expression="get-property('query.param.USER_ID')" name="USER_ID" scope="default" type="STRING"/>
<property expression="$url:USER_ID" name="uri.var.USER_ID" scope="default" type="STRING"/>
<call blocking="true">
<endpoint>
<http method="get" statistics="enable" trace="enable" uri-template="https://localhost:8243/services/ORDERS_DataService/user/{query.param.USER_ID}"/>
</endpoint>
</call>
<log level="custom">
<property expression="get-property('USER_ID')" name="_TEST"/>
<property expression="get-property('query.param.USER_ID')" name="_TEST2"/>
<property expression="get-property('uri.var.USER_ID')" name="_TEST3"/>
</log>
<enrich>
<source clone="false" type="body"/>
<target property="_ORDERS" type="property"/>
</enrich>
<filter regex="200" source="get-property('axis2', 'HTTP_SC')">
<then>
<log level="custom">
<property name="switchlog" value="Case: first call successful"/>
</log>
<call blocking="true">
<endpoint>
<http method="get" statistics="enable" trace="enable" uri-template="https://localhost:8243/services/ORDERS_DataService/metadata/user/{query.param.USER_ID}"/>
</endpoint>
</call>
<enrich>
<source clone="false" type="body"/>
<target property="_METADATAS" type="property"/>
</enrich>
<log level="custom">
<property expression="get-property('_METADATAS')" name="_METADATAS"/>
</log>
</then>
<else/>
</filter>
</inSequence>
</resource>
What have I tried?
Using {USER_ID}, {uri.var.USER_ID}, {$url:USER_ID}, and {query.param.USER_ID} in both of the uri-templates for the <call>s.
Tried non-blocking and blocking mode, getting different results.
My problem is just (for now) with the calling of both sequences and assigning to properties.
I planned to bring them all together with an iterate, and something like the below to map the properties to each of the items in the array I'm looking for:
<!-- Assuming iterate provides _ORDER and _METADATA whilst looping -->
<format>
{
"Order":"$1",
"Metadata":"$2"
}
</format>
<args>
<arg expression="get-property('_ORDER')"/>
<arg expression="get-property('_METADATA')"/>
</args>
And if that doesn't work I was going to evaluate an XSLT transform on the two items, but I haven't thought that far ahead yet, stuck on the two calls not working properly.
I've figured out the answer to the issue. Found this curious line buried in the documentation [1]:
<property name="REST_URL_POSTFIX" scope="axis2" action="remove"/>
Adding it just after grabbing the query parameter works! It does exactly what it says, apparently a Postfix is applied by default. Now to look for a global way to turn that off automatically!
References:
[1] https://docs.wso2.com/display/EI650/Configuring+Specific+Use+Cases
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)">
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>
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.