Tuesday, June 24, 2014

WSO2 API Manager- Extended Mediation Capabilities on APIs -Part1

After a while,thought to write a blog-post about how we can use extended mediation capabilities with the published APIs from WSO2 APIManager.

Requirement- A back-end  endpoint with returning xml content need to wrap with an API from WSO2 APIManager to give additional security,throttling and monitoring capabilities for it.

For this blog-post,as the back-end endpoint,I have used the sample JAX-RS based web-app which can be found from here,deployed in WSO2 AS 5.2.1.You can try downloading WSO2 AS 5.2.1 and try deploying this web-app as instructed in here.I have started AS with port offset 2.Thus the deployed jax-rs web-app url is http://localhost:9765/Order-1.0/
This jax-rs web app supports following HTTP verbs with the url-patterns;

POST  /submitOrder    Input & Output content-type : text/xml

Input Payload:


<Order>
<customerName>Jack</customerName>
<quantity>5</quantity>
<creditCardNumber>233</creditCardNumber>
<delivered>false</delivered>
</Order>

GET    /orderStatus     Output content-type : text/xml
GET    /cancelOrder    Output content-type : text/xml



Then download the latest WSO2 AM 1.7.0 binary pack from here. With AM 1.7.0 we have done a major re-design the APIPublisher UI,in which allowing users to get the experience of designing APIs addition to implement APIs and manage APIs as previous AM publisher  versions are only focus on implement and manage APIs.
Start AM server,log in to APIPublisher app and create an API with below details;For more information,refer the quick start guide,
In Design API view,enter below parameters.

  • Name -order
  • Context -order
  • Version-v1

  Under Resources section,define following three API resources.

  •   URL-Pattern - submitOrder
              HTTP Verb - POST
             
  •   URL-Pattern -cancelOrder/{id}
              HTTP Verb- GET

  •  URL-Pattern - orderStatus/{id}
             HTTP Verb- GET

  •  URL-Pattern - confirmCancelOrder/*
             HTTP Verb- GET

a.png

   4) Then save the design API view content.Then click on ‘Implement’ button.

   5) Enter  above deployed JAXRS web app url [http://localhost:9765/Order-1.0/] as the production endpoint value with setting endpoint type as ‘HTTP Endpoint’.

 6) Then save the details and next click on ‘manage’ button.
 7) Select ‘Tier Availability’ as ‘Unlimited’


8) Set the Authentication Type for all API resources as ‘Application &amp; Application User’

9) Click ‘save & publish’ option in it.

Once created the above API and publish the API,you'll see a xml configuration named as admin--Order_v1.xml has been created at {AM}/repository/deployment/server/synapse-configs/default/api location.

<?xml version="1.0" encoding="UTF-8"?><api xmlns="http://ws.apache.org/ns/synapse" name="admin--Order" context="/Order" version="1" version-type="url">
    <resource methods="POST" url-mapping="/submitOrder">
        <inSequence>
           <property name="POST_TO_URI" value="true" scope="axis2"/>
            <filter source="$ctx:AM_KEY_TYPE" regex="PRODUCTION">
                <then>
                   <send>
                        <endpoint name="admin--TTT_APIproductionEndpoint_0" >
                            <address uri="https://10.100.1.85:9463/services/orderSvc/" format="soap11">
                                <timeout>
                                    <duration>30000</duration>
                                    <responseAction>fault</responseAction>
                                </timeout>
                                <suspendOnFailure>
                                    <errorCodes>-1</errorCodes>
                                    <initialDuration>0</initialDuration>
                                    <progressionFactor>1.0</progressionFactor>
                                    <maximumDuration>0</maximumDuration>
                                </suspendOnFailure>
                                <markForSuspension>
                                    <errorCodes>-1</errorCodes>
                                </markForSuspension>
                            </address>
                        </endpoint>
                    </send>
                </then>
                <else>
                    <sequence key="_sandbox_key_error_"/>
                </else>
            </filter>
        </inSequence>
        <outSequence>
            <send/>
        </outSequence>
    </resource>
    <resource methods="GET" uri-template="/cancelOrder/{id}">
        <inSequence>
            <property name="POST_TO_URI" value="true" scope="axis2"/>
            <filter source="$ctx:AM_KEY_TYPE" regex="PRODUCTION">
                <then>
                 
                </then>
                <else>
                    <sequence key="_sandbox_key_error_"/>
                </else>
            </filter>
        </inSequence>
        <outSequence>
            <send/>
        </outSequence>
    </resource>
    <resource methods="GET" uri-template="/orderStatus/{id}">
        <inSequence>
            <property name="POST_TO_URI" value="true" scope="axis2"/>
            <filter source="$ctx:AM_KEY_TYPE" regex="PRODUCTION">
                <then>
                   <send>
                        <endpoint name="admin--TTT_APIproductionEndpoint_0" >
                            <address uri="https://10.100.1.85:9463/services/orderSvc/" format="soap11">
                                <timeout>
                                    <duration>30000</duration>
                                    <responseAction>fault</responseAction>
                                </timeout>
                                <suspendOnFailure>
                                    <errorCodes>-1</errorCodes>
                                    <initialDuration>0</initialDuration>
                                    <progressionFactor>1.0</progressionFactor>
                                    <maximumDuration>0</maximumDuration>
                                </suspendOnFailure>
                                <markForSuspension>
                                    <errorCodes>-1</errorCodes>
                                </markForSuspension>
                            </address>
                        </endpoint>
                    </send>
                </then>
                <else>
                    <sequence key="_sandbox_key_error_"/>
                </else>
            </filter>
        </inSequence>
        <outSequence>
            <send/>
        </outSequence>
    </resource>
    <handlers>      
        <handler class="org.wso2.carbon.apimgt.gateway.handlers.security.APIAuthenticationHandler"/>        
        <handler class="org.wso2.carbon.apimgt.gateway.handlers.throttling.APIThrottleHandler">
            <property name="id" value="A"/>
            <property name="policyKey" value="gov:/apimgt/applicationdata/tiers.xml"/>
        </handler>      
        <handler class="org.wso2.carbon.apimgt.usage.publisher.APIMgtGoogleAnalyticsTrackingHandler">
            <property name="configKey" value="gov:/apimgt/statistics/ga-config.xml"/>
        </handler>
        <handler class="org.wso2.carbon.apimgt.gateway.handlers.ext.APIManagerExtensionHandler"/>
    </handlers>
</api>

Once publish the API,browse APIStore and create a subscription for this API and generate a application token from APIstore. 

Now let's try to invoke /submitOrder method of API. 

A sample curl request would be as ;

curl -d @payload.xml -H "Authorization:Bearer xxxxx" -H "Content-Type:text/xml" http://localhost:8280/Order/1/submitOrder 

payload.xml content -

<Order> <customerName>Jack</customerName> <quantity>5</quantity> <creditCardNumber>233</creditCardNumber> <delivered>false</delivered> </Order>

You'll observe a response similar to below.

<Order> <creditCardNumber>233</creditCardNumber> <customerName>Jack</customerName> <date>06/24/2014 08:43:52</date> <delivered>false</delivered> <orderId>a4c1315d-8a07-4e80-85b1-3795ab47db7a</orderId> <quantity>5</quantity> </Order>

New Requirement 1

Now,let's say you want to make the above Order API as a json/REST API.Thus the input and output has to be in json format.For this,you have to change the Order API xml content.Since AM 1.7.0 doesn't provide mediation UI capabilities,you can try directly editing the deployed api xml file located at {AM}/repository/deployment/server/synapse-configs/default/api.

Replace json message formatter and message builder


Replace below message-formatter and builder to axis2.xml of {AM}/repository/conf/axis2/ location and restart AM server.


Message Formatter

<messageFormatter contentType="application/json" class="org.apache.synapse.commons.json.JsonFormatter"/>


Message Builder

<messageBuilder contentType="application/json"
                       class="org.apache.synapse.commons.json.JsonBuilder"/>

To set response to be json format in /submitOrder resource of Order API.
Set messageType and content-type as ‘application/json’ in out-sequence of /submitOrder resource.

<outSequence> <property name="messageType" value="application/json" scope="axis2"/> <property name="ContentType" value="application/json" scope="axis2"/> <send/> </outSequence>

To accept json formatted inputs for /submitOrder API resource
To pass the payload as json from client and then convert that payload from json to xml in APIManager side,we have added below payload factory inside ‘/submitOrder’ API resource.

<payloadFactory media-type="xml"> <format> <Order> <customerName>$1</customerName> <quantity>$2</quantity> <creditCardNumber>$3</creditCardNumber> <delivered>$4</delivered> </Order> </format> <args> <arg expression="$.Order.customerName" evaluator="json"></arg> <arg expression="$.Order.quantity" evaluator="json"></arg> <arg expression="$.Order.creditCardNumber" evaluator="json"></arg> <arg expression="$.Order.delivered" evaluator="json"></arg> </args> </payloadFactory>

The changed order API is as below.
<?xml version="1.0" encoding="UTF-8"?><api xmlns="http://ws.apache.org/ns/synapse" name="admin--Order" context="/Order" version="1" version-type="url">
    <resource methods="POST" url-mapping="/submitOrder">
        <inSequence> 
<payloadFactory media-type="xml">
           <format>
           <Order>
  <customerName>$1</customerName>
  <quantity>$2</quantity>
  <creditCardNumber>$3</creditCardNumber>
  <delivered>$4</delivered>
</Order>

           </format>
           <args>           
            <arg expression="$.Order.customerName" evaluator="json"></arg>
            <arg expression="$.Order.quantity" evaluator="json"></arg>
            <arg expression="$.Order.creditCardNumber" evaluator="json"></arg>
<arg expression="$.Order.delivered" evaluator="json"></arg>
           </args>
           </payloadFactory>
 
            <property name="POST_TO_URI" value="true" scope="axis2"/>
            <filter source="$ctx:AM_KEY_TYPE" regex="PRODUCTION">
                <then>
                   <send>
                        <endpoint name="admin--TTT_APIproductionEndpoint_0" >
                            <address uri="https://localhost:9463/services/orderSvc/" format="soap11">
                                <timeout>
                                    <duration>30000</duration>
                                    <responseAction>fault</responseAction>
                                </timeout>
                                <suspendOnFailure>
                                    <errorCodes>-1</errorCodes>
                                    <initialDuration>0</initialDuration>
                                    <progressionFactor>1.0</progressionFactor>
                                    <maximumDuration>0</maximumDuration>
                                </suspendOnFailure>
                                <markForSuspension>
                                    <errorCodes>-1</errorCodes>
                                </markForSuspension>
                            </address>
                        </endpoint>
                    </send>
                </then>
                <else>
                    <sequence key="_sandbox_key_error_"/>
                </else>
            </filter>
        </inSequence>
        <outSequence>
 <property name="messageType" value="application/json" scope="axis2"/>
 <property name="ContentType" value="application/json" scope="axis2"/>
            <send/>
        </outSequence>
    </resource>
    <resource methods="GET" uri-template="/cancelOrder/{id}">
        <inSequence>
            <property name="POST_TO_URI" value="true" scope="axis2"/>
            <filter source="$ctx:AM_KEY_TYPE" regex="PRODUCTION">
                <then>
                 <send>
                        <endpoint name="admin--TTT_APIproductionEndpoint_0" >
                            <address uri="https://localhost:9463/services/orderSvc/" format="soap11">
                                <timeout>
                                    <duration>30000</duration>
                                    <responseAction>fault</responseAction>
                                </timeout>
                                <suspendOnFailure>
                                    <errorCodes>-1</errorCodes>
                                    <initialDuration>0</initialDuration>
                                    <progressionFactor>1.0</progressionFactor>
                                    <maximumDuration>0</maximumDuration>
                                </suspendOnFailure>
                                <markForSuspension>
                                    <errorCodes>-1</errorCodes>
                                </markForSuspension>
                            </address>
                        </endpoint>
                    </send>  
                <else>
                    <sequence key="_sandbox_key_error_"/>
                </else>
            </filter>
        </inSequence>
        <outSequence> 
            <send/>
        </outSequence>
    </resource>
    <resource methods="GET" uri-template="/orderStatus/{id}">
        <inSequence>
            <property name="POST_TO_URI" value="true" scope="axis2"/>
            <filter source="$ctx:AM_KEY_TYPE" regex="PRODUCTION">
                <then>
                   <send>
                        <endpoint name="admin--TTT_APIproductionEndpoint_0" >
                            <address uri="https://localhost:9463/services/orderSvc/" format="soap11">
                                <timeout>
                                    <duration>30000</duration>
                                    <responseAction>fault</responseAction>
                                </timeout>
                                <suspendOnFailure>
                                    <errorCodes>-1</errorCodes>
                                    <initialDuration>0</initialDuration>
                                    <progressionFactor>1.0</progressionFactor>
                                    <maximumDuration>0</maximumDuration>
                                </suspendOnFailure>
                                <markForSuspension>
                                    <errorCodes>-1</errorCodes>
                                </markForSuspension>
                            </address>
                        </endpoint>
                    </send>
                </then>
                <else>
                    <sequence key="_sandbox_key_error_"/>
                </else>
            </filter>
        </inSequence>
        <outSequence> 
            <send/>
        </outSequence>
    </resource>
    <handlers>        
        <handler class="org.wso2.carbon.apimgt.gateway.handlers.security.APIAuthenticationHandler"/>      
        <handler class="org.wso2.carbon.apimgt.gateway.handlers.throttling.APIThrottleHandler">
            <property name="id" value="A"/>
            <property name="policyKey" value="gov:/apimgt/applicationdata/tiers.xml"/>
        </handler>        
        <handler class="org.wso2.carbon.apimgt.usage.publisher.APIMgtGoogleAnalyticsTrackingHandler">
            <property name="configKey" value="gov:/apimgt/statistics/ga-config.xml"/>
        </handler>
        <handler class="org.wso2.carbon.apimgt.gateway.handlers.ext.APIManagerExtensionHandler"/>
    </handlers>
</api>

Now let's try to invoke /submitOrder method of API. A sample curl request would be as ; 
curl -d @payload.json "Authorization:Bearer xxxxx" -H "Content-Type:application/json"  http://localhost:8280/Order/1/submitOrder

payload.json content -
{"Order":{"customerName:"PP_88","quantity":"8" ,".creditCardNumber":"1234","delivered":"true"}}
Response would be as below.
{"Order":{"orderId:"a4c1315d-8a07-4e80-85b1-3795ab47db7a","date:"06/24/2014 08:43:52","customerName:"PP_88","quantity":"8" ,".creditCardNumber":"1234","delivered":"true"}}