I want to update a WsdlRequest parameter values at runtime using groovy.
Say I have a WsdlRequest that contains two parameters: name, address. I’d like to pass to this WsdlRequest the values I would like the request to have prior to creating a WsdlSubmit instance. I know the base code is this:
WsdlProject project = new WsdlProject()
WsdlInterface iface = WsdlInterfaceFactory.importWsdl(project, wsdl, true)[0]
WsdlOperation operation = (WsdlOperation) iface.getOperationAt(3)
WsdlRequest request = operation.addNewRequest(requestName)
request.setRequestContent (requestContent);
The requestContent is the soapxml in a String format. Is there a good way to insert my values (say I want name value to be ‘Test’ and address value to be ‘Example’ for the request)? I’d rather not store the xml as a string and update this if I already have that information when I generate the request.
Here is an example of the xml:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:data="http://test.com">
<soapenv:Header/>
<soapenv:Body>
<data:updateFieldName>
<fieldId>?</fieldId>
<!--Optional:-->
<newFieldId>?</newFieldId>
</data:updateFieldName>
</soapenv:Body>
</soapenv:Envelope>
Prior to creating the WsdlRequest, I have created a groovy object that contains the values I want to fill into the above soap xml message. Let's say this object states the fieldId = 10 and the newFieldRequest = 15. I a not sure how to pass those values into the request. Is there a way to do this with the SoapUI API? I do have a pro license also.
You can use an XMLHolder to parse your xml, and the you can use setNodeValue(xpath, value) to specify the value for this node, in your case this looks like:
import com.eviware.soapui.support.XmlHolder
// your request content
def requestContent = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:data="http://test.com">'+
'<soapenv:Header/>'+
'<soapenv:Body>'+
'<data:updateFieldName>'+
'<fieldId>?</fieldId>'+
'<newFieldId>?</newFieldId>'+
'</data:updateFieldName>'+
'</soapenv:Body>'+
'</soapenv:Envelope>'
// parse it as xml bean
def requestXml = new XmlHolder(requestContent)
// set your node values
requestXml.setNodeValue("//*:fieldId","10");
requestXml.setNodeValue("//*:newFieldId","15");
Then to get the xml content as string again you can use getXml() method as follows:
WsdlRequest request = ...
// to set in your request use getXml()
request.setRequestContent (requestXml.getXml());
For more info you can take a look at XMLHolder api documentation
There is also another way to do this without groovy script; using properties. You can add a properties in for example your TestCase and then use it directly in your TestStep request like this:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:data="http://test.com">
<soapenv:Header/>
<soapenv:Body>
<data:updateFieldName>
<fieldId>${#TestCase#yourProperty}</fieldId>
<newFieldId>${#TestCase#anotherProperty}</newFieldId>
</data:updateFieldName>
</soapenv:Body>
</soapenv:Envelope>
If you're interested in this take a look at: Working with properties and property expansion
EDIT BASED ON COMMENT:
I write a whole example with your code and xml holder using a public wsdl due you can try and get the result without the NPE and compare with yours to check what is going on:
import com.eviware.soapui.impl.wsdl.WsdlProject
import com.eviware.soapui.impl.wsdl.WsdlInterface
import com.eviware.soapui.impl.WsdlInterfaceFactory
import com.eviware.soapui.impl.wsdl.WsdlOperation
import com.eviware.soapui.impl.wsdl.WsdlRequest
import com.eviware.soapui.support.XmlHolder
wsdl = "http://www.webservicex.net/geoipservice.asmx?WSDL"
WsdlProject project = new WsdlProject()
WsdlInterface iface = WsdlInterfaceFactory.importWsdl(project, wsdl, true )[0]
WsdlOperation operation = (WsdlOperation) iface.getOperationByName( "GetGeoIP" )
WsdlRequest request = operation.addNewRequest("Request")
def defaultRequest = operation.createRequest(true)
def xmlHolder = new XmlHolder(defaultRequest)
xmlHolder.setNodeValue("//*:IPAddress","127.0.0.1");
request.setRequestContent (xmlHolder.getXml());
Hope this helps,
Below is the groovy script for the following:
Update all WSDL Definitions in the project.
Recreate all requests to updated ones.
Takes backup of old requests.
import static com.eviware.soapui.impl.wsdl.actions.iface.UpdateInterfaceAction.recreateRequests
import static com.eviware.soapui.impl.wsdl.actions.iface.UpdateInterfaceAction.recreateTestRequests
project = testRunner.testCase.testSuite.project; //get the project reference
def ifaceList = project.getInterfaceList(); //get all the interfaces present in the project in a list
//start a loop for number of interfaces
for(int i = 0; i < project.getInterfaceCount() ; i++)
{
def iface = project.getInterfaceAt(i);
def url = iface.definition;
iface.updateDefinition( url, true); //updateDefinition(String url , Boolean createRequests)
//The above part updates the definition
//The part below recreates the requests based on updated wsdl definition
//syntax -
//recreateRequests( WsdlInterface iface, boolean buildOptional, boolean createBackups, boolean keepExisting, boolean keepHeaders )
recreateRequests(iface,true,true,true,true);
recreateTestRequests(iface,true,true,true,true);
}
//End of Script//
Related
I have an Azure Custom Connector to a SOAP API that is configured with SOAP to REST. One of the methods have datetime as input:
I am genereting the DateTime with the following expression:
formatDateTime(addDays(utcNow(), -1), 's')
With the following raw input from Logic Apps, I get datetime format exception
{
"method": "post",
"path": "/MethodWithDates",
"retryPolicy": {
"type": "None"
},
"body": {
"MethodWithDates": {
"timefrom": "2019-03-18T15:59:03",
"timeto": "2019-03-19T15:59:03"
}
}
Errormessage from API:
The value '3/18/2019 3:59:03 PM' cannot be parsed as the type 'DateTime'.'
Notice how the datetime format has changed from raw output to recieved in the API. This leads me to believe the custom connector somehow changes the time format.
If I call the same endpoint with SOAP UI with the following SOAP request I get correct response. Notice the Datetime format is same as in RAW input from Logic app:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
<soapenv:Header/>
<soapenv:Body>
<tem:MethodWithDates>
<tem:timefrom>2019-03-18T15:13:31</tem:timefrom>
<tem:timeto>2019-03-19T15:13:31</tem:timeto>
</tem:MethodWithDates>
</soapenv:Body>
</soapenv:Envelope>
Interestingly, this seems only to happen for the "s" format specifier, if I format the value in any other way it is passed through in the format I specify. I still get an error in the API as its a WCF API and it seems to require the "s" format.
I can reproduce the same error when SOAP service has a Datetime input which I believe is not parsing correctly.
I am able to make this work by changing the input Datetime fields in Soap Service to string.
Non Working SOAP Service code:
public string GetDaysBetweenDates(DateTime timefrom, DateTime timeto)
{
double value = (timeto - timefrom).TotalDays;
return string.Format("Difference is: {0}", value);
}
Working WSDL Code
public string GetDaysBetweenDates(string timefrom, string timeto)
{
DateTime fromdate = DateTime.Parse(timefrom);
DateTime toDate = DateTime.Parse(timeto);
double value = (fromdate - toDate).TotalDays;
return string.Format("Difference is: {0}", value);
}
u/KetanChawda-MSFT answer is good enough, if you are able to actually change the webservice, but since this was out of our control on this one we had to do something else.
We created a separate SOAP custom connector, just for this one method with SOAP pass through.
The connector has one method, configured like this, with a default WCF API:
Url - http://hostname/Service1.svc/SoapPassThrough
Add two custom headers: Content-Type text/xml and SOAPAction methodname (ours: http://tempuri.org/IService1/methodname where tempuri is namespace
Set body to {} (Empty JSON Object)
In your logic app, you can then create a variable that contain all of the XML for a standard Soap Request. I Used SOAP UI to create a SOAP request and just pasted in the XML from the generated request. This variable can be used as body in the logic app when you consume the service.
This resource can be helpful for this: https://blogs.msdn.microsoft.com/david_burgs_blog/2018/05/03/friendlier-soap-pass-through-with-logic-app-designer-ux/
From what we have concluded, it seems like the custom connector actually sends a string datatype instead of a datetime. Creating the XML request ourself seems to fix this issue.
This is my response from soapUI
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<SearchAirFaresResponse xmlns="http://www.sample.com/xchange">
<SearchAirFaresResult>
<![CDATA[
<FareSearchResponse>
<MasterDetails>
<CurrencyCode>INR</CurrencyCode>
<RecStNo>1</RecStNo>
<SessionID>5705b1a6-95ac-486c-88a1f90f85e57590</SessionID>
</MasterDetails>
</FareSearchResponse>
]]>
</SearchAirFaresResult>
</SearchAirFaresResponse>
</soap:Body>
</soap:Envelope>
How to extract SessionID element which is inside CDATA using groovy script and use it in another request like
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<xch:GetMoreFares>
<xch:sGetMoreFare>
<![CDATA[
<MoreFlights>
<MasterDetails>
<NoOfResult Index="1">40</NoOfResult>
<BranchId>1</BranchId>
<SessionId>5705b1a6-95ac-486c-88a1f90f85e57590</SessionId>
</MasterDetails>
<Journey>DOM</Journey>
<ResponseType>XML</ResponseType>
<SearchType>OW</SearchType>
</MoreFlights>
]]>
</xch:sGetMoreFare>
</soap:Body>
</soap:Envelope>
3.I have been searching lot but didnt get the right one,and also am new to groovy script using soapUi , pls guide me stepwise procedure in soapUi to implement .
To do so you can use a Groovy testStep, inside it get the SOAP testStep where you've the response with desired sessionID and use a XmlSlurper to parse the response and get the CDATA value. Note that XmlSlurper treat CDATA as String so you've to parse it again. Finally save the returned value as a TestSuite or TestCase level (in the example I use TestCase):
// get your first testStep by its name
def tr = testRunner.testCase.getTestStepByName('Test Request')
// get your response
def response = tr.getPropertyValue('response')
// parse the response and find the node with CDATA content
def xml = new XmlSlurper().parseText(response)
def cdataContent = xml.'**'.find { it.name() == 'SearchAirFaresResponse' }
// XmlSlurper treat CDATA as String so you've to parse
// its content again
def cdata = new XmlSlurper().parseText(cdataContent.toString())
// finally get the SessionID node content
def sessionId = cdata.'**'.find { it.name() == 'SessionID' }
// now save this value at some level (for example testCase) in
// order to get it later
testRunner.testCase.setPropertyValue('MySessionId',sessionId.toString())
Then change a bit your second testStep to use property expansion to get the MySessionId property in your second request as ${#TestCase#MySessionId}:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<xch:GetMoreFares>
<xch:sGetMoreFare>
<![CDATA[
<MoreFlights>
<MasterDetails>
<NoOfResult Index="1">40</NoOfResult>
<BranchId>1</BranchId>
<SessionId>${#TestCase#MySessionId}</SessionId>
</MasterDetails>
<Journey>DOM</Journey>
<ResponseType>XML</ResponseType>
<SearchType>OW</SearchType>
</MoreFlights>
]]>
</xch:sGetMoreFare>
</soap:Body>
</soap:Envelope>
This is also most similar to albciff's answer, but with little variant (using reusable closure to parse).
Here is the Script Assertion for the first request step. This will avoid additional groovy script step in the test case.
Please follow the appropriate comments inline:
Script Assertion:
/**
* This is Script Assertion for the first
* request step which extracts cdata from response,
* then sessionId from extracted cdata. And
* Assigns the value at test case level, so that
* it can be used in the rest of the test steps of
* the same test case.
* However, it is possible to store the sessionId
* at Suite level as well if you want use the sessionId
* across the test cases too.
*
* */
/**
* Closure to search for certain element data
* input parameters
* xml data, element name to search
**/
def searchData = { data, element ->
def parsedData = new XmlSlurper().parseText(data)
parsedData?.'**'.find { it.name() == element} as String
}
//Assert the response
assert context.response, "Current step response is empty or null"
//Get the cdata part which is inside element "SearchAirFaresResult"
def cData = searchData(context.response,'SearchAirFaresResult')
//Assert CDATA part is not null
assert cData, "Extracted CDATA of the response is empty or null"
//Get the SessionID from cdata
def sessionId = searchData(cData, 'SessionID')
//Assert sessionId is not null or empty
assert sessionId, "Session Id is empty or null"
log.info "Session id of response $sessionId1"
//Set the session to test case level custom property
//So, that it can be used for the rest of the steps
//in the same test case
context.testCase.setPropertyValue('SESSION_ID', sessionId)
In the following test steps, you can use the saved SESSION_ID in the following ways:
If the following step is request step (REST, SOAP, HTTP, JDBC etc), then use property expansion ${#TestCase#SESSION_ID} like <SessionId>${#TestCase#SESSION_ID}</SessionId>
If the following step is groovy script, then use one of the below:
context.expand('${#TestCase#SESSION_ID}') or
context.testCase.getPropertyValue('SESSION_ID') or
testRunner.testCase.getPropertyValue('SESSION_ID')
I am in a pickle.
I feel there is a simple solution to what I want to achieve, but I am at a loss to how to go about it.
Basically, I am standing up some mock Soap services.
I want to echo back an update call with that which is passed in.
My request looks like this:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"/>
<soap:Body>
<ns2:setEvents xmlns:ns2="http://example.com/eventingservices" xmlns:ns3="http://example.com/eventing/sdo">
<setEventsRequest>
<SystemCode>ABC</SystemCode>
<Event>
<EventTypeCode>01</EventTypeCode>
</Event>
<Event>
<EventTypeCode>04</EventTypeCode>
</Event>
</setEventsRequest>
</ns2:SetEvents>
</soap:Body>
</soap:Envelope>
I then want to simply transfer the list of Events to the response. They have the same attributes as the request.
A typical response, would look like this:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"/>
<soapenv:Body>
<qu:setEventsResponse xmlns:qu="http:/example.com/eventingServices">
<setEventsResponse>
<Event>
<EventTypeCode>01</EventTypeCode>
</Event>
<Event>
<EventTypeCode>04</EventTypeCode>
</Event>
</setEventsResponse>
</qu:setEventsResponse>
</soapenv:Body>
</soapenv:Envelope>
I tried using the following Groovy script, replacing the Events in the response with ${events}:
def groovyUtils = new com.eviware.soapui.support.GroovyUtils(context)
def holder = groovyUtils.getXmlHolder(mockRequest.requestContent)
def events = String.valueOf(holder.getNodeValues("//Event"))
context.setProperty("events", events);
I also tried the above without doing the string of. To no avail.
Please please help me.
I'll buy you a beer if I can get this damned thing to work!
I suppose that you have a mock service in SOAPUI configured as follows.
Operation with a response similar to:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"/>
<soapenv:Body>
<setEventsResponse xmlns="http:/example.com/eventingServices">
<setEventsResponse>
${events}
</setEventsResponse>
</setEventsResponse>
</soapenv:Body>
</soapenv:Envelope>
And on the onRequestScript tab of your mockService you want to have the necessary script to get the <Event> nodes from the request and put it in the response using ${events} property. To do so, I recommend that you can use XmlSlurper as follows:
import groovy.xml.StreamingMarkupBuilder
// parse the request
def reqSlurper = new XmlSlurper().parseText(mockRequest.requestContent)
// find all 'Event' nodes from request
def events = reqSlurper.depthFirst().findAll { it.name() == 'Event' }
// builder to convert the nodes to string
def smb = new StreamingMarkupBuilder()
// concatenate in the string all "<Event>" nodes from request
def eventAsString = ''
events.each{
eventAsString += smb.bindNode(it)
}
// set the property to use in the response
context.setProperty("events", eventAsString);
That's all to make it work :).
Additionally note that there are some mistakes in you xmls. The request in your question is not well formed: </ns2:SetEvents> not close <ns2:setEvents> (note uppercase), and if you want that xmlns:ns2="http://example.com/eventingservices" namespace applies to all childs of <SetEvents> you have to add ns2 to all the child nodes or remove the ns2 to make it default for this subtree:<SetEvents xmlns="http://example.com/eventingservices">` (this applies also for your response)
Hope it helps,
In an XML that I am parsing, I have the following:
<event>
<book>Felicity Fly</book>
<author>Christina Gabbitas</author>
<bookImgUrl>http://www.whsmith.co.uk/Images/Products\957\255\9780957255203_t_f.jpg</bookImgUrl>
<info>Christina Gabbitas will be signing copies of her new book, Felicity Fly. Books should be bought from WHSmith. Proof of purchase may be necessary</info>
<date>20 Apr 2013</date>
<startTime>10:30</startTime>
<location>
<name>WHSmith Chester</name>
<address>5-7 Foregate Street</address>
<city>Chester</city>
<county>Cheshire</county>
<postcode>CH1 1HH</postcode>
<tel>01244 321106</tel>
</location>
</event>
I want to create a DateTime object from the two nodes <date> and <startTime>. So I am doing this:
var EventEntity = new Event()
{
Book = e.Book,
BookImgUrl = e.BookImgUrl,
Info = e.Info,
Start = new DateTime().**?**
};
But When I press the dot [.] after the DateTime object, I am not getting the Parse method from Intellisense, why is this? What am I doing wrong?
I was planning on using the solution outlined in this post.
Parse is a static method but you are calling it as it were an instance method.
You should call it in this way:
var EventEntity = new Event()
{
Book = e.Book,
BookImgUrl = e.BookImgUrl,
Info = e.Info,
Start = DateTime.ParseExact(...) // or DateTime.TryParseExact(...)
};
My question is about putting data elements (from groovy script) in the response in SoapUI.
I've an array of data that I would like to put in my response (in different tags/elements)
I'm aware of putting a simple element like this:
The element "MyName" in the Xml response:
<ns:MyName>${MyName}</ns:MyName>
Is mapped from the Groovy script by
context.setProperty("MyName" , "My name" )
Now the problem:
my Xml response looks like this:
<soapenv:Body>
<ns:GetDataSummaryResponse>
<!--Optional:-->
<ns:GetDataSummaryResult>
<ns:DataSummary>
<!--Zero or more repetitions:-->
<ns:DataSummaryResponseDetail>
<ns:Name>?</ns:Name>
<!--Optional:-->
<ns:DataProgress>
<!--Optional:-->
<From>?</From>
<!--Optional:-->
<Procent>?</Procent>
<!--Optional:-->
<To>?</To>
<!--Optional:-->
In Groovy I've built data array which is filled with data for example like this:
context:[DataSummary:[DataSummaryResponseDetail:[Name:My name, DataProgress:[From:some text, **Procent:some value**, To:some text]]]
In the response I'm able to see the whole value of ${DataSummary} but how do I get the element "Procent"
I maybe am wrong about how to build my context data, but feel free to adjust!
I managed to do this with a lot of searching.
First of all, I needed to use "=" in front of the element to be able to get a handle like
${=DataSummary[0]}
This works on the top level of my data shown before.
But to really solve the problem I created a Class in Groovy like this:
Code: Select all
Class DataSummaryResponseDetail {
public String name
public DataProgress DataProgress = new DataProgress()
public Value[] value = new Value[2]
}
class DataProgress {
public Date From
public Date To
public float Procent
}
class Value {
public String Currency
public int Amount
}
def hsrd = new DataSummaryResponseDetail()
hsrd.name = 'Some name'
hsrd.value[0]=new Value()
hsrd.value[0].Amount = 1000
hsrd.value[0].Currency = 'SEK'
hsrd.totalValue.Amount = 2000
hsrd.totalValue.Currency = 'USD'
and then in the response xml. I refer to elements like this
${hsrd.value[0].Amount}
${=hsrd.totalValue.Amount}
def hsrd = new DataSummaryResponseDetail()
hsrd.name = 'Some name'
hsrd.value[0]=new Value()
hsrd.value[0].Amount = 1000
hsrd.value[0].Currency = 'SEK'
hsrd.totalValue.Amount = 2000
hsrd.totalValue.Currency = 'USD'
Problem solved. If you know of a better way, please let me know.
I haven't work with SoapUI, but have you tried this:
${DataSummary.DataSummaryResponseDetail.DataProgress.Procent}
If DataSummary is an array, you could access the first entry using
${DataSummary[0].DataSummaryResponseDetail.DataProgress.Procent}
You can iterate over the array using the method each(), so
${DataSummary.each { it.DataSummaryResponseDetail.DataProgress.Procent }}
would get you all entries.