WSO2 EI Dataservice as Rest API with sequence calls - rest

I'm new to wso2 and currently I'm trying to expose a mongodb data service as REST API. I followed this tutorial and exposed my data service as Rest resource like this:
<data>
<config>...</config>
<query id="count" useConfig="MongoDB">
<expression>myCollection.count()</expression>
<result outputType="json">{
"Documents": {
"Document": [
{
"Data": "$document"
}
]
}
}</result>
</query>
<operation name="count_op">
<call-query href="count"/>
</operation>
<resource method="GET" path="users/count">
<call-query href="count"/>
</resource>
</data>
Works fine, however when I tried to access the resource from my angular2 project, I stumbled upon the CORS problem. I read up some posts about that and figured I need to modify my Rest API with something like this:
<resource methods="OPTIONS" url-mapping="/*">
<inSequence>
<property action="set" name="HTTP_SC" scope="axis2"
type="STRING" value="200"/>
<property action="set" name="messageType" scope="axis2"
type="STRING" value="application/json"/>
<sequence key="rest_add_access_control_headers"/>
<respond/>
</inSequence>
<outSequence/>
<faultSequence>
<sequence key="rest_add_access_control_headers"/>
<respond/>
</faultSequence>
</resource>
I tried to integrate this code with my data service definition (the one above) but I can't get it working and it makes me wonder if data service exposed as REST resource are the same thing as Rest APIs? It seems I cannot invoke any elements or other mediators in the data service definition. Do I need to create an actual Rest API for this? If so, how can I use my mongo dataservice as a resource for my Rest API?
EDIT: The CORS error I'm getting from my angular2 project when trying to access the service is "No 'Access-Control-Allow-Origin' header is present on the requested resource." which is another topic. But it led me to edit my data service so I edited my data service resource element and added a new one:
<resource method="GET" path="users/count">
<inSequence>
<property action="set" name="HTTP_SC" scope="axis2" type="STRING" value="200"/>
<property action="set" name="messageType" scope="axis2" type="STRING" value="application/json"/>
<sequence key="rest_add_access_control_headers"/>
<respond/>
</inSequence>
<outSequence/>
<faultSequence>
<sequence key="rest_add_access_control_headers"/>
<respond/>
</faultSequence>
<call-query href="count"/>
</resource>
<resource method="OPTIONS" path="users/count">
<inSequence>
<property action="set" name="HTTP_SC" scope="axis2"
type="STRING" value="200"/>
<property action="set" name="messageType" scope="axis2"
type="STRING" value="application/json"/>
<sequence key="rest_add_access_control_headers"/>
<respond/>
</inSequence>
<outSequence/>
<faultSequence>
<sequence key="rest_add_access_control_headers"/>
<respond/>
</faultSequence>
</resource>
rest_add_access_control_headers sequence looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<sequence name="rest_add_access_control_headers" xmlns="http://ws.apache.org/ns/synapse">
<property name="Access-Control-Allow-Origin" scope="transport"
type="STRING" value="*"/>
<property name="Access-Control-Allow-Methods" scope="transport"
type="STRING" value="GET,POST,PUT,DELETE,OPTIONS"/>
<property name="Access-Control-Allow-Headers" scope="transport"
type="STRING" value="origin, content-type, accept, Authorization"/>
</sequence>
The first seems to be fine but the second one makes my dataservice faulty, giving this error in managemenet console:
DS Code: UNKNOWN_ERROR Source Data Service:- Name: mongodb_dataservice Location: \MongoDB.dbs Description: N/A Default Namespace: http://ws.wso2.org/dataservice Nested Exception:- java.lang.NullPointerException at org.wso2.carbon.dataservices.core.DataServiceFactory.createDataService(DataServiceFactory.java:207) at org.wso2.carbon.dataservices.core.DBDeployer.createDBService(DBDeployer.java:797) at org.wso2.carbon.dataservices.core.DBDeployer.processService(DBDeployer.java:1152) at org.wso2.carbon.dataservices.core.DBDeployer.deploy(DBDeployer.java:201) at org.apache.axis2.deployment.repository.util.DeploymentFileData.deploy(DeploymentFileData.java:136) at org.apache.axis2.deployment.DeploymentEngine.doDeploy(DeploymentEngine.java:807) at org.apache.axis2.deployment.repository.util.WSInfoList.update(WSInfoList.java:144) at org.apache.axis2.deployment.RepositoryListener.update(RepositoryListener.java:377) at org.apache.axis2.deployment.RepositoryListener.checkServices(RepositoryListener.java:254) at org.apache.axis2.deployment.RepositoryListener.startListener(RepositoryListener.java:371) at org.apache.axis2.deployment.scheduler.SchedulerTask.checkRepository(SchedulerTask.java:59) at org.apache.axis2.deployment.scheduler.SchedulerTask.run(SchedulerTask.java:67) at org.wso2.carbon.core.deployment.CarbonDeploymentSchedulerTask.runAxisDeployment(CarbonDeploymentSchedulerTask.java:93) at org.wso2.carbon.core.deployment.CarbonDeploymentSchedulerTask.run(CarbonDeploymentSchedulerTask.java:138) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) Caused by: java.lang.NullPointerException at org.wso2.carbon.dataservices.core.engine.DataService.init(DataService.java:352) at org.wso2.carbon.dataservices.core.DataServiceFactory.createDataService(DataServiceFactory.java:190) ... 20 more
Again, I'm not sure if I'm even supposed to put such code in data service definition since the code was meant to be for Rest API definition, the one where you use sequences, mediators etc...
How can I consume my dataservice with an ESB API? Would you please provide an example on how to do that ? I did some research but couldn't find anything.

