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

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"/>

Related

How do I call my data service from my REST API and redirect to a URL on Enterprise Integrator?

I have created a data service on Enterprise Integrator that searches a microsoft sql server database for a usercode, if the usercode I am searching for exists in the db the response is the users first name and last name. Is it possible for the user to get redirected to a c# webpage instead of their first name and last name being returned?
I am then calling my data service with my rest api, my intention is to search an microsft sql db and if the data is in the db I should be redirected to a c# webpage. However when I try to test my API I am getting back my json from the Result (Output Mapping) in my query from my data service. I am unsure how to resolve the conflict and any assistance would be greatly appreciated.
My Data Service Code:
`
<data name="restds" transports="http https">
<config enableOData="false" id="restdb">
<property name="carbon_datasource_name">REST</property>
</config>
<query id="query2" useConfig="restdb">
<sql>select UserCode,FirstName,LastName from UserDB.dbo.Users where UserCode=?</sql>
<result outputType="json" useColumnNumbers="true"> {
"users": {
"user": [
 {
 "UserCode": "$1",
 "FirstName": "$2",
 "LastName": "$3"
 }
 ]
 }
} 
 
 </result>
<param name="UserCode" optional="false" sqlType="STRING"/>
</query>
<resource method="GET" path="Users">
<call-query href="query2">
<with-param name="UserCode" query-param="UserCode"/>
</call-query>
</resource>
</data>
My REST API Code:
`<api xmlns="http://ws.apache.org/ns/synapse" name="DSAPI2" context="/dsapi2">
<resource methods="GET" uri-template="/{UserCode}">
<inSequence>
<call>
<endpoint>
<http method="GET" uri-template="http://localhost:8280/services/restds/Users"/>
</endpoint>
</call>
<filter xpath="$body//FirstName/text() != ''">
<then>
<log>
<property name="Message" value="Name Exists Lets redirect"/>
</log>
<property name="HTTP_SC" value="302" scope="axis2" type="STRING"/>
<property name="Location" value="https://wso2.com/" scope="transport" type="STRING"/>
</then>
<else>
<log>
<property name="HTTP_SC" value="302"/>
<property name="Location" value="https://www.youtube.com/"/>
</log>
</else>
</filter>
<respond/>
</inSequence>
<outSequence/>
<faultSequence/>
</resource>
</api>
`
I was expecting to redirect when testing the API, however the response body I am getting is:
{
"users": {}
}
Which is from the json in my data service
You can't do that in the dataservice. In REST API on Integration studio, while you get successful response, you can use HTTP redirect 302. Use property HTTP_SC with 302 code and http header with Location like below:
<property name="HTTP_SC" scope="axis2" type="STRING" value="302"/>
<property name="Location" value="https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections" scope="transport" type="STRING"/>
You can set the Location address to your c# website.
But I am not sure, if that that solution is not too awkward.
See docs:
Web/HTTP/Redirections and Web/HTTP/Headers/Location
As tmoasz mentioned. You can't do it in the Dataservice, what you can do is interface the Dataservice with an API and handle the redirection logic there. You don't have to use DbLookup or additional mediators to connect to the DB. Simply call your Dataservice from the API. Something like below.
<?xml version="1.0" encoding="UTF-8"?>
<api context="/checkuser" name="YOURAPI" xmlns="http://ws.apache.org/ns/synapse">
<resource methods="GET">
<inSequence>
<call>
<endpoint>
<http method="get" uri-template="http://localhost:8290/rcsrest/Users">
<suspendOnFailure>
<initialDuration>-1</initialDuration>
<progressionFactor>-1</progressionFactor>
<maximumDuration>0</maximumDuration>
</suspendOnFailure>
<markForSuspension>
<retriesBeforeSuspension>0</retriesBeforeSuspension>
</markForSuspension>
</http>
</endpoint>
</call>
<filter xpath="$body//FirstName/text() != ''">
<then>
<log>
<property name="Message" value="Name Exists Lets redirect"/>
</log>
<property name="HTTP_SC" scope="axis2" type="STRING" value="302"/>
<property name="Location" value="https://to-someplace" scope="transport" type="STRING"/>
</then>
<else>
<log>
<property name="Message" value="Name is empty no redirection"/>
</log>
</else>
</filter>
<respond/>
</inSequence>
<outSequence/>
<faultSequence/>
</resource>
</api>
Update
Adding the Query Param to the Payload.
Note: GET request will not allow you to send Payloads in the request body, hence change the Dataservice method to a POST as well.
Dataservice
<data name="restds" transports="http https">
<config enableOData="false" id="restdb">
<property name="carbon_datasource_name">REST</property>
</config>
<query id="query2" useConfig="restdb">
<sql>select UserCode,FirstName,LastName from UserDB.dbo.Users where UserCode=?</sql>
<result outputType="json" useColumnNumbers="true"> {
"users": {
"user": [
 {
 "UserCode": "$1",
 "FirstName": "$2",
 "LastName": "$3"
 }
 ]
 }
} 
 
 </result>
<param name="UserCode" optional="false" sqlType="STRING"/>
</query>
<resource method="POST" path="Users">
<call-query href="query2">
<with-param name="UserCode" query-param="UserCode"/>
</call-query>
</resource>
</data>
API
<api xmlns="http://ws.apache.org/ns/synapse" name="DSAPI2" context="/dsapi2">
<resource methods="GET" uri-template="/{UserCode}">
<inSequence>
<payloadFactory media-type="xml">
<format>
<body xmlns="">
<p:_getusers xmlns:p="ws.wso2.org/dataservice/query2">
<xs:UserCode xmlns:xs="ws.wso2.org/dataservice/query2">$1</xs:UserCode>
</p:_getusers>
</body>
</format>
<args>
<arg evaluator="xml" expression="$url:UserCode"/>
</args>
</payloadFactory>
<call>
<endpoint>
<http method="POST" uri-template="http://localhost:8280/services/restds/Users"/>
</endpoint>
</call>
<filter xpath="$body//FirstName/text() != ''">
<then>
<log>
<property name="Message" value="Name Exists Lets redirect"/>
</log>
<property name="HTTP_SC" value="302" scope="axis2" type="STRING"/>
<property name="Location" value="https://wso2.com/" scope="transport" type="STRING"/>
</then>
<else>
<log>
<property name="HTTP_SC" value="302"/>
<property name="Location" value="https://www.youtube.com/"/>
</log>
</else>
</filter>
<respond/>
</inSequence>
<outSequence/>
<faultSequence/>
</resource>
</api>

Get content of local-entry into Basic Authorization property

I am using the esb as a mediator, to translate rest to soap basically. The soap I am invoking is secure, so I need a Authorization property. I have no problem using this service:
<resource methods="GET" uri-template="/getAllZones">
<inSequence>
<property xmlns:ns="http://org.apache.synapse/xsd" name="Authorization" expression="fn:concat('Basic ', base64Encode('user:password'))" scope="transport"></property>
<property name="DISABLE_CHUNKING" value="true" scope="axis2"></property>
<payloadFactory media-type="xml">
<format>
<zon:GetAllZones xmlns:zon="http://someUrl.xsd">
<Filter></Filter>
</zon:GetAllZones>
</format>
<args></args>
</payloadFactory>
<send>
<endpoint key="myEndpoint"></endpoint>
</send>
<log level="full"></log>
</inSequence>
<outSequence>
<property name="messageType" value="application/json" scope="axis2" type="STRING"></property>
<send></send>
</outSequence>
</resource>
But I want to store the user and password as a local entry inside ESB. I have tried this:
<property xmlns:ns="http://org.apache.synapse/xsd" name="Authorization" expression="fn:concat('Basic ', base64Encode(get-property('user'):get-property('password')))" scope="transport"></property>
But it is not working. Any Ideas?
Thanks in advance
You can store your values inside the registry. Then access them using get-property with scope registry.
For example, if you have some registry resource stored in conf:/creds/user and this has properties username and password, you can access those using:
get-property('registry', 'conf:/creds/user#username') and get-property('registry', 'conf:/creds/user#password')
As an example, in order to access properties setup as follows:
You would use something like:
<inSequence>
<log level="custom">
<property name="Username" expression="get-property('registry', 'conf:/user#username')"/>
<property name="Password" expression="get-property('registry', 'conf:/user#password')"/>
<property name="Encoded" expression="fn:concat('Basic ', base64Encode(fn:concat(get-property('registry', 'conf:/user#username'), ':', get-property('registry', 'conf:/user#username'))))"/>
</log>
<respond/>
</inSequence>
But you might want to consider encrypting the password. In which case you should use the secure vault inside the ESB. You can access something in a secure vault using alias {wso2:vault-lookup('user')}.
More details in https://docs.wso2.com/display/ESB490/Working+with+Passwords.

local entry as filter criteria in wso2 esb mediation

I have a sequence as part of proxy service which filters based on "Source and Regular Expression". I have defined source as element value coming as part of SOAP request and regular expression as "local entry defined in ESB". However, result is not what I am expecting.
Local Entry is defined as Inline Text (myFields) - FIELD1|FIELD2|FIELD3
Mediation sequence is defined as -
<sequence xmlns="http://ws.apache.org/ns/synapse" name="007">
<property xmlns:ns="http://org.apache.synapse/xsd" name="fieldName" expression="$body/fieldName/text()" scope="default" type="STRING"/>
<filter xmlns:ns="http://org.apache.synapse/xsd" source="get-property('fieldName')" regex="get-property('myFields')">
<then>
<log level="full" separator="*****YES*********">
<property name="myFields" expression="get-property('myFields')"/>
</log>
</then>
<else>
<log level="full" separator="*********NO**************">
<property name="myFields" expression="get-property('myFields')"/>
</log>
</else>
</filter>
</sequence>
When I am sending SOAP request as -
<body>
<fieldName>FIELD1</fieldName>
</body>
execution is always going to else part. Any suggestion ?
With filter mediator, regex attribute must be a string, not an expression.
You can use XPATH2 "matches"
Sample :
<inSequence>
<property name="FORCE_SC_ACCEPTED" value="true" scope="axis2"/>
<property name="fieldName" expression="$body/fieldName/text()"/>
<property xmlns:fn="http://www.w3.org/2005/xpath-functions" name="match" expression="fn:matches(syn:get-property('fieldName'),syn:get-property('myFields'))"/>
<filter source="get-property('match')" regex="true">
<then>
<log level="full" separator="*****YES*********">
<property name="myFields" expression="get-property('myFields')"/>
</log>
</then>
<else>
<log level="full" separator="*********NO**************">
<property name="myFields" expression="get-property('myFields')"/>
</log>
</else>
</filter>
<log level="full"/>
</inSequence>

How can i handle this in Wso2ESB which is not passing Respone to Client

i am inserting in multiple table some of those is directly insertion and some more is we need split it means use iterate mediator i have done using two proxy and add fault sequence also insertion is ok if my DSS is down it is passing proper fault to client .the problem is when error occuers in dss like ..primarykey voilation like this i added this property <property name="FORCE_ERROR_ON_SOAP_FAULT" value="true"/> in every sequence even though its throwing like this error
ERROR - NativeWorkerPool Uncaught exception
org.apache.axiom.om.OMException: com.ctc.wstx.exc.WstxParsingException: Illegal processing instruction target ("xml"); xml (case insensitive) is reserved by the specs.
at [row,col {unknown-source}]: [1,167]
and its giving ESB side error like above i am attaching my fault sequence ..
<sequence xmlns="http://ws.apache.org/ns/synapse" name="fault">
<property xmlns:ns="http://org.apache.synapse/xsd" name="actionid" expression="get-property('actionid')"/>
<property xmlns:ns="http://org.apache.synapse/xsd" name="actiondetailid" expression="get-property('actiondetailid')"/>
<dbreport>
<connection>
<pool>
<password>Youtility11</password>
<user>youtilitydba</user>
<url>jdbc:postgresql://localhost:5432/USCProduction</url>
<driver>org.postgresql.Driver</driver>
</pool>
</connection>
<statement>
<sql>
<![CDATA[ delete from tactiondetail where actiondetailid=?]]></sql>
<parameter xmlns:ns="http://org.apache.synapse/xsd" expression="get-property('actiondetailid')" type="BIGINT"/>
</statement>
</dbreport>
<log level="full"/>
<dbreport>
<connection>
<pool>
<password>Youtility11</password>
<user>youtilitydba</user>
<url>jdbc:postgresql://localhost:5432/USCProduction</url>
<driver>org.postgresql.Driver</driver>
</pool>
</connection>
<statement>
<sql>
<![CDATA[ delete from tactiondetail where actionid=?]]></sql>
<parameter xmlns:ns="http://org.apache.synapse/xsd" expression="get-property('actionid')" type="BIGINT"/>
</statement>
</dbreport>
<log>
<property name="MESSAGE" value="Executing default 'fault' sequence"/>
<property xmlns:ns="http://org.apache.synapse/xsd" name="ERROR_CODE" expression="get-property('ERROR_CODE')"/>
<property xmlns:ns="http://org.apache.synapse/xsd" name="ERROR_MESSAGE" expression="get-property('ERROR_MESSAGE')"/>
</log>
<switch xmlns:ns="http://org.apache.synapse/xsd" source="get-property('ERROR_CODE')">
<case regex="500000">
<property name="ERROR_MESSAGE" value="duplicate key value violates or The system is attempting to access an inactive service " scope="default" type="STRING"/>
</case>
<case regex="101503">
<property name="ERROR_MESSAGE" value="Error connecting to the back end" scope="default" type="STRING"/>
</case>
</switch>
<payloadFactory>
<format>
<ResponseJSON xmlns="">
<Exception>$1</Exception>
<Status>$2</Status>
<Total>0</Total>
</ResponseJSON>
</format>
<args>
<arg xmlns:ns="http://org.apache.synapse/xsd" expression="get-property('ERROR_MESSAGE')"/>
<arg xmlns:ns="http://org.apache.synapse/xsd" expression="get-property('ERROR_CODE')"/>
</args>
</payloadFactory>
<header name="To" action="remove"/>
<property name="RESPONSE" value="true" scope="default" type="STRING"/>
<property name="NO_ENTITY_BODY" action="remove" scope="axis2"/>
<log level="full"/>
<send/>
</sequence>
its passing fault message whenever dss is off but not passing when dss is running mode giving error like this
Caused by: com.ctc.wstx.exc.WstxParsingException: Illegal processing instruction target ("xml"); xml (case insensitive) is reserved by the specs.
at [row,col {unknown-source}]: [1,167]
As the error says: "Illegal processing instruction target ("xml"); xml (case insensitive) is reserved by the specs." it seems that you are using a reserved word "xml" in the response that you return from DSS. Please double check the XML response from DSS, and run the response trough an XML validator (may be use Eclipse XML validator) to see if the response is all OK.

How to pass dynamic fields Query to database using WSO2ESB OR WSO2DSS

I want to pass my query to database for retrieving the column. I am passing dynamic columns using ESB my configuration is like this
<proxy xmlns="http://ws.apache.org/ns/synapse" name="dbl3" transports="https,http" statistics="disable" trace="disable" startOnLoad="true"> <target>
<inSequence>
<property name="A" value="select e_name,e_address from emp where " scope="default" type="STRING"/>
<property name="B" expression="//fieldname/text()" scope="default" type="STRING"/>
<property name="C" expression="//fieldvalue/text()" scope="default" type="STRING"/>
<property name="D" value="=" scope="default" type="STRING"/>
<property name="E" expression="concat(get-property('A'),get-property('B'),get-property('D'),get-property('C'))" scope="default" type="STRING"/>
<dblookup>
<connection>
<pool>
<password>Youtility11</password>
<user>youtilitydba</user>
<url>jdbc:postgresql://localhost:5432/sample</url>
<driver>org.postgresql.Driver</driver>
</pool>
</connection>
<statement>
<sql>get-property('E')</sql>
<result name="ee" column="e_name"/>
</statement>
</dblookup>
<log level="custom">
<property name="AA" expression="get-property('A')"/>
<property name="BB" expression="get-property('B')"/>
<property name="CC" expression="get-property('C')"/>
<property name="DD" expression="get-property('ee')"/>
<property name="EE" expression="get-property('E')"/>
</log>
</inSequence>
<outSequence>
<send/>
</outSequence> </target> <description></description> </proxy>
My output like this:
EE = select e_name,e_address from emp where e_address=thane
EE = select e_name,e_address from emp where e_no=5
Based on input values query will generate, how can I pass the above query to the database?
My error is like this:
[2013-01-24 14:22:32,743] ERROR - DBLookupMediator Error executing statement : get-property('E') against DataSource : jdbc:postgresql://localhost:5432/sample
org.postgresql.util.PSQLException: ERROR: syntax error at or near "get"
Position: 1
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2102)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1835)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:257)
at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:500)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:388)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeQuery(AbstractJdbc2Statement.java:273)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:96)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:96)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:96)
at org.apache.synapse.mediators.db.DBLookupMediator.processStatement(DBLookupMediator.java:46)
at org.apache.synapse.mediators.db.AbstractDBMediator.mediate(AbstractDBMediator.java:143)
at org.apache.synapse.mediators.AbstractListMediator.mediate(AbstractListMediator.java:60)
at org.apache.synapse.mediators.base.SequenceMediator.mediate(SequenceMediator.java:114)
at org.apache.synapse.core.axis2.ProxyServiceMessageReceiver.receive(ProxyServiceMessageReceiver.java:154)
at org.apache.axis2.engine.AxisEngine.receive(AxisEngine.java:181)
at org.apache.axis2.transport.http.util.RESTUtil.invokeAxisEngine(RESTUtil.java:144)
at org.apache.axis2.transport.http.util.RESTUtil.processXMLRequest(RESTUtil.java:89)
at org.apache.synapse.transport.nhttp.util.RESTUtil.processPOSTRequest(RESTUtil.java:189)
at org.apache.synapse.transport.nhttp.ServerWorker.processEntityEnclosingMethod(ServerWorker.java:411)
at org.apache.synapse.transport.nhttp.ServerWorker.run(ServerWorker.java:268)
at org.apache.axis2.transport.base.threads.NativeWorkerPool$1.run(NativeWorkerPool.java:172)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:679)
I'm afraid you can't pass a SQL statement to the "<sql/>" element of the DBLookup/DBReport mediators using "get-property" construct. Besides, I believe what you're trying to do is sort of a hack to pass variables to a SQL query. The proper way of doing this should be by using WSO2 Data Services Server. Please refer the documentation of WSO2 DSS [1] which provides you with a comprehensive guide on how to use it to achieve your data manipulative tasks with its rich set of features offered to the users.
[1] http://docs.wso2.org/wiki/display/DSS301/WSO2+Data+Services+Server+Documentation