Sunday, October 26, 2014

Exposing a SOAP service as a REST API using WSO2 API Manager

This post explains how we can publish an existing SOAP service as a  REST API using WSO2 API Manager.

We will be using a sample data-service called "OrderSvc"as the SOAP service which can be deployed as a SOAP service in WSO2 Data Services Server. But this could be any of SOAP service.

1. Service Description of ‘OrderSvc’ SOAP Backend Service

This “orderSvc” service provides WSDL with 3 operations (“submitOrder”, “cancelOrder”, “getOrderStatus”). 


submitOrder operation takes ProductCode and Quantity as parameters.
Sample request :
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:dat="http://ws.wso2.org/dataservice">
  <soapenv:Header/>
  <soapenv:Body>
     <dat:submitOrder>
        <dat:ProductCode>AA_1</dat:ProductCode>
        <dat:Quantity>4</dat:Quantity>
     </dat:submitOrder>
  </soapenv:Body>

</soapenv:Envelope>

cancelOrder operation takes OrderId as parameter and does an immediate cancellation and returns a confirmation code.
Sample request:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:dat="http://ws.wso2.org/dataservice">
  <soapenv:Header/>
  <soapenv:Body>
     <dat:cancelOrder>
        <dat:OrderId>16</dat:OrderId>
     </dat:cancelOrder>
  </soapenv:Body>
</soapenv:Envelope>

orderStatus operaioin takes the orderId as parameter and return the order status as response.
Sample request :
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:dat="http://ws.wso2.org/dataservice">
  <soapenv:Header/>
  <soapenv:Body>
     <dat:orderStatus>
        <dat:OrderId>16</dat:OrderId>
     </dat:orderStatus>
  </soapenv:Body>
</soapenv:Envelope>


We need to expose this "OrderSvc" SOAP service as a REST API using API Manager. And once we exposed this as a REST API, “submitOrder”, “cancelOrder”, “getOrderStatus” operations should map to REST resources as bellow which takes the user parameters as query parameters.

“/submitOrder” (POST) => request does not contain order id or date; response is the full order payload.

“/cancelOrder/{id}” (GET) => does an immediate cancellation and returns a confirmation code.

“/orderStatus/{id}” (GET) => response is the order header (i.e., payload excluding line items).


Deploying the Data-Service :

1. Login to the MySQL and create a database called “demo_service_db” . (This database name can be anything , we need to update the data-service (.dbs file) accordingly).

mysql> create database demo_service_db;
mysql> demo_service_db;

2. Execute the dbscript given here on the above created database. This will create two tables ‘CustomerOrder’, ‘OrderStatus’ and one stored procedure ‘submitOrder’. Also it will insert some sample data into two tables.

3. Include mysql jdbc driver into DSS_HOME/repository/components/lib directory.


4. Download the data-service file given here. Before deploy this .dbs file, we need to modify the data source section defined in it. i.e in the downloaded orderSvc.dbs file, change the following properties by providing correct jdbcUrl ( need to point to the database that you created in step 1)  and change the userName/ Pwd of mysql connection, if those are different than the one defined here.


<config id="ds1">
     <property name="driverClassName">com.mysql.jdbc.Driver</property>
     <property name="url">jdbc:mysql://localhost:3306/demo_service_db</property>
     <property name="username">root</property>
     <property name="password">root</property>
  </config>


5. Deploy the orderSvc.dbs file in Data services server by copying this file into “wso2dss-3.2.1/repository/deployment/server/dataservices” directory. Start the server.


6. Before expose through API Manager, check whether all three operations works as expected using try-it tool or SOAP-UI.


submitOrder
Sample request :
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:dat="http://ws.wso2.org/dataservice">
  <soapenv:Header/>
  <soapenv:Body>
     <dat:submitOrder>
        <dat:ProductCode>AA_1</dat:ProductCode>
        <dat:Quantity>4</dat:Quantity>
     </dat:submitOrder>
  </soapenv:Body>
</soapenv:Envelope>


Response :
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Body>
     <submitOrderResponse xmlns="http://ws.wso2.org/dataservice">
        <OrderId>16</OrderId>
        <ProductCode>AA_1</ProductCode>
        <Quantity>4</Quantity>
     </submitOrderResponse>
  </soapenv:Body>
</soapenv:Envelope>


cancelOrder
Sample request:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:dat="http://ws.wso2.org/dataservice">
  <soapenv:Header/>
  <soapenv:Body>
     <dat:cancelOrder>
        <dat:OrderId>16</dat:OrderId>
     </dat:cancelOrder>
  </soapenv:Body>
