SoapUI - Automatically add custom SOAP headers to outgoing request - soap

So what I want to do is to automatically add SOAP header to every request that is generated in SoapUI as I've got hundreds of them and doing this manually is annoying.
Lets say that this is my example request generated from the WSDL which looks like that:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:pol="http://something">
<soapenv:Header>
</soapenv:Header>
<soapenv:Body>
<pol:GetSomething>
<tag1>3504</tag1>
<tag2>ALL</tag2>
</pol:GetSomething>
</soapenv:Body>
</soapenv:Envelope>
and when I make the request I want SoapUI to modify it to look like that:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:pol="http://something">
<soapenv:Header>
<token xmlns="ns1">${TOKEN}</token>
<user xmlns="ns2">user</user>
<system xmlns="ns3">system</system>
</soapenv:Header>
<soapenv:Body>
<pol:GetSomething>
<tag1>3504</tag1>
<tag2>ALL</tag2>
</pol:GetSomething>
</soapenv:Body>
</soapenv:Envelope>
Is it possible in SoapUI?

In your testCase you can add a first step of type Groovy Script, in this script you can manipulate each request to add necessary elements on <soap:Header>, I give you an example that works for me:
def groovyUtils = new com.eviware.soapui.support.GroovyUtils( context );
def tcase = testRunner.testCase ;
// get total number of testSteps
def countTestSteps = tcase.getTestStepList().size();
// start with 1 to avoid groovy script testStep
for(i=1;i<countTestSteps;i++){
// get testStep
def testStep = tcase.getTestStepAt(i);
// get request
def request = testStep.getProperty('Request').getValue();
// get XML
def xmlReq = groovyUtils.getXmlHolder(request);
// get SOAPHEADER
def soapHeader = xmlReq.getDomNode("declare namespace soap='http://schemas.xmlsoap.org/soap/envelope/'; //soap:Header")
// document to create new elements
def requestDoc = soapHeader.getOwnerDocument()
// create new element
def newElem = requestDoc.createElementNS(null, "element");
// insert in header
soapHeader.insertBefore(newElem, soapHeader.getFirstChild());
// now put your new request in testStep
log.info xmlReq.getXml();
testStep.setPropertyValue('Request', xmlReq.getXml());
}
This sample code only add one new element on the <soap:header>, but you can modify it to add attributes, text content and more nodes. You can also take a look at:
dynamically create elements in a SoapUI request | SiKing

Related

Pass variable as attribute to XML body

