Saturday, December 14, 2019

Header-Mediator in WSO2 ESB

Header Mediator:

The header mediator sets or removes a specified header from the current soap info-set. At the moment set header only supports simple valued headers.

Syntax

 <header name="qname" (value="literal" | expression="xpath") [action="set"]/>
 <header name="qname" action="remove"/>

The optional action attribute specifies whether the mediator should set or remove the header. If omitted, it defaults to a set-header.
When building SOAP or HTTP services using the WSO2 Enterprise Integrator or WSO2 Enterprise Service Bus it is sometimes necessary to either remove or add specific headers to a message which is being transmitted. To make this easy, the WSO2 product has the <header> mediator to help you accomplish this task.

For each example in this blog I use the following input message:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">   <soapenv:Header/>
   <soapenv:Body>
      <headerData>Thisismyheaderdata</headerData>
   </soapenv:Body>
</soapenv:Envelope>

HTTP headers:

Http headers are fairly simple to produce with the header mediator, you can choose the header name using the "name" property of the mediator.
As the "value" property the header value can be added and in the case of HTTP headers the "scope" property should be "transport".  
In the case of an "Accept" header it would look like this:
    <header name="Accept" value="text/xml" scope="transport"/>

SOAP headers

SOAP headers have a bit more variation in what's possible so not everything is covered by the header mediator.
First, we will cover what is possible with the header mediator.
Adding a simple SOAP header can be accomplished using something like the following header mediator configuration: 
  <header scope="default">
     <myHeaderName xmlns="https://www.yenlo.com">
          Iwanttosendthisheader
     </myHeaderName>
  </header>
This is the result:
<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Header>
      <myHeaderName xmlns="https://www.yenlo.com">          Iwanttosendthisheader      </myHeaderName>    </soapenv:Header>    <soapenv:Body/></soapenv:Envelope>
As shown above this will of course result in a hardcoded header, for something more dynamic you can use both (regular and synapse) xpath functionalities provided by the ESB.
For example: 
  1. Grabbing data from the incoming message element "headerData"
    <header xmlns:y="https://www.yenlo.com"  expression="//headerData" name="y:header" scope="default"/>
  1. Using Synapse xpath functionality to retrieve data from a property called "headerData"
    <header xmlns:y="https://www.yenlo.com"  expression="$ctx:headerData" name="y:header" scope="default"/>
  1. Or a combination of both where we specifically retrieve headerData from the body of the message.
        <header xmlns:y="https://www.yenlo.com" expression="$body/headerData" name="y:header" scope="default"/> 
Culminating into the following message (the headerData is also in the body as we did not change the initial incoming message):

<?xml version='1.0' encoding='utf-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">    <soapenv:Header>        <y:header xmlns:y="https://www.yenlo.com">         Thisismyheaderdata        </y:header>    </soapenv:Header>    <soapenv:Body>        <headerData>Thisismyheaderdata</headerData>    </soapenv:Body></soapenv:Envelope> 
Apart from dynamically adding data it is also possible to add more complex XML constructions inside the header if necessary. 
<header>
    <m:complexHeader xmlns:m="https://www.yenlo.com">
        <subPartOne expression="Some expression"/>
        <subPartTwo value="Some value"/> 
   </m:complexHeader>
</header>
As I mentioned earlier, there are things the header mediator doesn't support. This is the case when there is a need for dynamic headers inside a complex xml structure. Below there is an example shown where a security header UsernameToken needs to be filled, this can be done using the script mediator.
Within the script mediator a <![CDATA[some stuff]]> block is used because we need to create xml elements, the CDATA block functions to sure that they aren't interpreted as XML so the script can be executed.
To add the header the script mediator first retrieves the username and password from their specific properties. In this case the password is retrieved from the secure vault within the ESB.
<property value="myUserName" name="BackendUser" scope="default" type="STRING"/>
<property expression="wso2:vault-lookup(SomePasswordKey)" name="BackendPassword" scope="default" type="STRING"/>
<script language="js"><![CDATA[var password = mc.getProperty("BackendPassword"); var username = mc.getProperty("BackendUser"); mc.addHeader(false,<Security xmlns="http://schemas.xmlsoap.org/ws/2002/04/secext"><UsernameToken  xmlns="http://schemas.xmlsoap.org/ws/2002/04/secext">        
<Username xmlns="http://schemas.xmlsoap.org/ws/2002/04/secext">
{username}</Username><Password xmlns="http://schemas.xmlsoap.org/ws/2002/04/secext">{password}</Password></UsernameToken></Security>);]]></script>
 