</soapenv:Envelope>


Response:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Body>
     <axis2ns1:REQUEST_STATUS xmlns:axis2ns1="http://ws.wso2.org/dataservice">SUCCESSFUL</axis2ns1:REQUEST_STATUS>
  </soapenv:Body>
</soapenv:Envelope>


orderStatus
Sample request :
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:dat="http://ws.wso2.org/dataservice">
  <soapenv:Header/>
  <soapenv:Body>
     <dat:orderStatus>
        <dat:OrderId>16</dat:OrderId>
     </dat:orderStatus>
  </soapenv:Body>
</soapenv:Envelope>


Response:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Body>
     <OrderStatus xmlns="http://ws.wso2.org/dataservice">
        <OrderStatus>CANCELED</OrderStatus>
     </OrderStatus>
  </soapenv:Body>

</soapenv:Envelope>



2. Configuring API Manager

1. Download the custom sequence given here and save it to the APIM registry location “/_system/governance/apimgt/customsequences/in/”.  This can be done by login to the API Manager carbon management console. 

In the left menu section, expand the Resources -> Browse -> Go to "/_system/governance/apimgt/customsequences/in" -> Click in "Add Resource" -> Browse the file system and upload the "orderSvc_supporting_sequence.xml" sequence that downloaded above. Then click "Add". This step will save the downloaded sequence into registry.


4. Create orderSvc API by wrapping orderSvc SOAP service.

Login to the API Publisher and create a API with following info.

Name: orderSvc
Context: ordersvc
Version: v1

Resource definition1
URL Pattern: submitOrder
Method: POST

Resource definition2
URL Pattern: cancelOrder/{id}
Method: GET

Resource definition3
URL Pattern: orderStatus/{id}
Method: GET


Endpoint Type:* : Select the endpoint type as Address endpoint. And go to the “Advanced Options” and select the message format as “SOAP 1.1”.
Production Endpoint:https://localhost:9446/services/orderSvc/ (Give the OrderSvc service endpoint)

Tier Availability : Unlimited

Sequences : Click on the Sequences checkbox and selected the previously saved custom sequence under “In Flow”.


Publish the API into gateway.

We are done with the API creation.

Functionality of the custom sequence "orderSvc_supporting_sequence.xml"

OrderSvc backend service expecting a SOAP request while user invoking API by sending parameters as query parameters (i.e cancelOrder/{id}, orderStatus/{id}).

This custom sequence will take care of building SOAP payload required for cancelOrder, orderStatus operations by looking at the incoming request URI and the query parameters.

Using a switch mediator, it read the request path . i.e 

<switch xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope" xmlns:ns3="http://org.apache.synapse/xsd" source="get-property('REST_SUB_REQUEST_PATH')">

Then check the value of request path using a regular expression and construct the payload either for cancelOrder or orderStatus according to the matched resource. i.e

<case regex="/cancelOrder.*">
    <payloadFactory media-type="xml">
        <format>
            <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
                <soapenv:Header/>
                <soapenv:Body xmlns:dat="http://ws.wso2.org/dataservice">
                    <dat:cancelOrder>
                        <dat:OrderId>$1</dat:OrderId>
                    </dat:cancelOrder>
                </soapenv:Body>
            </soapenv:Envelope>
        </format>
        <args>
            <arg evaluator="xml" expression="get-property('uri.var.id')"/>
        </args>
    </payloadFactory>
    <header name="Action" scope="default" value="urn:cancelOrder"/>
</case>

<case regex="/orderStatus.*">
    <payloadFactory media-type="xml">
        <format>
            <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
                <soapenv:Header/>
                <soapenv:Body xmlns:dat="http://ws.wso2.org/dataservice">
                    <dat:orderStatus>
                        <dat:OrderId>$1</dat:OrderId>
                    </dat:orderStatus>
                </soapenv:Body>
            </soapenv:Envelope>
        </format>
        <args>
            <arg evaluator="xml" expression="get-property('uri.var.id')"/>
        </args>
    </payloadFactory>
    <header name="Action" scope="default" value="urn:orderStatus"/>
</case>


Test OrderSvc API published in API Manager

Login to the API Store and subscribe to the OrderSvc API and generate a access token. Invoke the orderStatus resource as given bellow. This will call to OrderSvc SOAP service and give you the response.

curl -v -H "Authorization: Bearer  _smfAGO3U6mhzFLro4bXVEl71Gga" http://localhost:8280/order/v1/orderStatus/3