I have overridden the SimpleHandler to pass a username and password using soap4r. the problem is that I am forced to give a QName, and this is causing the result to fail because it's not in the right format.
What soap4r is adding is something like this (the "ns1" things are dummy values):
<env:Header>
<n1:ns1 env:mustUnderstand="0"
xmlns:n1="ns1">
<n1:Username>someuser</n1:Username>
<n1:Password>topsecret</n1:Password>
</n1:ns1>
</env:Header>
What it needs to be is this:
<env:Header>
<n1:Username>someuser</n1:Username>
<n1:Password>topsecret</n1:Password>
</env:Header>
How can I NOT pass in a containing name?
Looks like you need to make some changes to soap4r: http://dev.ctor.org/soap4r/browser/branches/1_5/lib/soap/header/handler.rb
If the service isn't all that complicated you could try Handsoap
https://github.com/unwire/handsoap/wiki/authentication#WS-Security
Soap4R is quite picky about overriding headers. In your situation, you'll need to add two "flat" handlers (one for Username, and another for Password) instead of a single one with both.
Related
I am now trying another xpath property transfer in SoapUI, and am struggling again.
Rather than specifics I thought I would ask some general questions.
If the response I am wanting to extract from has a line like this:
<ns2:getApprovedPortChangeRequestsResponse xmlns:ns2="http://transferobjects.abc.abc.org">
Oddly, when I click the ns: button in SoapUI it generates the following:
declare namespace soap='http://schemas.xmlsoap.org/soap/envelope/';
declare namespace ns1='http://transferobjects.abc.abc.org';
declare namespace ns2='http://abc.abc.org/api/serviceorder';
But the message response is pretty clear with the ns2 line above
Then I think that I can safely assume that I should do this:
declare namespace ns2='getApprovedPortChangeRequestsResponse';
Now, when I do the part describing what I want to capture, I am using the ns2 tag, following by the section names that follow as I go in to the message, in this case two layers:
//ns2:return/approvedPortChangeRequests/#version
The value I want is the value of the field called version, don't know if I want the # symbol this time, it's a numeric value, but I get null regardless of whether the # is there or not. I have thoroughly checked the response message and pretty certain I have it right. There are a couple of other sections in the response above the field, but they are at the same level as , from what I can see.
I have tried including the getApprovedPortChangeRequestsResponse as a parent layer in the last line, with no effect.
Only when I use getApprovedPortChangeRequestsResponse in the ns2 declaration can I get anything other than Null, and then only verbose errors like this:
[net.sf.saxon.trans.XPathException: XPath syntax error at char 7 on line 2 in {\n//ns2:/return}:
QName cannot end with colon: {ns2:}]
Basically, I am utterly ignorant and my googlefu hasn't shown me any resource where I can build any sort of understanding of what I am doing, so any suggestion on that front would be appreciated. I just need a couple of examples of doing this in SoapUI, and I should be sweet.
Thanks in advance.
EDIT- Full Response here:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns2:getApprovedPortChangeRequestsResponse xmlns:ns2="http://transferobjects.abc.abc.org">
<return>
<success>true</success>
<approvedPortChangeRequests>
<lspOverride>false</lspOverride>
<numbers>
<complete>false</complete>
<gainingCompanyId>11667</gainingCompanyId>
<losingCompanyId>11657</losingCompanyId>
<notRequired>false</notRequired>
<phoneNumber>
<phoneNumber>098453509</phoneNumber>
</phoneNumber>
</numbers>
<DateTimeStart>2018-03-07T08:00:00+13:00</DateTimeStart>
<som>6001309</som>
<category>Simple</category>
<requestDateTime>2018-03-07T12:05:25+13:00</requestDateTime>
<requesterResellerId>21</requesterResellerId>
<responderResellerId>1</responderResellerId>
<responseDue>
<actualDays>0</actualDays>
<actualHours>1</actualHours>
<actualMinutes>47</actualMinutes>
<businessDays>0</businessDays>
<businessHours>1</businessHours>
<businessMinutes>47</businessMinutes>
<negative>false</negative>
</responseDue>
<status>
<status>Awaiting APC Approval</status>
</status>
<version>1</version>
</approvedPortChangeRequests>
</return>
</ns2:getApprovedPortChangeRequestsResponse>
</soap:Body>
</soap:Envelope>
In your payload, version is an element rather than an attribute, so you don't need the #.
soapUI tries to help you out by declaring namespaces, but you can go simpler. If version is all you need and there's only one, you can navigate directly to it regardless of namespaces by using //version. The // notation will select a node no matter where it is in the payload.
I am having trouble using the MockOperation Editor in Soap UI.
I have got this request:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<methodName xmlns="http://tempuri.org/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<dataAreaId>error</dataAreaId>
<pInvoiceList>
<dataAreaId>NOTTHESAME</dataAreaId>
...
</pInvoiceList>
</methodName>
</s:Body>
</s:Envelope>
I tried almost every XPATH expression but I always get "Missing match in request"
What to fill in the Xpath box?
I tried:
//dataAreaId/text()
//dataAreaId/
//dataAreaId
/dataAreaId/text()
/dataAreaId
/methodName/dataAreaId/text()
/methodName/dataAreaId/
/methodName/dataAreaId
I finally managed to get it based on the answer from user1740631
Seems I it had to do with namespaces afterall.
The correct syntax:
declare namespace tem='http://tempuri.org/';
//tem:methodName/tem:dataAreaId[1]
If you don't care about namespaces you can use the following syntax:
XPath1.0
//*[local-name() = 'methodName']/*[local-name() = 'dataAreaId'][1]
XPath2.0
//*:methodName/*:dataAreaId[1]
Write like this
For First One
//methodName[1]/dataAreaId[1]
For Second one
//methodName[1]/pInvoiceList[1]/dataAreaId[1]
*If you have multiple node with same name in Xml then you should use numbers to locate that particular node.
There is a good hint: When defining an Assertion for a Testcase (or maybe also in the Mock-Window) there is a button "Declare" above the XPath-Expression-Field. It doesn't really look like a button, until you point with the mouse-pointer at it, so I didn't realize it at first.
Just click on the button an SoapUI (actually I use 5.2.1) will add the declare-statements for you, that you can use.
I found that feature coincidentally, as it is not really visible. Maybe this can help also...
I need to add this attribute (xmlns:wsa="http://www.w3.org/2005/08/addressing") to the soap header, like this:
<env:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
</env:Header>
How do I do this, using Savon?
I was actually able to make another workaround to the problem in my case, since my endpoint would accept this:
<env:Header>
<wsa:Action xmlns:wsa="http://www.w3.org/2005/08/addressing">value</wsa:Action>
</env:Header>
Investigating the original question, here's the response from the Savon creator:
"hey magne,
looking at the code which creates the header and body tags, it doesn't seem
possible to add any attributes/namespaces without monkey-patching right now:
https://github.com/rubiii/savon/blob/v0.9.7/lib/savon/soap/xml.rb#L151
if you still need this feature, please open a ticket and i'll see what i can do:
https://github.com/rubiii/savon/issues
i'm currently very involved in taking a new approach to improve the library,
so i'm not sure when i'll be able to solve your problem. but ... i hacked together
a small monkey-patch that should help until this feature is implemented:
https://gist.github.com/1698636
cheers,
daniel"
You can add your own namespace to the request like this:
resp = client.request :soap_action do
soap.namespace['xmlns:wsa'] = 'http://www.w3.org/2005/08/addressing'
end
foo = client.request do
soap.header['xmlns:wsa'] = 'http://www.w3.org/2005/08/addressing'
end
I've created a driver from wsdl
When I invoke my request, I would like the header to contain an element, i.e, I want to see something like the following:
REPLACE_WITH_ACTUAL
blah blah blah
However, looking around, everyone talks about subclassing SOAP::Header::SimpleHandler and then injecting an instance into the driver.headerhandler
However, if I do that, then I end up with a nested header, i.e,
REPLACE_WITH_ACTUAL
So there must be a way to just add an element to the existing headerhandler so I can do something like
driver.headerhandler.AddElement("session", "123")
but I can't find any way to do that. I also tried things like
driver.headerhandler["session"]="123" and other such tricks, but I can't find any way to make this work.
Looking at driver.headerhandler.methods, I cannot see any obvious mechanism.
Would really appreciate a pointer to how to to this.
Well, a colleague in my team solved the problem above after looking at some of the typical examples that I had previously found including the one at http://dev.ctor.org/soap4r/browser/trunk/sample/soapheader/authheader/client2.rb
Turns out that the trivial (sigh) solution is to replace
def on_simple_outbound
if #sessionid
{ "sessionid" => #sessionid }
end
end
with
def on_simple_outbound
if #sessionid
#sessionid
end
end
Now, if you just name the header "session" (in the QName creation), you get the unnested header, exactly what I wanted.
I thought I'd paste my solution to my own problem on the assumption that others might be running into the same problem.
I'm dealing with a SOAP webservice call from a server that is expecting to receive method calls with the paramaters in the format of:
<urn:offeringId> 354 </urn:offeringId>
But SOAP::RPC::Driver is generating messages in the form of:
<offeringId xsi:type = "xsd:int">354</offeringId>
The server keeps erroring when it gets these messages (especially since it's expecting offeringId to be a custom type internal to itself, not an int).
Is there anyway to configure the driver to format things the way the server is expecting it. Is the server even doing SOAP? I'm having trouble finding reference to that style of formating for SOAP (I know it DOES work though, because SOAPUI works just fine with that type of message).
-Jenny
Edit: I've got at least part of it solved. the RPC::Driver (obviously) uses the RPC standard, whereas apparently the server I'm trying to talk to is doing "document". Now, when I look at RPC::Driver's API, I'm seeing a method named "add_document_method". That SOUNDS to me like it might be what I want, but I can't figure out what paramaters to give it. The examples I've seen around the net don't make much sense to me, things like:
def GetNamePair(response)
response.account.each do |x|
class << x
attr :configuration, true
end
x.configuration = Hash[*x.a.map do |y|
[y.__xmlattr[XSD::QName.new(nil, 'n')], String.new(y)]
end.flatten]
end
end
mNS = 'urn:zimbraAdmin'
drv.add_document_method('GetAllAdminAccountsRequest', mNS, [XSD::QName.new(mNS, 'GetAllAdminAccountsRequest')],
[XSD::QName.new(mNS, 'GetAllAdminAccountsResponse')] )
puts YAML.dump(GetNamePair(drv.GetAllAdminAccountsRequest([]))
All I really know is that I have a method that takes in certain parameters.... I really don't get why, if this method does what I think it does, it has to be more complicated. Isn't this just a matter of taking the exact same data and formating it differently? I'm so confused....
Okay, what I ended up doing was using SOAP:RPC:Drivers add_document_method, which requires me to give it the wsdl, namespace, etc, and then give it the attributes later as a single input hash thingy (and gives me the output in a similar format). It worked, it just wasn't as clean as add_rpc_method (which is waht add_method defaults to)
-Jenny