The resulting message looks as follows: 
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Header>
      <Security soapenv:mustUnderstand="0" xmlns="http://schemas.xmlsoap.org/ws/2002/04/secext">
         <UsernameToken>
            <Username>BackendUserPropertyData</Username>
            <Password>BackendPasswordPropertyData</Password>
         </UsernameToken>
      </Security>
   </soapenv:Header>
   <soapenv:Body>
      <headerData>Thisismyheaderdata</headerData>
   </soapenv:Body>
</soapenv:Envelope>

Enrich Mediator - WSO2 ESB

Let’s discuss about Enrich mediator of WSO2 ESB. You can find it under core mediators.

Source - What will be using to do enriching ? (Ex: Property, inline content, body content etc.)
Target - Where and Action going to do Within the enrich process ? (Ex: Replace, add child to payload etc.)

I will here, list down main functionalities of Enrich mediator with regard to its available options. (I will give example scenarios for different Target Actions)

There are main three target actions as “Replace”, “Child” and “Sibling”. With the different source types (Inline, Property, Body etc.), you can perform number of different actions. I present basic sample scenarios for Target Actions.

1. Replace

Config:

<sequence name="enrich_seq_1" xmlns="http://ws.apache.org/ns/synapse">
    <property name="testProperty" scope="default" type="STRING" value="WSO2"/>
    <enrich>
       <source clone="true" property="testProperty" type="property"/>
       <target action="replace" type="custom"
           xmlns:ns="http://org.apache.synapse/xsd"
           xmlns:ser="http://services.samples" xpath="//ser:getSimpleQuote/ser:symbol/text()"/>
    </enrich>
    <log level="full"/>
</sequence>

Request:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://services.samples">
  <soapenv:Header/>
  <soapenv:Body>
     <ser:getSimpleQuote>
        <!--Optional:-->
        <ser:symbol>IBM</ser:symbol>
     </ser:getSimpleQuote>
  </soapenv:Body>

</soapenv:Envelope>

After Enriching

<?xml version='1.0' encoding='UTF-8'?>
  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://services.samples">
     <soapenv:Body>    
        <ser:getSimpleQuote>       
           <!--Optional:-->       
              <ser:symbol>WSO2</ser:symbol>    
           </ser:getSimpleQuote>   
        </soapenv:Body>
     </soapenv:Envelope>

2. Adding Child

Config:

<sequence name="enrich_seq_11" xmlns="http://ws.apache.org/ns/synapse">
    <log level="full"/>
    <enrich>
       <source clone="true" type="body"/>
       <target action="child" type="body"/>
    </enrich>
    <log level="full"/>
</sequence>
Request:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://services.samples">
  <soapenv:Header/>
  <soapenv:Body>
     <ser:getSimpleQuote>
        <!--Optional:-->
        <ser:symbol>IBM</ser:symbol>
     </ser:getSimpleQuote>
  </soapenv:Body>
</soapenv:Envelope>

After Enriching

<?xml version='1.0' encoding='UTF-8'?>
  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://services.samples">   
     <soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing"/>   
     <soapenv:Body>    
        <ser:getSimpleQuote>       
           <!--Optional:-->       
              <ser:symbol>IBM</ser:symbol>
              <ser:getSimpleQuote>       
                 <!--Optional:-->       
                    <ser:symbol>IBM</ser:symbol>    
                 </ser:getSimpleQuote>
              </ser:getSimpleQuote>   
           </soapenv:Body>
        </soapenv:Envelope>

3. Adding Sibling

Config:

<sequence name="enrich_seq_11" xmlns="http://ws.apache.org/ns/synapse">
   <log level="full"/>
   <enrich>
       <source clone="true" type="body"/>
       <target action="sibling" type="body"/>
   </enrich>
   <log level="full"/>