I am creating a Gatling load test in Scala and I wish to create a HTTP POST request with attribute.
val getServiceCall = http("get service call")
.post("/MyService")
.headers(header)
.body(ElFileBody("templates/GetServiceCallRqTemplate.xml"))
.check(status.is(200))
And this is a snippet from my xml:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body>
<pus:GetServiceCallRq>
<com:header>
<com:type>pus:GetServiceCallRq</com:type>
<com:timestamp>timestamp</com:timestamp>
<com:source>source</com:source>
<com:destination>destination</com:destination>
<com:serverNode>serverNode</com:serverNode>
</com:header>
<pus:body>
<pus:reference>${reference}</pus:reference>
....
I would like to pass the ${reference} attribute from a variable creating within my code - and not from a csv file.
How is it possible to pass it as a request param?
using a feeder (csv file) is not the only way to get a value into a session parameter.
you could also set the value manually via a session action
exec(session => session.set("reference", "ref0001"))
or via a check on an earlier request (whatever is in the 'ref' key in the response gets saved to the ${reference} variable
exec(
http("setupReq")
.get("someURL")
.check(
jsonPath("$.ref").saveAs("reference")
)
)
or even by creating your own custom feeder - for example, if you just wanted to generate random references
//somewhere at the top of your scala class
private val refs = Iterator.continually(
Map("reference" -> (Random.alphanumeric take 10 mkString).toUpperCase())
)
//then later in your scenario...
.feed(refs) //will put a new random string into ${reference}
//then make your "get service call" request

node-soap adding namespace to envelope

I am trying to consume this soap service: http://testws.truckstop.com:8080/v13/Posting/LoadPosting.svc?singleWsdl with node-soap, but the client is mangling the namespaces and I have been unable to find a working solution.
I believe the answer is to either add a namespace to the soap envelope, or overwrite the soap envelope.
Using Soap UI, the request should look like:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:v11="http://webservices.truckstop.com/v11"
xmlns:web="http://schemas.datacontract.org/2004/07/WebServices">
<soapenv:Header/>
<soapenv:Body>
<v11:GetLoads>
<v11:listRequest>
<web:IntegrationId>integrationId</web:IntegrationId>
<web:Password>password</web:Password>
<web:UserName>username</web:UserName>
</v11:listRequest>
</v11:GetLoads>
</soapenv:Body>
</soapenv:Envelope>
However, when I do:
client = soap.createClient(url);
let query = {
listRequest: {
Password: password,
UserName: username,
IntegrationId: integrationId
}
};
let results = client.GetLoads(query);
The client generates this xml:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:tns="http://webservices.truckstop.com/v11"
xmlns:q1="http://schemas.datacontract.org/2004/07/WebServices.Posting"
xmlns:q2="http://schemas.datacontract.org/2004/07/WebServices.Objects"
xmlns:q3="http://schemas.datacontract.org/2004/07/WebServices.Posting"
xmlns:q4="http://schemas.datacontract.org/2004/07/WebServices.Objects"
xmlns:q5="http://schemas.datacontract.org/2004/07/WebServices.Posting"
xmlns:q6="http://schemas.datacontract.org/2004/07/WebServices.Objects"
xmlns:q7="http://schemas.datacontract.org/2004/07/WebServices.Posting"
xmlns:q8="http://schemas.datacontract.org/2004/07/WebServices.Objects"
xmlns:q9="http://schemas.datacontract.org/2004/07/WebServices.Posting"
xmlns:q10="http://schemas.datacontract.org/2004/07/WebServices.Objects"
xmlns:q11="http://schemas.datacontract.org/2004/07/WebServices.Posting"
xmlns:q12="http://schemas.datacontract.org/2004/07/WebServices.Objects">
<soap:Body>
<GetLoads xmlns="http://webservices.truckstop.com/v11">
<listRequest>
<ns1:IntegrationId>integrationId</ns1:IntegrationId>
<ns1:Password>password</ns1:Password>
<ns1:UserName>usernam</ns1:UserName>
</listRequest>
</GetLoads>
</soap:Body>
</soap:Envelope>
This fails because IntegrationId, Password and UserName need http://schemas.datacontract.org/2004/07/WebServices, but the namespace isn't referenced in the envelope.
I've tried updating the client to add the namespace as suggested here:
client.wsdl.definitions.xmlns.ns1 = "http://schemas.datacontract.org/2004/07/WebServices";
client.wsdl.xmlnInEnvelope = client.wsdl._xmlnsMap();
I can see the namespace in client.wsdl.xmlnInEnvelope, but it doesn't seem to change the actual generated xml.
Is there another step required to refresh the client to use the updated envelope?
I also tried overriding the root element as shown here:
var wsdlOptions = {
//namespaceArrayElements: "xmlns:ns1=http://schemas.datacontract.org/2004/07/WebServices"
"overrideRootElement": {
"namespace": "xmlns:tns",
"xmlnsAttributes": [{
"name": "xmlns:tns",
"value": "http://webservices.truckstop.com/v11"
}, {
"name": "xmlns:ns1",
"value": "http://schemas.datacontract.org/2004/07/WebServices"
}]
}
};
this.loadPostClient = soap.createClient(this.tsConfig.loadPostUrl, wsdlOptions);
This changes the root body element:
<soap:Body>
<xmlns:tns:GetLoads
xmlns:tns="http://webservices.truckstop.com/v11"
xmlns:ns1="http://schemas.datacontract.org/2004/07/WebServices">
<listRequest>
<ns1:IntegrationId>integrationId</ns1:IntegrationId>
<ns1:Password>password</ns1:Password>
<ns1:UserName>username</ns1:UserName>
</listRequest>
</xmlns:tns:GetLoads>
</soap:Body>
But the remote server doesn't understand.
Thank you for reading!
This answer was correct all along
It wasn't working for me due to autocomplete and similar fields
client.wsdl.xmlnInEnvelope = client.wsdl._xmlnsMap();
Should have been:
client.wsdl.xmlnsInEnvelope = client.wsdl._xmlnsMap();
I left out an s and was setting xmlnInEnvelope instead of xmlnsInEvelope
It's been a few years, but I ran into a similar need of adding custom attributes to the soap envelope and wanted to give an alternative.
As of this writing, that _xmlnsMap() is a private method on the WSDL class so you can use it at your own risk. I always take private methods as subject to change from the developer without any notice to the library consumers so I wanted to find another way and turns out its possible.
TL;DR - Create your own WSDL class instance and pass it to your own Client class instance.
Use the open_wsdl method to bring in your WSDL
Use the callback to build your own custom attributes in a concatenated string.
Assign the attributes to the public xmlnsInEnvelope property.
return the updated WSDL instance (I used a promise).
const fetchWSDL = new Promise<WSDL>((resolve, reject) => {
// method that returns a WSDL instance from a url/file path
open_wsdl(this.wsdl, (err: any, wsdl?: WSDL) => {
// Build custom attributes
if (wsdl && wsdl.definitions.xmlns) {
const xmlns: { [key: string]: string } = {
[your namespaces]: 'values',
};
// turn your custom attributes map into a single concatenated string
let str = '';
for (const alias in xmlns) {
const ns = xmlns[alias];
str += ' xmlns:' + alias + '="' + ns + '"';
}
// Leverage public attribute on WSDL instance to apply our custom attributes
wsdl.xmlnsInEnvelope = str;
resolve(wsdl);
}
reject(err);
});
});
Use the updated WSDL instance to create your own client.
NOTE: the createClient method is just a convenience wrapper for creating a WSDL instance and returning a new Client instance.
const ModifiedWSDL = await fetchWSDL;
// Create client with our modified WSDL instance
this.client = new Client(ModifiedWSDL)
// adjust your Client instance as needed
A bit more code that the OP, but hopefully more in line with node-soap types and safer to use if you plan to upgrade.

Soapui read soap response to dialog for user entry

SoapUI. I have a pretty old version of SoapUI (version 2.0.2)
We know in Soapui, we can present Alert, and input Dialogs..
Which work great..
I have the following SOAP Response and would like to read the question to present as a dialog.
<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<ns1:Response xmlns:ns1="http://some.name.space.com">
<ns1:payload>
<ns1:Question>
<ns1:questionText>How much is that doggie in the window?</ns1:questionText>
</ns1:Question>
</ns1:payload>
</ns1:Response>
</soapenv:Body>
</soapenv:Envelope>
I would like to read or present the "questionText" to the tester..
def ui = com.eviware.soapui.support.UISupport;
ui.showInfoMessage("Hello World");
But, instead read the question (the questions do change) and to be something like:
def ui = com.eviware.soapui.support.UISupport;
ui.showInfoMessage("testStep.xmlResponse.questionText");
And then have where the tester can input their answer..
def ui = com.eviware.soapui.support.UISupport;
ui.prompt("Input Answer","Answer");
Thanks for the information at - Can I pause for console input in a SOAPUI groovy script?
Here is what I have been able to find so far on this (which does not work at this point):
// pulled information from:
// http://www.soapui.org/Developers-Corner/extending-soapui.html
import com.eviware.soapui.support.XmlHolder;
def ui = com.eviware.soapui.support.UISupport;
def holder = new XmlHolder ( messageExchangeContentAsXml)
holder.namespaces["ns1"] = "http://some.name.space.com"
def node = holder.getDomName ("//ns1:Response[1]/ns1:payload[1]/ns1:Question[1]/ns1:questionText[1]")
ui.showInfoMessage(node.questionText);
The error that I am getting at this point is:
org.codehaus.groovy.runtime.metaclass.MissingPropertyExceptionNoStack: No such property: messageExchangeContentAsXml for class: Script12
And I think I should be able to "pass" that on to the next request fairly easily.
Thanks.
I have figured this out..
http://www.soapui.org/Developers-Corner/extending-soapui.html
http://www.soapui.org/Scripting-Properties/tips-a-tricks.html
http://forum.soapui.org/viewtopic.php?t=2597
Reading from properties, we need the following:
def ui = com.eviware.soapui.support.UISupport;
groovyUtils = new com.eviware.soapui.support.GroovyUtils( context );
def propId = context.getProperty( "userName", "userName" );
ui.showInfoMessage( propId );
Reading from the Response in SOAPUI, we need to do the following:
def ui = com.eviware.soapui.support.UISupport;
groovyUtils = new com.eviware.soapui.support.GroovyUtils( context );
holder = groovyUtils.getXmlHolder("TestRequest#Response");
holder.namespaces["ns1"] = "http://some.namespace";
def responseId = holder.getNodeValue("//ns1:userName");
ui.showInfoMessage( responseId );

How do I extract the ACK from a webservice response in Mirth?

I have a destination which sends an HL7 v2 message to a WCF webservice. I've managed to get the message correctly to the webservice (after fixing various encoding problems) and I can confirm that the message is reaching the WCF endpoint correctly. However, after much hunting around in the forums and documentation, I am unable to correctly parse the ACK we receive back to indicate when an error has occurred.
The response coming back from the webservice looks like:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header/>
<s:Body>
<ProcessMessageResponse xmlns="http://www.bluewire-technologies.com/webservices">
<ProcessMessageResult>
MSH|^~\&|Epro|RGR|||||ACK||D||||||GBR|ASCII|
MSA|AE||Empty message|
ERR|^^^100|
</ProcessMessageResult>
</ProcessMessageResponse>
</s:Body>
</s:Envelope>
The response contains an ACK in the ProcessMessageResult element. How to I extract this ACK from the response and pass it as the output of the destination? Will Mirth automatically parse the ACK and determine that there was an error?
I had thought that I needed some kind of transformer on the destination (called 'SOAP') and use something along the lines of:
var xml = responseMap.get('SOAP').getMessage();
and then extract the ProcessMessageResponse element but responseMap.get('SOAP') returns null so that seems the wrong approach.
I've now solved part of this thanks to an answer on the Mirth forums.
To summarize, I use the following postprocessor to extract the ack and update the status:
var s = new Namespace('http://schemas.xmlsoap.org/soap/envelope/');
var bw = new Namespace('http://www.bluewire-technologies.com/webservices');
var response = new XML($r('SOAP').getMessage());
var ack = new XML(SerializerFactory.getHL7Serializer().toXML(response.s::Body.bw::ProcessMessageResponse.bw::ProcessMessageResult.toString()));
var ackCode = ack.MSA['MSA.1']['MSA.1.1'].toString();
if(ackCode == 'AE')
{
var errorMessage = ack.MSA['MSA.3']['MSA.3.1'].toString();
var messageController = com.mirth.connect.server.controllers.DefaultMessageObjectController.create();
var channelStatisticsController = com.mirth.connect.server.controllers.DefaultChannelStatisticsController.create();
messageObject.getContext().put("replace", "true"); // yuk - this is to make setError below work.
messageController.setError(messageObject, null, errorMessage, null, null);
channelStatisticsController.decrementSentCount(new java.lang.String(channelId));
}
Not pretty, but it works...

Groovy HTTPBuilder SOAP response not being parsed properly

I don't understand why XmlSlurper is apparently not working on the result.
import groovyx.net.http.*
import static groovyx.net.http.ContentType.*
import static groovyx.net.http.Method.*
def String WSDL_URL = ...
def http = new HTTPBuilder( WSDL_URL , ContentType.XML )
String soapEnvelope =
"""<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<GetTerritories xmlns="...">
<State>AZ</State>
<ZipCode>85203</ZipCode>
</GetTerritories>
</soap12:Body>
</soap12:Envelope>"""
http.request( POST, XML ) {
headers."Content-Type" = "application/soap+xml; charset=utf-8"
headers."Accept" = "application/soap+xml; charset=utf-8"
body = soapEnvelope
response.success = { resp, xml ->
println "XML was ${xml}"
println "Territories were ${xml.Territories}"
println "State were ${xml.Territories.State}"
println "City was ${xml.Territories.Territory.City}"
println "County was ${xml.Territories.Territory.County}"
}
response.failure = { resp, xml ->
xml
}
}
leads to
XML was <Territories><State>AZ</State><ZipCode>85203</ZipCode><Territory><City>Mesa</City><County>Maricopa</County>...</Territory></Territories>
Territories were
State were
City was
County was
UPDATE: Thanks to John Wagenleitner's insight, I did a little more digging.
When I add that assert, I see an issue:
assert "Territories" == xml.name()
| | |
| | Envelope
| <Territories><State>AZ</State><ZipCode>85203</ZipCode</Territories>
false
Changing the request parameters from POST, XML to POST, TEXT is revealing:
XML was <?xml version="1.0" encoding="utf-8"?>
<soap:Envelope
xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<GetTerritoriesResponse xmlns="...">
<GetTerritoriesResult><Territories><State>AZ</State><ZipCode>85203</ZipCode><Territory><City>Mesa</City><County>Maricopa</County>...</Territory></Territories>
</GetTerritoriesResult>
</GetTerritoriesResponse>
</soap:Body>
</soap:Envelope>
...
So it looks like the XmlSlurper, when the variable is being printed out, is throwing away the SOAP stuff and evaluating the innermost node () while not actually navigating to that node. Is this expected behavior?
I have been unable to find a a more complete and modern SOAP call and parse using httpBuilder, so I assumed XML would be the right content type. But it looks like I'll just have to accept TEXT and parse the body myself, which seems lame. Is there a better way of handling SOAP responses with httpBuilder?
I would recommend printing the raw text of the response:
println "XML was ${resp.data.text}"
Assuming that the printed XML line is what you expect (though odd since there is no Envelope or Body nodes), then you should be able to remove Territories from your references to xml. When parsed with XmlSlurper the root node is the GPathResult.
assert "Territories" == xml.name()
println "State were ${xml.State.text()}"
println "City were ${xml.Territory.City.text()}"
println "County were ${xml.Territory.County.text()}"
Also, just wanted to point out that the SOAP 1.2 media type is "application/soap+xml".
UPDATE:
So it looks like the XmlSlurper, when the variable is being printed
out, is throwing away the SOAP stuff and evaluating the innermost node
() while not actually navigating to that node. Is this expected
behavior?
Yes, the toString() method for a GPathResult just prints all text nodes and not the actual elements or attributes. With HTTPBuilder you can print out the raw response text by using:
println resp.data.text
I have been unable to find a a more complete and modern SOAP call and
parse using httpBuilder, so I assumed XML would be the right content
type. But it looks like I'll just have to accept TEXT and parse the
body myself, which seems lame. Is there a better way of handling SOAP
responses with httpBuilder?
The ContentType.XML is fine, the issue is with how the SOAP response that your web service returns is formed. The web service is sending back the Territories results as an encoded string in the GetTerritoriesResult element and not as part of the actual XML response that HTTPBuilder automatically parses for you (this is not a problem with the way HTTPBuilder is handling it). Because the data you really want is in that encoded string you will need to parse the text node of the GetTerritoriesResult yourself.
response.success = { resp, xml ->
println "XML was ${resp.data.text}"
def territories = new XmlSlurper().parseText(
xml.Body.GetTerritoriesResponse.GetTerritoriesResult.text()
)
println "State were ${territories.State}"
println "City was ${territories.Territory.City}"
println "County was ${territories.Territory.County}"
}