WSO2 ESB - Iterator / Loop - wso2

Is it possible to Iterate through the elements of the xml file and call the sequence for each iteration without a send mediator within the Iterator. [Similar to Iterator Mediator without Send Mediator within it]
XML File:
<?xml version="1.0" encoding="UTF-8"?><Files><File>testValue1</File><File>testValue2</File></Files>
Expected Iterator:
<iterate xmlns:ns="http://org.apache.synapse/xsd" expression="$ctx:test//File" id="UTMIterator" sequential="true">
<target>
<sequence>
<property name="InsideSeq1" expression="//File"></property>
<class name="samples.mediators.SimpleClassMediator">
<property name="varible1" expression="$ctx:InsideSeq1"/>
</class>
<log level="custom">
<property name="text" value="***** Inside Iterator *****"></property>
</log>
</sequence>
</target>
</iterate>
Sample:
<iterate xmlns:ns="org.apache.synapse/xsd"; expression="$ctx:test//File" id="UTMIterator" sequential="true">
<target>
<sequence>
<log level="custom">
<property name="text" value="***** Inside Iterator **"></property>
<property name="InsideSeq1" expression="//File"></property>
<property name="text" value="** Inside Iterator *****"></property>
</log>
</sequence>
</target>
</iterate>
Note: Iterating the xml without the send mediator.
Any suggestion?

Yes you can. It is not a must to have send mediator.

Related

Is it possible to make your REST API redirect link dynamic/configurable?