May I know why you are unable to invoke the data service? Are you getting any error while invocation? if you could provide the logs it will be helpful.
Moreover DSS is used to fetch data from DB ,The best practice is to provide an ESB API which will consume this service rather than exposing DSS service to outside world directly

Related

How to convert text/xml response into application/json wso2 EI

I have a soap service and I need to expose this service as a rest api in Wso2 EI, content type is text/xml, I tried with
<property name="messageType" value="application/json"
scope="axis2"/>
in the outsequence, but it doesnt convert my response into a json. Can you please assist me on how to do it.
I tried this,
<resource methods="POST">
<inSequence>
<send>
<endpoint>
<address uri="http://wsf.cdyne.com/WeatherWS/Weather.asmx?WSDL"/>
</endpoint>
</send>
</inSequence>
<outSequence>
<property name="messageType" value="application/json" scope="axis2"/>
<send/>
</outSequence>
</resource>
Reason for this is that WSO2 EI or ESB is developed in a manner where it responds to a SOAP request with a SOAP response by default. When you invoke the PROXY or API with SOAPAction and Content-Type: text/xml, EI understands this as a SOAP request and it'll respond with a SOAP response.
So if the client request is in SOAP-1.1 EI responds with a SOAP-1.1 response or if the client request is in SOAP-1.2 EI responds with a SOAP-1.2.
To bypass this behaviour they have provided an additional property as below.
<property name="IsClientDoingREST" scope="default" type="BOOLEAN" value="true"/>
So, before responding the client, properties should be set as below to get the expected behaviour of responding with a JSON.
<property name="IsClientDoingREST" scope="default" type="BOOLEAN" value="true"/>
<property name="messageType" scope="axis2" value="application/json"/>
This approach will help you to get the JSON response out of a SOAP request.
It should work perfectly. A sample REST API configuration given below.
<api xmlns="http://ws.apache.org/ns/synapse" name="CheckREST" context="/samplerest">
<resource methods="GET">
<inSequence>
<send>
<endpoint>
<http uri-template="http://localhost:8280/services/sampleSOAPproxy"/>
</endpoint>
</send>
</inSequence>
<outSequence>
<property name="messageType" value="application/json" scope="axis2" type="STRING"/>
<send/>
</outSequence>
</resource>
</api>
If not working, please mention the EI version you are using.