</sequence>

Request:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://services.samples">
  <soapenv:Header/>
  <soapenv:Body>
     <ser:getSimpleQuote>
        <!--Optional:-->
        <ser:symbol>IBM</ser:symbol>
     </ser:getSimpleQuote>
  </soapenv:Body>
</soapenv:Envelope>

After enriching

<?xml version='1.0' encoding='UTF-8'?>
  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://services.samples">   
     <soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing"/>   
     <soapenv:Body>    
        <ser:getSimpleQuote>       
           <!--Optional:-->       
              <ser:symbol>IBM</ser:symbol>    
           </ser:getSimpleQuote>
           <ser:getSimpleQuote>       
              <!--Optional:-->       
            <ser:symbol>IBM</ser:symbol>    
              </ser:getSimpleQuote>   
           </soapenv:Body>
        </soapenv:Envelope>


Note: As I mentioned earlier, this is basic functionalities u can do with Enrich mediator. Try out it through different aspects and find new mediation patterns ;)


How to delete a random element from the XML payload with the use of Script mediator in WSO2 ESB

In WSO2 ESB, we can use the Script Mediator manipulate a XML payload. Here I have used JavaScript/E4X for accessing/manipulating the elements.

Example XML payload;

<breakfast_menu>
   <food>
      <name>Belgian Waffles</name>
      <price>$5.95</price>
      <calories>650</calories>
   </food>
   <food>
      <name>Strawberry Belgian Waffles</name>
      <price>$7.95</price>
      <calories>900</calories>
   </food>
   <food>
      <name>Berry-Berry Belgian Waffles</name>
      <price>$8.95</price>
      <calories>900</calories>
   </food>

</breakfast_menu> 

Lets assume we want to remove the last food element (Berry-Berry Belgian Waffles); In this scenario, breakfast_menu is the root element and the set of children elements will be food

The length of the child elements (food) can be obtained as follows;


var payload = mc.getPayloadXML();
var length = payload.food.length();

Then delete the last element as follows; Here the index of the last element would be length-1

delete payload.cuidInfo[length-1];

Complete Script Mediator configuration would be as follows;
<script language="js">
    var payload = mc.getPayloadXML();
    var length = payload.food.length();
    delete payload.food[length-1];
    mc.setPayloadXML(payload);
</script>
The output of the script mediator would be as follows;
<breakfast_menu>
   <food>
      <name>Belgian Waffles</name>
      <price>$5.95</price>
      <calories>650</calories>
   </food>
   <food>
      <name>Strawberry Belgian Waffles</name>
      <price>$7.95</price>
      <calories>900</calories>
   </food>
</breakfast_menu>

NOTE:
<script language="js">var log = mc.getServiceLog();          var message = mc.getPayloadXML();          log.info(":: Message ::"+message);          log.info(":: Message Header ::"+message..*::Header.*::To);          delete message..*::Header..*::To;          mc.setPayloadXML(message);</script>
<script language="js">var log = mc.getServiceLog();          var message = mc.getPayloadXML();          log.info(":: Message ::"+message);          log.info(":: Message Header ::"+message..*::Header.*::To[0]);          delete message..*::Header..*::To[0];          mc.setPayloadXML(message);</script>

Tuesday, December 3, 2019

XML to Json Conversion in WSO2 ESB

