I am trying to pass data through twilio's Record verb action url. When there are two or more query string parameters it fails, but when there is only one it succeeds.
Succeeds:
var response = '<Response><Say>STUFF TO SAY</Say><Pause length="1"/><Record maxLength="3600" timeout="30" action="/service/training/call/recording?test1=test&test2=test"></Record></Response>';
Fails:
var response = '<Response><Say>STUFF TO SAY</Say><Pause length="1"/><Record maxLength="3600" timeout="30" action="/service/training/call/recording?test1=test"></Record></Response>';
error:
Error on line 1 of document : The reference to entity "test2" must end with the ';' delimiter.
Is there a way I can pass the data through the query string or do I have to resort to using url parameters? "/service/training/call/recording/test/test
Twilio support got back to me. Here is their response.
The fix is to replace the '&' in your code with it's valid-XML replacement - '&'. So your TwiML would look like this:
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Say>STUFF TO SAY</Say>
<Pause length="1"/>
<Record maxLength="3600" timeout="30" action="/service/training/call/recording?test1=test&test2=test">
</Record>
</Response>
Related
So as you can see, we have this XML
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<GetECConfigResponse xmlns="urn:ecs.wsapi.broadon.com">
<Version>2.0</Version>
<DeviceId>4498122730</DeviceId>
<MessageId>ECDK-4498122730-39653600512638909</MessageId>
<TimeStamp>1599408189821</TimeStamp>
<ErrorCode>0</ErrorCode>
<ServiceStandbyMode>false</ServiceStandbyMode>
<ContentPrefixURL>http://example.com</ContentPrefixURL>
<UncachedContentPrefixURL>http://example.com</UncachedContentPrefixURL>
<SystemContentPrefixURL>http://example.com</SystemContentPrefixURL>
<SystemUncachedContentPrefixURL>http://example.com</SystemUncachedContentPrefixURL>
<EcsURL>http://example.com</EcsURL>
<IasURL>http://example.com</IasURL>
<CasURL>http://example.com</CasURL>
<NusURL>http://example.com</NusURL>
</GetECConfigResponse>
</soapenv:Body>
</soapenv:Envelope>
I do this so I can edit stuff inside the xml depending on the user's POST body but I have to edit the stuff I want, and turn it into a XML back again and send it as a response, since it's not possible with Node.JS to edit the XML directly without converting to JS or JSON
const xmlParser = require("xml2json");
var json2xml = require("json2xml");
// Current time in epoch
function currentEpoch() {
const now = Date.now()
return now
}
// Read the XML file
fs.readFile("./ecs/ecommercesoap/getecconfigresponse.xml", function (err, data) {
// Convert the XML to JSON
const xmlObj = xmlParser.toJson(data, {
object: true
})
// Get the user's body details
var deviceId = req.body["DeviceId"]
// Change XML index depending on the user
xmlObj["soapenv:Envelope"]["soapenv:Body"]["GetECConfigResponse"]["DeviceId"] = deviceId
xmlObj["soapenv:Envelope"]["soapenv:Body"]["GetECConfigResponse"]["TimeStamp"] = currentEpoch().toString()
This is where it converts xmlObj back to xml again
// Convert the JSON to XML and finalize it
const finalXml = xmlParser.toXml(xmlObj)
// Set the response's type as application/xml
res.type('application/xml');
// Sending the completely edited XML back to the user
res.send(finalXml)
And as you can see, the output changes when the converting happens. The XML below is after it's edited and parsed back into XML from JSON. If you noticed, <?xml version="1.0" encoding="utf-8"?> is also deleted, which shouldn't be.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<GetECConfigResponse xmlns="urn:ecs.wsapi.broadon.com" Version="2.0" DeviceId="4498122730" MessageId="ECDK-4498122730-39653600512638909" TimeStamp="1599505063565" ErrorCode="0" ServiceStandbyMode="false" ContentPrefixURL="http://example.com" UncachedContentPrefixURL="http://example.com" SystemContentPrefixURL="http://example.com" SystemUncachedContentPrefixURL="http://example.com" EcsURL="http://example.com" IasURL="http://example.com" CasURL="http://example.com" NusURL="http://example.com"/>
</soapenv:Body>
</soapenv:Envelope>
So I wonder what can I do to fix this, the tags start and end with <> in the original while edited one doesn't even have it. Long story short, bunch of stuff got changed. I also tried xml2js instead of xml2json but the output is still the same. I just want my output exactly same as the original file, but with my stuff changed.
In our project we have to deal with manually modifiying soap/xml in NodeJS as well - we've recently switched to fast-xml-parser and are really happy with it. I took your sample input and using the following code:
const xmlToJsonParser = require('fast-xml-parser');
const options = {
ignoreAttributes: false,
ignoreNameSpace: false,
parseNodeValue: true,
parseAttributeValue: true,
trimValues: true,
};
// xmlData refers to the xml-string
const tObj = xmlToJsonParser.getTraversalObj(xmlData, options);
const jsonObj = xmlToJsonParser.convertToJson(tObj, options);
// modify json
jsonObj["soapenv:Envelope"]["soapenv:Body"]["GetECConfigResponse"].DeviceId = 123456;
const JsonToXmlParser = require("fast-xml-parser").j2xParser;
const parser = new JsonToXmlParser({format: true, ignoreAttributes: false});
const xml = parser.parse(jsonObj);
console.log(xml);
it produces:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<GetECConfigResponse xmlns="urn:ecs.wsapi.broadon.com">
<Version>2</Version>
<DeviceId>123456</DeviceId>
<MessageId>ECDK-4498122730-39653600512638909</MessageId>
<TimeStamp>1599408189821</TimeStamp>
<ErrorCode>0</ErrorCode>
<ServiceStandbyMode>false</ServiceStandbyMode>
<ContentPrefixURL>http://example.com</ContentPrefixURL>
<UncachedContentPrefixURL>http://example.com</UncachedContentPrefixURL>
<SystemContentPrefixURL>http://example.com</SystemContentPrefixURL>
<SystemUncachedContentPrefixURL>http://example.com</SystemUncachedContentPrefixURL>
<EcsURL>http://example.com</EcsURL>
<IasURL>http://example.com</IasURL>
<CasURL>http://example.com</CasURL>
<NusURL>http://example.com</NusURL>
</GetECConfigResponse>
</soapenv:Body>
</soapenv:Envelope>
All that's left is to prepend the xml-header, as they've decided to exclude this feature from the parser (https://github.com/NaturalIntelligence/fast-xml-parser/issues/184).
we have a requirement of finding the number of dealers in current country.In the below xml request key-value pair will be varied for every request. Inputs to the soap request will be given in .txt file.Based on the number of inputs in .txt file i need to generate key-value pair xml tags dynamically.
**Format of Input.txt**
1.key1=Fruit,value1=Carrot,Key2=Vegetable,value2=Carrot
2.key1=Vegetable,value1=Potato
3.key1=Fruit,value1=Apple,key2=Fruit,value2=Orange,key3=Fruit,value3=Mango
SoapUI Request
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://www.webserviceX.NET">
<soapenv:Header/>
<soapenv:Body>
<web:GetCitiesByCountry>
<web:CountryName>Ind</web:CountryName>
<web:queryParameters>
<web:key>Fruits</web:key>
<web:value>Mango</web:value>
</web:queryParameters>
<web:queryParameters>
<web:key>Vegetables</web:key>
<web:value>Carrot</web:value>
</web:queryParameters>
</web:GetCitiesByCountry>
</soapenv:Body>
</soapenv:Envelope>
You say you have the reading of the input records sorted, so I've just put some records in a map for demonstration purposes. Then, if your request payload starts off looking like:
<soap:Envelope xmlns:soap="w3.org/2003/05/soap-envelope">
<soap:Header/>
<soap:Body>
<web:GetCitiesByCountry xmlns:web="webserviceX.NET">
<web:CountryName>hello</web:CountryName>
</web:GetCitiesByCountry>
</soap:Body>
</soap:Envelope>
You can loop through your input records and append them to the request:
import groovy.xml.XmlUtil
// Assuming the input document has been read into a HashMap
def map = ['Fruits': 'Banana',
'Vegetable': 'Potato'
]
// Get testStep by it's name
def testStep = context.testCase.getTestStepByName('soapRequest')
// Parse the request
def xml = new XmlSlurper().parseText(testStep.getPropertyValue('request'))
// Loop through the map and append the key/value pairs
xml.Body.appendNode {
map.each {k, v ->
Parameters {
Key(k)
Value(v)
}
}
}
// Check the output
log.info(XmlUtil.serialize( xml ))
// Update the request
testStep.setPropertyValue('request',groovy.xml.XmlUtil.serialize( xml ))
Afterwards, your request will look like:
<soap:Envelope xmlns:soap="w3.org/2003/05/soap-envelope">
<soap:Header/>
<soap:Body>
<web:GetCitiesByCountry xmlns:web="webserviceX.NET">
<web:CountryName>hello</web:CountryName>
</web:GetCitiesByCountry>
<Parameters>
<Key>Fruits</Key>
<Value>Banana</Value>
</Parameters>
<Parameters>
<Key>Vegetable</Key>
<Value>Potato</Value>
</Parameters>
</soap:Body>
</soap:Envelope>
As you said you need to add
<Parameters> <Key>key1</Key> <Value>Value1</Value> </Parameters>
dynamically while executing groovy script
So inside your groovy script in a variable
xmlValue="<Parameters> <Key>key1</Key> <Value>Value1</Value> </Parameters>"
testRunner.testCase.setPropertyValue(xmlNodes,xmlValue)
So now your dynamic xml is in testCase property. So inside your xml where you want to place it
you can place this code
<web:CountryName>${#TestCase#xmlValue}</web:CountryName>
This way it will get added. If you pass it null,then nothing will be added in your xml.
There's some good technical solutions here, but if you buy a SoapUI license, you'll be able to access the data driven test functionality, which does exactly what you want right out of the box. No groovy scripts required.
I recently ran into issues when using an integration record with TBA to request for multiple pages of transaction summary records when using the getPostingTransactionSummary web services action.
I found the first request was consistently successful, but following requests for successive pages would fail giving the following error:
For pages > 1, search parameters must match the original search
exactly
I followed the suggestions in SuiteAnswer 33416 labeled "getPostingTransactionSummary returns
an error: For pages > 1, search
parameters must match the original
search exactly". I then ensured we were requesting the first page first, ensured no field or filter parameters had changed, and was requesting the second page immediately.
The mechanism used to generate signature appears to be correct as all calls to any web service endpoint for the first page are always successful.
The following represents the sequence of requests used to reproduce (where real data is mocked):
Request for first page:
<x:Envelope xmlns:x="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn2="urn:messages_2015_2.platform.webservices.netsuite.com" xmlns:urn="urn:core_2015_2.platform.webservices.netsuite.com">
<x:Header>
<urn:tokenPassport>
<urn1:account>1234567</urn1:account>
<urn1:consumerKey>12345</urn1:consumerKey>
<urn1:token>1234</urn1:token>
<urn1:nonce>62005285</urn1:nonce>
<urn1:timestamp>1478114023</urn1:timestamp>
<urn1:signature algorithm="HMAC-SHA256">NIhrCvTi2SpGfhxbxO8N09WQ7mvXw7oJICiEewCtm1M=</urn1:signature>
</urn:tokenPassport>
</x:Header>
<x:Body>
<urn2:getPostingTransactionSummary>
<urn2:fields>
<urn:period>true</urn:period>
<urn:account>true</urn:account>
<urn:location>true</urn:location>
<urn:subsidiary>true</urn:subsidiary>
</urn2:fields>
<urn2:filters>
<urn:period>
<urn:recordRef internalId="1"/>
<urn:recordRef internalId="2"/>
<urn:recordRef internalId="3"/>
</urn:period>
<urn:account>
<urn:recordRef internalId="985" type="account"></urn:recordRef>
</urn:account>
</urn2:filters>
<urn2:pageIndex>1</urn2:pageIndex>
</urn2:getPostingTransactionSummary>
</x:Body>
</x:Envelope>
Response for first page:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header>
<platformMsgs:documentInfo xmlns:platformMsgs="urn:messages_2015_2.platform.webservices.netsuite.com">
<platformMsgs:nsId>WEBSERVICES_1234</platformMsgs:nsId>
</platformMsgs:documentInfo>
</soapenv:Header>
<soapenv:Body>
<getPostingTransactionSummaryResponse xmlns="urn:messages_2015_2.platform.webservices.netsuite.com">
<platformCore:getPostingTransactionSummaryResult xmlns:platformCore="urn:core_2015_2.platform.webservices.netsuite.com">
<platformCore:status isSuccess="true"/>
<platformCore:totalRecords>2150</platformCore:totalRecords>
<platformCore:pageSize>1000</platformCore:pageSize>
<platformCore:totalPages>3</platformCore:totalPages>
<platformCore:pageIndex>1</platformCore:pageIndex>
<platformCore:postingTransactionSummaryList>
<platformCore:postingTransactionSummary>
<platformCore:period internalId="1"/>
<platformCore:account internalId="985"/>
<platformCore:subsidiary internalId="1"/>
<platformCore:amount>100</platformCore:amount>
</platformCore:postingTransactionSummary>
<platformCore:postingTransactionSummary>
<platformCore:period internalId="1"/>
<platformCore:account internalId="985"/>
<platformCore:location internalId="3"/>
<platformCore:subsidiary internalId="2"/>
<platformCore:amount>100</platformCore:amount>
</platformCore:postingTransactionSummary>
.... MORE RECORDS HERE
</platformCore:postingTransactionSummaryList>
</platformCore:getPostingTransactionSummaryResult>
</getPostingTransactionSummaryResponse>
</soapenv:Body>
</soapenv:Envelope>
Request for second page:
To be clear the only changes between the first and second request are nonce, timestamp, signature and pageIndex.
<x:Envelope xmlns:x="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn2="urn:messages_2015_2.platform.webservices.netsuite.com" xmlns:urn="urn:core_2015_2.platform.webservices.netsuite.com">
<x:Header>
<urn:tokenPassport>
<urn1:account>1234567</urn1:account>
<urn1:consumerKey>12345</urn1:consumerKey>
<urn1:token>1234</urn1:token>
<urn1:nonce>64687129</urn1:nonce>
<urn1:timestamp>1478114087</urn1:timestamp>
<urn1:signature algorithm="HMAC-SHA256">HWQ/O4MSz5L6/TRu0GhtxU37yXIHdhSqvznbG92Salg=</urn1:signature>
</urn:tokenPassport>
</x:Header>
<x:Body>
<urn2:getPostingTransactionSummary>
<urn2:fields>
<urn:period>true</urn:period>
<urn:account>true</urn:account>
<urn:location>true</urn:location>
<urn:subsidiary>true</urn:subsidiary>
</urn2:fields>
<urn2:filters>
<urn:period>
<urn:recordRef internalId="1"/>
<urn:recordRef internalId="2"/>
<urn:recordRef internalId="3"/>
</urn:period>
<urn:account>
<urn:recordRef internalId="985" type="account"></urn:recordRef>
</urn:account>
</urn2:filters>
<urn2:pageIndex>2</urn2:pageIndex>
</urn2:getPostingTransactionSummary>
</x:Body>
</x:Envelope>
Response for second page:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header>
<platformMsgs:documentInfo xmlns:platformMsgs="urn:messages_2015_2.platform.webservices.netsuite.com">
<platformMsgs:nsId>WEBSERVICES_1234</platformMsgs:nsId>
</platformMsgs:documentInfo>
</soapenv:Header>
<soapenv:Body>
<getPostingTransactionSummaryResponse xmlns="urn:messages_2015_2.platform.webservices.netsuite.com">
<platformCore:getPostingTransactionSummaryResult xmlns:platformCore="urn:core_2015_2.platform.webservices.netsuite.com">
<platformCore:status isSuccess="false">
<platformCore:statusDetail type="ERROR">
<platformCore:code>USER_ERROR</platformCore:code>
<platformCore:message>For pages > 1, search parameters must match the original search exactly</platformCore:message>
</platformCore:statusDetail>
</platformCore:status>
</platformCore:getPostingTransactionSummaryResult>
</getPostingTransactionSummaryResponse>
</soapenv:Body>
</soapenv:Envelope>
The error leads me to suggest that the first request is not cached on netsuite's side correctly. I have also tried various permutations of fields in the token passport for the second request to no avail.
Any assistance appreciated.
This issue was resolved by:
1) Removing the token passport node all subsequent requests (pageIndex > 1)
<x:Envelope xmlns:x="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn2="urn:messages_2015_2.platform.webservices.netsuite.com" xmlns:urn="urn:core_2015_2.platform.webservices.netsuite.com">
<x:Header>
</x:Header>
<x:Body>
<urn2:getPostingTransactionSummary>
<urn2:fields>
<urn:period>true</urn:period>
<urn:account>true</urn:account>
<urn:location>true</urn:location>
<urn:subsidiary>true</urn:subsidiary>
</urn2:fields>
<urn2:filters>
<urn:period>
<urn:recordRef internalId="1"/>
<urn:recordRef internalId="2"/>
<urn:recordRef internalId="3"/>
</urn:period>
<urn:account>
<urn:recordRef internalId="985" type="account"></urn:recordRef>
</urn:account>
</urn2:filters>
<urn2:pageIndex>2</urn2:pageIndex>
</urn2:getPostingTransactionSummary>
</x:Body>
</x:Envelope>
2) Persisting and sending jsessionid and other cookies returned in headers (Set-Cookie) on first response in each subsequent request
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,