I have a REST API on Enterprise Integrator that uses a db lookup mediator to search a microsoft sql server database and redirects based on the whether or not the data exists in the db. I need to make the redirect part of the code configurable/dynamic as it wouldn't make sense to constantly update the url and redeploy every time the url changes.
<api xmlns="http://ws.apache.org/ns/synapse" name="DBLookupAPI" context="/dblookup">
<resource methods="GET" uri-template="/{UserCode}">
<inSequence>
<log level="custom">
<property name="Value" expression="get-property('uri.var.UserCode')"/>
</log>
<dblookup>
<connection>
<pool>
<driver>com.microsoft.sqlserver.jdbc.SQLServerDriver</driver>
<url>jdbc:sqlserver://10.1.1.111\test;databaseName=UserDB</url>
<user>admin</user>
<password>admin</password>
</pool>
</connection>
<statement>
<sql>select UserCode from UserDB.dbo.Users where UserCode =?;</sql>
<parameter expression="get-property('uri.var.UserCode ')" type="CHAR"/>
<result name="foundnr" column="UserCode "/>
</statement>
</dblookup>
<log level="custom">
<property name="Value" expression="get-property('foundnr')"/>
</log>
<filter source="boolean(get-property('foundnr'))" regex="true">
<then>
<log>
<property name="Message" value="Name Exists Lets redirect"/>
</log>
<property name="HTTP_SC" value="302"/>
<property name="Location" value="https://wso2.com/" scope="transport"/>
</then>
<else>
<log>
<property name="Message" value="Name Does Not Exist Lets redirect"/>
</log>
<property name="HTTP_SC" value="302"/>
<property name="Location" value="https://www.youtube.com/" scope="transport"/>
</else>
</filter>
<respond/>
</inSequence>
<outSequence/>
<faultSequence/>
</resource>
</api>
You can use the property mediator as below.
<property name="Location" expression="get-property('registry','REGISTRY_PATH')"/>
Below are the possible options for get-property method.
Read from registry
get-property('registry', String registryPath#propertyName)
get-property('registry', String registryPath)
Read from Java System property
get-property('system', String propertyName)
Read from the environment
get-property('env', String propertyName)
Read from a file
get-property('file', String propertyName)
Read from the context
You can use the class mediator or any other mediator and set the redirect url to the context and use the following property mediator to retrieve it from the context.
<property name="Location" expression="$ctx:ERROR_MESSAGE"/>
Please refer the documentation - https://ei.docs.wso2.com/en/latest/micro-integrator/references/mediators/property-reference/accessing-properties-with-xpath/#get-property-function
There are different ways to do this. One way is to read from environment variables. In the following example the Location property is set from the environment variable named REDIRECT_URL.
<property name="Location" expression="get-property('env','REDIRECT_URL')" scope="transport"/>

What is the best way to iterate inside WSO2 EI?

After reading of WSO2 EI References, I still confuse about how use iterators inside a EI sequence.
In my case I have a payload like this....
{
...
"array": [
{"cpf": "12345678911"},
{"cnpj":"12345678912346"}
]
}
So I have to iterate to check if those guys exist using another web services. in order to achieve that flow, I am using the iterate mediator to split the message and then building the logic to make those validations as follows..
The code that implements this image is following:
<iterate description="" expression="//jsonObject/array" id="myid">
<target>
<sequence>
<property expression="json-eval($.array.cpf)" name="tipoCPF" scope="default" type="STRING"/>
<filter description="" xpath="boolean(get-property('tipoCPF'))">
<then>
<property expression="json-eval($.array.cpf)" name="uri.var.cpf" scope="default" type="STRING"/>
<call>
<endpoint>
<http method="get" uri-template="http://endpoint/service/{uri.var.cpf}"/>
</endpoint>
</call>
<filter regex="200" source="get-property('axis2','HTTP_SC')">
<then/>
<else>
<payloadFactory description="" media-type="json">
<format>{
"code":"400",
"error":"CPF inexistente"
}</format>
<args/>
</payloadFactory>
<property name="HTTP_SC" scope="axis2" type="STRING" value="400"/>
<respond/>
</else>
</filter>
</then>
<else>
<property expression="json-eval($.array.cnpj)" name="tipoCNPJ" scope="default" type="STRING"/>
<filter xpath="boolean(get-property('tipoCNPJ'))">
<then>
<property expression="json-eval($.array.cnpj)" name="uri.var.cnpj" scope="default" type="STRING"/>
<header name="Authorization" scope="transport" value="Basic Y29yZS5jb25zdWx0aW5nOm8xNXRyZWI="/>
<call>
<endpoint>
<http method="get" uri-template="http://endpoint/service/cnpj/{uri.var.cnpj}"/>
</endpoint>
</call>
<filter regex="200" source="get-property('axis2','HTTP_SC')">
<then/>
<else>
<payloadFactory media-type="json">
<format>{
"code":"400",
"error":"CNPJ inexistente"
}</format>
<args/>
</payloadFactory>
<property name="HTTP_SC" scope="axis2" type="STRING" value="400"/>
<respond/>
</else>
</filter>
</then>
<else>
<call>
<endpoint>
<http method="get" uri-template="http://endpoint/service/info"/>
</endpoint>
</call>
</else>
</filter>
</else>
</filter>
</sequence>
</target>
</iterate>
This iterator work as part inside of the an 'insequence'. The 'Insequence' is deigned to allow to insert new contracts information inside the Database.
Problem: after add this iterator, the service starts to make duplicated insertions inside Database. It´s looks like the iteration don´t finish in the edge of tags 'iterator'. It´s like the iteration continues to the rest of insequence.
Try: In order to solve this problem i try to add an aggregator mediator after the iterator. But or doesn't have any effect end the duplicated insert persist, or I receive an error message.
So What is the correct whey to make this iterations inside WSO2 EI?
As you have mentioned, Iteration will occur even outside the iterate tag, until the Aggregate mediator is used. To resolve this issue, you need to add the aggregate mediator inside the iterate mediator. This will stop the iteration within the iterator tag itself.
For your use case, you may need to set continueParent="true" in the IterateMediator, so that the mediation will continue after the iterate mediator for the insertion Operation in database.
Thanks for the helping Arunan!
After your answer I try to add the Aggregator as follows
The config of aggregator is following:
...
<aggregate id="NIRO">
<completeCondition>
<messageCount max="-1" min="-1"/>
</completeCondition>
<onComplete expression="//jsonObject">
<log description="" level="full" separator=";">
<property expression="json-eval($.)" name="jsonObject"/>
</log>
</onComplete>
</aggregate>
</sequence>
</target>
</iterate>
As you sad I change the iterator property 'Continue Parent' to 'true'. But the problem persists....
As proposed in the other answer, you need to
use an Aggregate mediator to close the iteration
If and only if the Iterator is set to continueParent="true"
However, I am not sure that putting it inside the <iterate> works. Here is a working solution using an Aggregate mediator right after your Iterator.
<sequence>
<iterate continueParent="true" description="" expression="//jsonObject/array" id="myid">
<target>
<your_sequence />
</target>
</iterate>
<aggregate>
<completeCondition>
<messageCount max="-1" min="-1" />
</completeCondition>
<onComplete enclosingElementProperty="//jsonObject/array" expression="/whatever/you/want"/>
</aggregate>
</sequence>
Notice expression="//jsonObject/array" you used in your Iteration, you'll need to use it in the Aggregator's enclosingElementProperty. This is how your EI will know from which iterator it should aggregate from (not 100% sure about this point, more of an empirical consideration).

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 respond with status 500 (server error) when proxy fails?

In our test environment we build a one-way proxy service that uses callout to get data from several other services. Now when one of those services fails wso2 proxy sends 202 accepted anyway. Is it possible to "catch exceptions" and return a different status?
I know that in this case it would be better to use a request-response proxy, but even if we would create a proxy that only stores the payload in a messagestore there could be issues regarding database where the payload would be stored (database is down, etc) and we don't want status 202 to be returned if the message was not stored in db.
edit:
Here is my proxy after changes suggested in first answer to my question:
<?xml version="1.0" encoding="UTF-8"?>
<proxy name="notifyOfClaimChangeOut" serviceGroup="" startOnLoad="true"
trace="disable" transports="http https" xmlns="http://ws.apache.org/ns/synapse">
<target>
<inSequence>
<property description="OUT_ONLY" name="OUT_ONLY" scope="default"
type="BOOLEAN" value="true"/>
<property name="FORCE_ERROR_ON_SOAP_FAULT" scope="default"
type="STRING" value="true"/>
<property description="OriginalPayload" expression="$body"
name="OriginalPayload" scope="default" type="STRING"/>
<iterate description=""
expression="$body//InsClaimData/PartnerList/PartnerEntry" id="" sequential="true">
<target>
<sequence>
<payloadFactory description="" media-type="xml">
<format>
<plat:FindCustomerSync xmlns:plat="http://platform.###.pl/">
<plat:request>
<plat:FirstName>$1</plat:FirstName>
<plat:LastName>$2</plat:LastName>
<plat:Pesel>$3</plat:Pesel>
</plat:request>
</plat:FindCustomerSync>
</format>
<args>
<arg evaluator="xml" expression="$body/PartnerEntry/BusinessPartner/personData/firstName"/>
<arg evaluator="xml" expression="$body/PartnerEntry/BusinessPartner/personData/lastName"/>
<arg evaluator="xml" expression="$body/PartnerEntry/BusinessPartner/personData/PESEL"/>
</args>
</payloadFactory>
<log level="custom">
<property expression="$body" name="property_name"/>
</log>
<callout action="http://platform.###.pl/FindCustomerSync"
description="QueryCustomersOut"
endpointKey="gov:###/endpoints/###/QueryCustomersOut.xml" initAxis2ClientOptions="false">
<source type="envelope"/>
<target key="QueryCustomersOutResponse"/>
</callout>
<log description="" level="custom" separator=",">
<property expression="$ctx:QueryCustomersOutResponse"
name="QueryCustomersOut"
xmlns:cla="http://###.###.io/service/internal/ClaimService" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"/>
</log>
</sequence>
</target>
</iterate>
<!--
<callout description="ClaimService"
endpointKey="gov:###/endpoints/###/ClaimService.xml" initAxis2ClientOptions="false">
<source type="envelope"/>
<target key="ClaimServiceResponse"/>
</callout>
<log description="ClaimService Response" level="custom">
<property expression="$ctx:ClaimServiceResponse" name="ClaimServiceResponse"/>
</log>
-->
<drop/>
</inSequence>
<outSequence/>
<faultSequence>
<property name="HTTP_SC" scope="axis2" type="STRING" value="500"/>
<log>
<property name="Error" value="Fault :("/>
</log>
</faultSequence>
</target>
<publishWSDL key="gov:###/schemas/###/ClaimService.wsdl"/>
</proxy>
You can use the HTTP_SC property to send status 500. You'll also need the makefault mediator to construct the fault message.
<property name="HTTP_SC" value="500" scope="axis2"/>
<makefault version="soap11">
You can refer this jira to get an idea.
In addition, in the inSequence, you may need to set FORCE_ERROR_ON_SOAP_FAULT property to move to the fault sequence when a soap fault occurs. Some explanation available here.
<inSequence>
<property name="FORCE_ERROR_ON_SOAP_FAULT" value="true" scope="default" type="STRING"/>
</inSequence>

Get Text from Envelope

I want to get text from following Envelope so that I can use it as REST_URL_POSTFIX.
I am sending a plain text message to a JMS queue and following WSO2-ESB proxy service is used as a receiver/listener of JMS queue. I have tried following expressions but they does not work :
$body
$body/text
$body/text()
The SOAP 1.1 or 1.2 body element. For example, the expression
$body/getQuote refers to the first getQuote element in a SOAP body,
regardless of whether the message is SOAP-11 or SOAP-12.
and
//Envelope//Body//text
//Envelope//Body//text()
INFO - (Send
LogMediator To: , MessageID: ID:sunnydyal-K55VM-44230-1439804805911-3:2:1:1:9,
Direction: request, ##### Message = ,
Envelope:
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<axis2ns8:text xmlns:axis2ns8="http://ws.apache.org/commons/ns/payload">Test</axis2ns8:text>
</soapenv:Body>
</soapenv:Envelope>
#Proxy Service:
<?xml version="1.0" encoding="UTF-8"?>
<proxy xmlns="http://ws.apache.org/ns/synapse"
name="JmsToRestProxy"
transports="jms"
statistics="enable"
trace="enable"
startOnLoad="true">
<target>
<inSequence>
<property name="OUT_ONLY" value="true"/>
<property name="HTTP_METHOD" value="GET" scope="axis2" type="STRING"/>
<property name="SOAPAction" scope="default" action="remove"/>
<header name="Action" scope="default" action="remove"/>
<property name="REST_URL_POSTFIX"
expression="//Envelope//Body//text"
scope="axis2"
type="STRING"/>
<switch source="$axis2:HTTP_METHOD">
<case regex="GET">
<property name="HTTP_METHOD" value="GET" scope="axis2" type="STRING"/>
</case>
<case regex="POST">
<property name="HTTP_METHOD" value="POST" scope="axis2" type="STRING"/>
</case>
<default/>
</switch>
<log level="full">
<property name="##### Message" expression="$body/text"/>
</log>
<send>
<endpoint>
<address uri="http://localhost:8080/Rest/rest" format="rest"/>
</endpoint>
</send>
</inSequence>
</target>
<parameter name="transport.jms.ContentType">
<rules>
<jmsProperty>contentType</jmsProperty>
<default>text/plain</default>
</rules>
</parameter>
<description/>
</proxy>
text node belong to a specific namespace "http://ws.apache.org/commons/ns/payload" :
<log level="full">
<property xmlns:syn="http://ws.apache.org/commons/ns/payload" name="##### Message" expression="$body/syn:text/text()"/>
</log>
You can get the body using the $body variable[1]. /text() is trying to access the text element of the body which doesn't exist.
<log level="custom">
<property name="Body" expression="$body" />
</log>
[1] - https://docs.wso2.com/display/ESB481/Synapse+XPath+Variables#SynapseXPathVariables-$body