<?xml version="1.0" encoding="UTF-8"?>
<api context="/get" name="GET-details" xmlns="http://ws.apache.org/ns/synapse">
    <resource methods="POST" uri-template="/validate?system={systemVal}&amp;user={userVal}&amp;otp={otpVal}">
        <inSequence>
            <property expression="$url:system" name="SYSTEM" scope="default" type="STRING"/>
            <property expression="$url:user" name="USER" scope="default" type="STRING"/>
            <property expression="$url:otp" name="OTP" scope="default" type="STRING"/>
            <log level="custom">
                <property expression="get-property('SYSTEM')" name="value-system"/>
                <property expression="get-property('USER')" name="value-user"/>
                <property expression="get-property('OTP')" name="value-otp"/>
            </log>
            <payloadFactory media-type="xml">
                <format>
                    <root>
                        <SYSTEM>$1</SYSTEM>
                        <USER>$2</USER>
                        <OTP>$3</OTP>
                    </root>
                </format>
                <args>
                    <arg evaluator="xml" expression="get-property('SYSTEM')"/>
                    <arg evaluator="xml" expression="get-property('USER')"/>
                    <arg evaluator="xml" expression="get-property('OTP')"/>
                </args>
            </payloadFactory>
            <log level="full"/>
            <property name="messageType" value="application/json" scope="axis2"/>
         
            <respond/>
        </inSequence>
        <outSequence/>
        <faultSequence/>
    </resource>
</api>

http://localhost:8280/get/validate?system=WSO2&user=1234&otp=0987--------Post

Monday, December 2, 2019

uri-template patterns in WSO2 ESB API

<api xmlns="http://ws.apache.org/ns/synapse" name="TEMP_API" context="/TEMP_API/otp">
<resource methods="POST" uri-template="/validate?system={systemVal}&amp;user={userVal}&amp;otp={otpVal}">
<inSequence>
<property name="SYSTEM" expression="$url:system" scope="default" type="STRING"/>
<property name="USER" expression="$url:user" scope="default" type="STRING"/>
<property name="OTP" expression="$url:otp" scope="default" type="STRING"/>
<log level="custom">
<property name="value-system" expression="get-property('SYSTEM')"/>
<property name="value-user" expression="get-property('USER')"/>
<property name="value-otp" expression="get-property('OTP')"/>
</log>
<property name="uri.var.urls" value="/user-administration-ws/authenticate?param=test" scope="default" type="STRING"/>
<property name="uri.var.user" expression="$trp:username" scope="default" type="STRING"/>
<property name="uri.var.password" expression="$trp:password" scope="default" type="STRING"/>
<call>
<endpoint>
<http method="POST" uri-template="http://localhost:8280/editing/edit?a={uri.var.user}&amp;b={uri.var.password}"/>
</endpoint>
</call>
</inSequence>
</resource>
</api>

------------------------------------------------------------------------------------
<api xmlns="http://ws.apache.org/ns/synapse" name="TEMP_API" context="/TEMP_API/otp">
<resource methods="POST" uri-template="/validate?system={systemVal}&amp;user={userVal}&amp;otp={otpVal}">
<inSequence>
<property name="SYSTEM" expression="$url:system" scope="default" type="STRING"/>
<property name="USER" expression="$url:user" scope="default" type="STRING"/>
<property name="OTP" expression="$url:otp" scope="default" type="STRING"/>
<property name="USERNAME" expression="$trp:username" scope="default" type="STRING"/>
<property name="PASSWORD" expression="$trp:password" scope="default" type="STRING"/>
<log level="custom">
<property name="value-system" expression="get-property('SYSTEM')"/>
<property name="value-user" expression="get-property('USER')"/>
<property name="value-otp" expression="get-property('OTP')"/>
<property name="value-username" expression="get-property('USERNAME')"/>
<property name="value-password" expression="get-property('PASSWORD')"/>
</log>
<property name="REST_URL_POSTFIX" value="" scope="axis2" type="STRING"/>
<property name="REST_URL_POSTFIX" expression="fn:concat(get-property('axis2','REST_URL_POSTFIX'), '?a=123')" scope="axis2" type="STRING"/>
<property name="REST_URL_POSTFIX" expression="fn:concat(get-property('axis2','REST_URL_POSTFIX'), '&amp;b=abcde')" scope="axis2" type="STRING"/>
<call>
<endpoint>
<address uri="http://localhost:8280/TEMP/editing/edit"/>
</endpoint>
</call>
</inSequence>
</resource>
</api>

--------------------------------------------------------------------------------------------------------
http://localhost:8280/get/validate?system=WSO2&user=1234&otp=0987&otp1=0987