WSO2 REST to SOAP passing operation parameters

Using WSO2 ESB 4.8.1, I have configured a WSDL proxy that I want to access over REST. the proxy points to the SOAP WSDL URI and has publish WSDL turned on. This seem to work fine and I can see the service and its various operations in the WSO2 admin UI. Likewise if I go to localhost:8280/services/
The questions is how do I pass operation specific parameters when accessing over HTTP REST?
Let's say my FooService OperationX expects a "p1" parameter, can I pass this directly when accessing localhost:8280/services/FooService/OperationX in a browser?
I tried for example localhost:8280/services/FooService/SomeOperation?p1=somevalue, but always get a validation error that the required parameter is missing:
cvc-complex-type.2.4.b: The content of element 'axis2ns15:OperationXRequest' is not complete. One of '{"somenamespace":p1}' is expected.
Can this be supported by a basic WSDL proxy? Or do I need to use the API?
I think the better option for your scenario is to use api to access over REST. Here I have created an api (I used http://jsonplaceholder.typicode.com/comments as my REST back end) which gets the query parameter(postId) which was sent in REST request (http://172.22.99.96:8290/test/comments?postId=1) and assign that value to a property called mypostId inside the api.
Then I am modifying the payload by adding the mypostId property using payload factory mediator which will match to the echo service request(I have used echo service as the SOAP backend).
Then I use enrich mediator to change my soap envelope to match the echo service request soap envelope by adding "xmlns:echo="http://echo.services.core.carbon.wso2.org"" name space. Finally I am sending my created request to echo service proxy.
<api xmlns="http://ws.apache.org/ns/synapse" name="test" context="/test">
<resource methods="GET" uri-template="/comments?postId={postId}">
<inSequence>
<log level="custom">
<property name="Message Flow" value="--- Order GET ---"></property>
</log>
<log level="custom">
<property name="postId" expression="$url:postId"></property>
</log>
<property name="mypostId" expression="$url:postId"></property>
<call>
<endpoint>
<http method="GET" uri-template="http://jsonplaceholder.typicode.com/comments?postId={uri.var.postId}"></http>
</endpoint>
</call>
<payloadFactory media-type="xml">
<format>
<echo:echoInt xmlns:echo="http://echo.services.core.carbon.wso2.org">
<in>$1</in>
</echo:echoInt>
</format>
<args>
<arg evaluator="xml" expression="get-property('mypostId')"></arg>
</args>
</payloadFactory>
<log level="full"></log>
<log level="custom">
<property name="Message Flow" value="--- After Palyload factory---"></property>
</log>
<property name="extarctedBody" expression="$body"></property>
<log level="custom">
<property name="MyextarctedBody" expression="get-property('extarctedBody')"></property>
</log>
<log level="full"></log>
<enrich>
<source type="inline" clone="true">
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:echo="http://echo.services.core.carbon.wso2.org"></soapenv:Envelope>
</source>
<target type="envelope"></target>
</enrich>
<log level="custom">
<property name="Message Flow" value="--- Order GET2 ---"></property>
</log>
<log level="full"></log>
<enrich>
<source type="property" clone="true" property="extarctedBody"></source>
<target xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:echo="http://echo.services.core.carbon.wso2.org" action="child" xpath="//soapenv:Envelope"></target>
</enrich>
<log level="full"></log>
<send>
<endpoint>
<address uri="http://localhost:8290/services/echo"></address>
</endpoint>
</send>
</inSequence>
<outSequence>
<send></send>
</outSequence>
</resource>
</api>
Hope this may help you .

Restore task class in WSO2 ESB

When i use a task in WSO2 ESB it always returns the same error with every web service:
"Unable to handle request. The action '(mySoapAction)' was not recognized"
where (mySoapAction) is every SOAP action used, for EVERY action, for EVERY proxy service i use for task implementation.
What could i do in order to fix this error? I thought a task class error in org.apache.synapse.startup.tasks.MessageInjector.
Obvoiusly the task implementation is correct, because the same tasks some day ago were perfectly working. Suggestions?
Here come a sample with the weather webservice (http://wsf.cdyne.com/WeatherWS/Weather.asmx?WSDL)
Proxy service :
<?xml version="1.0" encoding="UTF-8"?>
<proxy xmlns="http://ws.apache.org/ns/synapse"
name="TestSOF"
transports="https http"
startOnLoad="true"
trace="disable">
<target>
<endpoint>
<wsdl service="Weather"
port="WeatherSoap12"
uri="http://wsf.cdyne.com/WeatherWS/Weather.asmx?WSDL"/>
</endpoint>
<outSequence>
<log level="full"/>
<property name="OUT_ONLY" value="true"/>
<property name="transport.vfs.ReplyFileName" value="weather.xml" scope="transport"/>
<send>
<endpoint>
<address uri="vfs:file:///E:/temp"/>
</endpoint>
</send>
</outSequence>
</target>
<publishWSDL uri="http://wsf.cdyne.com/WeatherWS/Weather.asmx?WSDL"/>
</proxy>
Task :
<?xml version="1.0" encoding="UTF-8"?>
<task xmlns="http://ws.apache.org/ns/synapse"
name="TestSOFTask"
class="org.apache.synapse.startup.tasks.MessageInjector"
group="synapse.simple.quartz">
<trigger count="1" interval="1"/>
<property xmlns:task="http://www.wso2.org/products/wso2commons/tasks"
name="proxyName"
value="TestSOF"/>
<property xmlns:task="http://www.wso2.org/products/wso2commons/tasks"
name="soapAction"
value="http://ws.cdyne.com/WeatherWS/GetCityWeatherByZIP"/>
<property xmlns:task="http://www.wso2.org/products/wso2commons/tasks" name="message">
<weat:GetCityWeatherByZIP xmlns:weat="http://ws.cdyne.com/WeatherWS/">
<weat:ZIP>11010</weat:ZIP>
</weat:GetCityWeatherByZIP>
</property>
<property xmlns:task="http://www.wso2.org/products/wso2commons/tasks"
name="format"
value="soap12"/>
<property xmlns:task="http://www.wso2.org/products/wso2commons/tasks"
name="injectTo"
value="proxy"/>
</task>
Schedule the task and you will find the service response in a file name weather.xml
If you want to change from soap12 to soap11 :
Change the value of property "format" in the task def to : soap11
Change the endpoint def in the proxy service using port "WeatherSoap" rather than "WeatherSoap12"
Hope it will help you to find what is going wrong with your conf...

WSO2 ESB How to send an email from a JMS message to a custom email address?

I'm wondering if someone can help me with the following setup.
I want to send a message from my application via JMS to WSO2 ESB so the ESB can send it as en email. I'm using ActiveMQ as queue. Until now, when I send a message via the ActiveMQ interface to the queue, wso2 esb gets it. Then, wso2 esb send the message as email to a specific email address.
So I could configure ActiveMQ and WSO2 esb to send the JMS message to a specific email address (eg. specificaddress#test.com).
And here is my question. How can I modify the receiver address for the email? In the ESB sequence configuration, I currently use a specific address. But the address is dependant on the user that uses my application. So I have to change the "To" property, dependant on the user that has to receive the email.
So how can I pass the values for the properties "To", but also for "Subject", through a JMS message to WSO2 esb sequence?
That's the configuration of the sequence I have:
<sequence xmlns="http://ws.apache.org/ns/synapse" name="sendMail">
<property name="messageType" value="text/html" scope="axis2" type="STRING"></property>
<property name="ContentType" value="text/html" scope="axis2"></property>
<property name="Subject" value="This is the subject." scope="transport"></property>
<property name="To" value="specificaddress#test.com" scope="transport"></property>
<property name="OUT_ONLY" value="true" scope="default" type="STRING"></property>
<log level="full"></log>
<send>
<endpoint>
<address uri="mailto:"></address>
</endpoint>
</send>
</sequence>
And this is my proxy:
<?xml version="1.0" encoding="UTF-8"?>
<proxy xmlns="http://ws.apache.org/ns/synapse"
name="sendToMailIn"
transports="jms"
statistics="disable"
trace="disable"
startOnLoad="true">
<target inSequence="sendMail"/>
<description/>
</proxy>
I hope someone has a clue.
UPDATE
I think I have the solution!!! Wow :-) Maybe, at first, I was stupid, but here it is ...
What you can do is sending a SOAP envelop through a JMS message to WSO2 ESB. And then, with an XPath expression, you can get the passed values. A little bit has to changed at the proxy and the sequence.
This is the new sequence:
<sequence xmlns="http://ws.apache.org/ns/synapse" name="sendMail">
<property name="messageType" value="text/html" scope="axis2" type="STRING"></property>
<property name="ContentType" value="text/html" scope="axis2"></property>
<property xmlns:ns="http://org.apache.synapse/xsd" name="Subject" expression="$body/subject" scope="transport"></property>
<property xmlns:ns="http://org.apache.synapse/xsd" name="To" expression="$body/to" scope="transport"></property>
<property name="OUT_ONLY" value="true" scope="default" type="STRING"></property>
<log level="full"></log>
<send>
<endpoint>
<address uri="mailto:"></address>
</endpoint>
</send>
</sequence>
And this is the new proxy:
<?xml version="1.0" encoding="UTF-8"?>
<proxy xmlns="http://ws.apache.org/ns/synapse"
name="sendToMailIn"
transports="jms"
statistics="disable"
trace="disable"
startOnLoad="true">
<target inSequence="sendMail"/>
<parameter name="transport.jms.ContentType">
<rules>
<jmsProperty>contentType</jmsProperty>
<default>text/xml</default>
</rules>
</parameter>
<description/>
</proxy>
And this was my SOAP Envelop that WSO2 ESB receives from my ActiMQ queue as JMS message:
<?xml version="1.0" encoding="utf-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org /soap/envelope/">
<soapenv:Body>
<subject>Email subject comes here.</subject>
<to>address#test.com</to>
</soapenv:Body>
</soapenv:Envelope>
you have couple of options here.
you can use http headers and send the "to", "subject" values to ESB.
Send it as a payload value and extract using XPath expression
I solved it with using JSON in a JMS message. Here is my setup that works for me.
This is my JSON message:
{"to":"mail#test.com","subject":"TestSubject","mailbody":"Some body text ..."}
This is my proxy:
<?xml version="1.0" encoding="UTF-8"?>
<proxy xmlns="http://ws.apache.org/ns/synapse"
name="sendToMailIn"
transports="jms"
statistics="disable"
trace="disable"
startOnLoad="true">
<target inSequence="sendMail"/>
<parameter name="transport.jms.ContentType">
<rules>
<jmsProperty>contentType</jmsProperty>
<default>application/json</default>
</rules>
</parameter>
<parameter name="transport.mail.ContentType">application/xml</parameter>
<description/>
</proxy>
And this is my sequence:
<sequence xmlns="http://ws.apache.org/ns/synapse" name="sendMail">
<property name="messageType" value="text/plain" scope="axis2" type="STRING"></property>
<property name="ContentType" value="text/plain" scope="axis2"></property>
<property xmlns:ns="http://org.apache.synapse/xsd" name="Subject" expression="json-eval($.subject)" scope="transport"></property>
<property xmlns:ns="http://org.apache.synapse/xsd" name="To" expression="json-eval($.to)" scope="transport"></property>
<property name="OUT_ONLY" value="true" scope="default" type="STRING"></property>
<script language="js">
<![CDATA[var mailbody = mc.getPayloadJSON().mailbody.toString(); mc.setPayloadXML(
<ns:text xmlns:ns="http://ws.apache.org/commons/ns/payload">{mailbody}</ns:text>);]]>
</script>
<log level="full"></log>
<send>
<endpoint>
<address uri="mailto:"></address>
</endpoint>
</send>
</sequence>

Malformed Response - Invoking one ESB REST service from another

I am relatively new to WSO2 ESB,
Trying to invoke a REST Web service with GET parameter which is hosted on WSO2 ESB through another REST API.
This is a simple Web service(SampleREST) which replies with welcome messsage in XML format,
When i am invoking this service directly; i can see the correct response on browser, shown below
<Message xmlns="http://ws.apache.org/ns/synapse">WelcomeRanjan</Message>
Now i created another REST web service(InvokeSampleRest) which in turn invokes SampleREST web service with the parametrized GET,
the Invoke WebService Client returns me the XML response wrapped inside the mediator xml tag and other malformed tags, shown below
<mediate><<Message xmlns>"http://ws.apache.org/ns/synapse">WelcomeRanjan</Message></<Message xmlns></mediate>
Here is my Code for the SampleREST API
<?xml version="1.0" encoding="UTF-8"?>
<api xmlns="http://ws.apache.org/ns/synapse" name="SampleREST" context="/SampleRest" hostname="10.203.245.47">
<resource methods="GET" uri-template="/{str1}">
<inSequence>
<header name="To" action="remove"/>
<property name="RESPONSE" value="true" scope="default" type="STRING"/>
<property name="NO_ENTITY_BODY" scope="axis2" action="remove"/>
<payloadFactory>
<format>
<Message>$1</Message>
</format>
<args>
<arg expression="get-property('uri.var.str1')"/>
</args>
</payloadFactory>
<log level="full" separator=",">
<property name="sequence" value="*** Got Request ***"/>
</log>
<send/>
</inSequence>
<outSequence/>
<faultSequence/>
</resource>
</api>
The code for the InvokeSampleREST code is as shown below( i am calling SampleREST service from here )
<?xml version="1.0" encoding="UTF-8"?>
<api xmlns="http://ws.apache.org/ns/synapse" name="InvokeSampleREST" context="/InvokeSampleREST" hostname="10.203.245.47">
<resource methods="GET" uri-template="/{str1}">
<inSequence>
<log level="full" separator=","/>
<property name="REST_URL_POSTFIX" expression="fn:concat('/Welcome',get-property('uri.var.str1'))" scope="axis2" type="STRING"/>
<log level="full">
<property name="sequence" value="****Message Sent *** "/>
</log>
<send>
<endpoint>
<address uri="http://10.203.245.47:8280/SampleRest/"/>
</endpoint>
</send>
</inSequence>
<outSequence>
<log level="full" separator=",">
<property name="out" value="** Ouput of Rest call ***"/>
</log>
<property name="ContentType" value="application/xml" scope="axis2" type="STRING"/>
<send/>
</outSequence>
<faultSequence/>
</resource>
</api>
Appreciate any help.
Thanks,
Ranjan
Why are you using two APIs? You can design your flow, in a single API. I mean you can merge them together.
BTW, you need to set the contentType property in your backend API(ie:At SampleREST API)
Because, when you send back the response to "InvokeSampleREST" API, System doesnt know the content-type of the incoming response and try to handle it as text message.
Eg:
<api name="SampleREST" context="/SampleRest" hostname="localhost">
<resource methods="GET" uri-template="/{str1}">
<inSequence>
<header name="To" action="remove"/>
<property name="NO_ENTITY_BODY" scope="axis2" action="remove"/>
<property name="RESPONSE" value="true" scope="default" type="STRING"/>
<payloadFactory>
<format>
<Message>$1</Message>
</format>
<args>
<arg expression="get-property('uri.var.str1')"/>
</args>
</payloadFactory>
<log level="full" separator=",">
<property name="sequence" value="*** Got Request ***"/>
</log>
<property name="Content-Type"
value="application/xml"
scope="transport"
type="STRING"/>
<send/>
</inSequence>
<faultSequence/>
</resource>
Try doing the following change at the InvokeSampleREST API.
At the outSequence, set the messageType as follows before the <send> mediator.
<property name="messageType" value="application/xml" scope="axis2"/>