<api xmlns="http://ws.apache.org/ns/synapse" name="GET-details" context="/get"> <resource methods="POST" uri-template="/validate?system={systemVal}&amp;user={userVal}&amp;otp={otpVal}&amp;otp1={otp1Val}"> <inSequence> <property name="SYSTEM" expression="$url:system" scope="default" type="STRING"/> <property name="USER" expression="$url:user" scope="default" type="STRING"/> <property name="OTP" expression="$url:otp" scope="default" type="STRING"/> <property name="OTP1" expression="$url:otp1" scope="default" type="STRING"/> <log level="custom"> <property name="value-system" expression="get-property('SYSTEM')"/> <property name="value-user" expression="get-property('USER')"/> <property name="value-otp" expression="get-property('OTP')"/> <property name="OTP1" expression="get-property('OTP1')"/> </log> <respond/> </inSequence> <outSequence/> <faultSequence/> </resource> </api>

sample GET method API(here getting details DS)

<api xmlns="http://ws.apache.org/ns/synapse" name="DataBase" context="/ds">
   <resource methods="GET" uri-template="/employee/{userId}">
      <inSequence>
         <property name="User Id" expression="get-property('uri.var.userId')" scope="default" type="STRING"/>
         <log level="custom">
            <property name="User Id" expression="get-property('User Id')"/>
         </log>
         <property name="ContentType" value="text/xml" scope="axis2" type="STRING"/>
         <property name="SOAPAction" value="urn:_getselect" scope="transport" type="STRING"/>
         <payloadFactory media-type="xml">
            <format>
               <p:select xmlns:p="http://ws.wso2.org/dataservice">
                  <xs:id xmlns:xs="http://ws.wso2.org/dataservice">$1</xs:id>
               </p:select>
            </format>
            <args>
               <arg xmlns:ds="http://ws.wso2.org/dataservice" evaluator="xml" expression="get-property('User Id')"/>
            </args>
         </payloadFactory>
         <log level="full" separator="," description="Request"/>
         <send>
            <endpoint key="DataServiceWsdl"/>
         </send>
      </inSequence>
      <outSequence>
         <respond/>
      </outSequence>
      <faultSequence/>
   </resource>
</api>
                        

Wednesday, November 20, 2019

vfs Transport in WSO2 ESB

<?xml version="1.0" encoding="UTF-8"?>
<proxy name="VFS" startOnLoad="true" transports="http https vfs" xmlns="http://ws.apache.org/ns/synapse">
    <target>
        <inSequence>
            <log level="custom">
                <property name="sequence" value="Proxy"/>
            </log>
            <clone>
                <target sequence="fileWriteSequence"/>
            </clone>
        </inSequence>
        <outSequence/>
        <faultSequence/>
    </target>
    <parameter name="transport.PollInterval">15</parameter>
    <parameter name="transport.vfs.FileURI">file:///D:/test/in/</parameter>
    <parameter name="transport.vfs.ContentType">text/plain</parameter>
    <parameter name="transport.vfs.ActionAfterProcess">MOVE</parameter>
    <parameter name="transport.vfs.MoveAfterFailure">file:///D:/test/FAILURE/</parameter>
    <parameter name="transport.vfs.ActionAfterFailure">MOVE</parameter>
    <parameter name="transport.vfs.FileNamePattern">.*.dat</parameter>
    <parameter name="transport.vfs.MoveAfterProcess">file:///D:/test/original/</parameter>
</proxy>

-----------------------------------------------------------------------------------------------------
fileWriteSequence:

<?xml version="1.0" encoding="UTF-8"?>
<sequence name="fileWriteSequence" trace="disable" xmlns="http://ws.apache.org/ns/synapse">
    <log level="custom">
        <property name="sequence" value="fileWriteSequence"/>
    </log>
    <property expression="fn:concat(fn:substring-after(get-property('MessageID'), 'urn:uuid:'), '.txt')" name="transport.vfs.ReplyFileName" scope="transport" type="STRING" xmlns:ns2="http://org.apache.synapse/xsd"/>
    <property name="OUT_ONLY" scope="default" type="STRING" value="true"/>
    <send>
        <endpoint name="FileEpr">
            <address uri="vfs:file:///D:/test/out/"/>
        </endpoint>
    </send>
</sequence>