ladon-dev-team team mailing list archive
-
ladon-dev-team team
-
Mailing list archive
-
Message #00103
Re: [Question #219424]: soap reponse in wsdl does not match description
Question #219424 on ladon changed:
https://answers.launchpad.net/ladon/+question/219424
GaryS posted a new comment:
I have developed a new interface which solves the problem - this seems
to work with java-ws clients and .net clients I have tested it with. I
called it soapws:
# -*- coding: utf-8 -*-
from ladon.interfaces.base import BaseInterface,ServiceDescriptor,BaseRequestHandler,BaseResponseHandler,BaseFaultHandler
from ladon.interfaces import expose
from ladon.compat import PORTABLE_STRING,type_to_xsd,pytype_support,BytesIO
from xml.sax.handler import ContentHandler,feature_namespaces
from xml.sax import make_parser
from xml.sax.xmlreader import InputSource
import sys,re,traceback
import logging
LOG = logging.getLogger(__name__)
rx_nil_attr = re.compile(PORTABLE_STRING('^\w*[:]{0,1}nil$'),re.I)
class SOAPWSServiceDescriptor(ServiceDescriptor):
xsd_type_map = type_to_xsd
_content_type = 'text/xml'
def generate(self,servicename,servicenumber,typemanager,methodlist,service_url,encoding):
"""
Generate WSDL file for SOAPWSInterface
"""
type_dict = typemanager.type_dict
type_order = typemanager.type_order
def map_type(typ):
if typ in SOAPWSServiceDescriptor.xsd_type_map:
return SOAPWSServiceDescriptor.xsd_type_map[typ]
else:
return typ.__name__
def map_type2(typ):
if typ in SOAPWSServiceDescriptor.xsd_type_map:
tmpval = SOAPWSServiceDescriptor.xsd_type_map[typ]
if tmpval == 'decimal':
tmpval = 'float'
return tmpval
else:
return typ.__name__
import xml.dom.minidom as md
doc = md.Document()
# SERVICE DEFINITION
# Create the definitions element for the service
definitions = doc.createElement('definitions')
definitions.setAttribute('xmlns:wsu','http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd')
definitions.setAttribute('xmlns:wsp','http://www.w3.org/ns/ws-policy')
definitions.setAttribute('xmlns:wsp1_2','http://schemas.xmlsoap.org/ws/2004/09/policy')
definitions.setAttribute('xmlns:wsam','http://www.w3.org/2007/05/addressing/metadata')
definitions.setAttribute('xmlns:soap','http://schemas.xmlsoap.org/wsdl/soap/')
definitions.setAttribute('name', servicename)
definitions.setAttribute('targetNamespace','urn:%s' % servicename)
definitions.setAttribute('xmlns:tns','urn:%s' % servicename)
definitions.setAttribute('xmlns','http://schemas.xmlsoap.org/wsdl/')
definitions.setAttribute('xmlns:xsi','http://www.w3.org/2001/XMLSchema-instance')
definitions.setAttribute('xmlns:xsd','http://www.w3.org/2001/XMLSchema')
definitions.setAttribute('xmlns:ns%d' % servicenumber,'urn:%s' % servicename)
doc.appendChild(definitions)
# TYPES
# The types element
types = doc.createElement('types')
definitions.appendChild(types)
# Service schema for types required by the target namespace we defined in the definition element
schema = doc.createElement('xsd:schema')
schema.setAttribute('targetNamespace','urn:%s' % servicename)
schema.setAttribute('xmlns:xs','http://www.w3.org/2001/XMLSchema')
schema.setAttribute('xmlns:ns%d' % servicenumber,'urn:%s' % servicename)
schema.setAttribute('xmlns:xsi','http://www.w3.org/2001/XMLSchema-instance')
schema.setAttribute('xmlns:xsd','http://www.w3.org/2001/XMLSchema')
types.appendChild(schema)
# Define types, the type_order variable holds all that need to be defined and in the
# correct order.
for m in methodlist:
basictype = doc.createElement('xs:element')
basictype.setAttribute('name',m.name())
basictype.setAttribute('type','tns:%s' % m.name())
schema.appendChild(basictype)
basictypeR = doc.createElement('xs:element')
basictypeR.setAttribute('name',"%sResponse" % m.name())
basictypeR.setAttribute('type','tns:%sResponse' % m.name())
schema.appendChild(basictypeR)
for m in methodlist:
complextype = doc.createElement('xs:complexType')
complextype.setAttribute('name',m.name())
schema.appendChild(complextype)
sequence = doc.createElement('xs:sequence')
complextype.appendChild(sequence)
for arg in m.args():
v = arg['type']
element = doc.createElement('xs:element')
element.setAttribute('name',arg['name'].replace('_','-'))
element.setAttribute('minOccurs','0')
if isinstance(v, list):
inner = v[0]
if inner in type_dict:
element.setAttribute('type','tns:%s' % inner.__name__)
else:
element.setAttribute('type','xs:%s' % map_type2(inner))
element.setAttribute('maxOccurs','unbounded')
if not(isinstance(v, list)):
if v in type_dict:
element.setAttribute('type','tns:%s' % v.__name__)
else:
element.setAttribute('type','xs:%s' % map_type2(v))
sequence.appendChild(element)
complextype = doc.createElement('xs:complexType')
complextype.setAttribute('name',"%sResponse" % m.name())
schema.appendChild(complextype)
sequence = doc.createElement('xs:sequence')
complextype.appendChild(sequence)
v = m._rtype
element = doc.createElement('xs:element')
element.setAttribute('name','return')
element.setAttribute('minOccurs','0')
if isinstance(v, list):
inner = v[0]
if inner in type_dict:
element.setAttribute('type','tns:%s' % inner.__name__)
element.setAttribute('maxOccurs','unbounded')
else:
element.setAttribute('type','xs:%s' % map_type2(inner))
element.setAttribute('maxOccurs','unbounded')
element.setAttribute('minOccurs','0')
if not(isinstance(v, list)):
if v in type_dict:
element.setAttribute('type','tns:%s' % v.__name__)
else:
element.setAttribute('type','xs:%s' % map_type2(v))
sequence.appendChild(element)
#special types
for typ in type_order:
if not(isinstance(typ, list)):
complextype = doc.createElement('xs:complexType')
complextype.setAttribute('name',typ['name'])
schema.appendChild(complextype)
sequence = doc.createElement('xs:sequence')
complextype.appendChild(sequence)
for k,v,props in typ['attributes']:
element = doc.createElement('xs:element')
element.setAttribute('name',k.replace('_','-'))
element.setAttribute('maxOccurs','1')
element.setAttribute('minOccurs','1')
if props.get('nullable')==True:
element.setAttribute('minOccurs','0')
element.setAttribute('nillable','true')
if isinstance(v, list):
inner = v[0]
#element.setAttribute('type','tns:%s' % map_type2(inner))
if inner in type_dict:
element.setAttribute('type','tns:%s' % inner.__name__)
else:
element.setAttribute('type','xs:%s' % map_type2(inner))
#inner.__name__)
element.setAttribute('minOccurs','0')
element.setAttribute('maxOccurs','unbounded')
element.setAttribute('nillable','true')
#element.setAttribute('type','tns:%s' % type(v[0]))
if not(isinstance(v, list)):
if v in type_dict:
element.setAttribute('type','tns:%s' % v.__name__)
else:
element.setAttribute('type','xs:%s' % map_type2(v))
sequence.appendChild(element)
#messages
for m in methodlist:
message = doc.createElement('message')
message.setAttribute('name',m.name())
definitions.appendChild(message)
part = doc.createElement('part')
part.setAttribute('name','parameters')
part.setAttribute('element',"tns:%s" % m.name())
message.appendChild(part)
message = doc.createElement('message')
message.setAttribute('name',"%sResponse" % m.name())
definitions.appendChild(message)
part2 = doc.createElement('part')
part2.setAttribute('name','parameters')
part2.setAttribute('element',"tns:%sResponse" % m.name())
message.appendChild(part2)
porttype = doc.createElement('portType')
porttype.setAttribute('name','%sPortType' % servicename)
definitions.appendChild(porttype)
#operations
for m in methodlist:
operation = doc.createElement('operation')
operation.setAttribute('name',m.name())
porttype.appendChild(operation)
if m.__doc__:
documentation = doc.createElement('documentation')
documentation.appendChild(doc.createTextNode(m.__doc__))
operation.appendChild(documentation)
input_tag = doc.createElement('input')
input_tag.setAttribute('wsam:Action',"%s/%s" % (service_url,m.name()))
input_tag.setAttribute('message','tns:%s' % m.name())
operation.appendChild(input_tag)
output_tag = doc.createElement('output')
output_tag.setAttribute('wsam:Action',"%s/%sResponse" % (service_url,m.name()))
output_tag.setAttribute('message','tns:%sResponse' % m.name())
operation.appendChild(output_tag)
#binding
binding = doc.createElement('binding')
binding.setAttribute('name',servicename)
binding.setAttribute('type',"tns:%sPortType" % servicename)
transport = doc.createElement('soap:binding')
transport.setAttribute('transport','http://schemas.xmlsoap.org/soap/http')
transport.setAttribute('style','document')
binding.appendChild(transport)
definitions.appendChild(binding)
for m in methodlist:
operation = doc.createElement('operation')
operation.setAttribute('name',m.name())
binding.appendChild(operation)
soapaction = doc.createElement('soap:operation')
soapaction.setAttribute('soapAction','')
operation.appendChild(soapaction)
m._multipart_response_required
input_tag = doc.createElement('input')
body_parent = input_tag
if m._multipart_request_required:
multipart_related = doc.createElement('mime:multipartRelated')
mime_body_part = doc.createElement('mime:part')
body_parent = mime_body_part
mime_content_part = doc.createElement('mime:part')
mime_content = doc.createElement('mime:content')
mime_content.setAttribute('type','*/*')
input_tag.appendChild(multipart_related)
multipart_related.appendChild(mime_body_part)
multipart_related.appendChild(mime_content_part)
mime_content_part.appendChild(mime_content)
input_soapbody = doc.createElement('soap:body')
input_soapbody.setAttribute('use','literal')
body_parent.appendChild(input_soapbody)
operation.appendChild(input_tag)
output_tag = doc.createElement('output')
body_parent = output_tag
if m._multipart_request_required:
multipart_related = doc.createElement('mime:multipartRelated')
mime_body_part = doc.createElement('mime:part')
body_parent = mime_body_part
mime_content_part = doc.createElement('mime:part')
mime_content = doc.createElement('content:part')
mime_content.setAttribute('type','*/*')
output_tag.appendChild(multipart_related)
multipart_related.appendChild(mime_body_part)
multipart_related.appendChild(mime_content_part)
mime_content_part.appendChild(mime_content)
output_soapbody = doc.createElement('soap:body')
output_soapbody.setAttribute('use','literal')
body_parent.appendChild(output_soapbody)
operation.appendChild(output_tag)
service = doc.createElement('service')
service.setAttribute('name',servicename)
documentation = doc.createElement('documentation')
documentation.appendChild(doc.createTextNode('Ladon generated service definition'))
service.appendChild(documentation)
port = doc.createElement('port')
port.setAttribute('name',servicename)
port.setAttribute('binding','tns:%s' % servicename)
service.appendChild(port)
address = doc.createElement('soap:address')
address.setAttribute('location',service_url)
port.appendChild(address)
definitions.appendChild(service)
if sys.version_info[0]>=3:
return doc.toxml()
return doc.toxml(encoding)
def u(instring):
if sys.version_info[0]==2:
return PORTABLE_STRING(instring,'utf-8')
else:
return PORTABLE_STRING(instring)
class ContainerSetRef(object):
def __init__(self,c,refval):
self.c = c
self.refval = refval
def set(self,val):
self.c[self.refval] = val
class SOAPWSRequestHandler(BaseRequestHandler):
def parse_request(self,soap_body,sinfo,encoding):
import xml.dom.minidom as md
self.sinfo = sinfo
doc = md.parseString(soap_body)
soap_envelope = doc.getElementsByTagNameNS('*','Envelope')[0]
soap_body = doc.getElementsByTagNameNS('*','Body')[0]
EN = soap_body.ELEMENT_NODE
soap_method = (node for node in soap_body.childNodes
if node.nodeType == EN).next()
soap_methodprefix = soap_method.prefix
try:
m = re.match("^ns(\d+)$",soap_methodprefix)
except:
m = None
servicenumber = None
if m: servicenumber = int(m.groups()[0])
self.soap_methodname = soap_method.localName
soap_args = {'methodname': self.soap_methodname,'servicenumber':servicenumber}
#for typ in self.sinfo.typemanager.type_order:
# if not(isinstance(typ, list)):
# self.appendToMyLog(typ)
TN = soap_method.TEXT_NODE
soap_args['args'] = self.getDictForNode(soap_method)
#self.appendToMyLog('soap_args is %s' % soap_args)
return soap_args
def getDictForNode(self, node):
localDict={}
for n in node.childNodes:
if n.nodeType == n.ELEMENT_NODE and not n.hasAttribute('xsi:nil'):
if self.isTagList(n.nodeName):
lon = self.getListItems(n,node)
tList = []
for nn in lon:
tList.append(self.getDictForNode(nn))
node.removeChild(nn)
localDict[n.nodeName]= tList
else:
localDict[n.nodeName]=self.getDictForNode(n)
elif n.nodeType==n.TEXT_NODE and not n.hasChildNodes() and node.childNodes.length==1:
return n.nodeValue
elif n.nodeType==n.CDATA_SECTION_NODE:
return n.nodeValue
return localDict
def isTagList(self, tagnm):
for typ in self.sinfo.typemanager.type_order:
#self.appendToMyLog(typ)
a=1
try:
a=1
if not isinstance(typ, list):
for k,v,props in typ['attributes']:
if tagnm == k and isinstance(v,list):
return True
except:
a=2
for m in self.sinfo.method_list():
#self.appendToMyLog(m)
#self.appendToMyLog(m.name())
for arg in m.args():
v = arg['type']
if self.soap_methodname == m.name() and arg['name'] == tagnm and isinstance(v,list):
#self.appendToMyLog("%s is list!!" % m.name())
return True
#if isinstance(m['rtype'], list):
# self.appendToMyLog(m['rtype'])
# return True
return False
def getListItems(self, node, parent):
tagList=[]
for n in parent.childNodes:
if n.nodeName==node.nodeName:
tagList.append(n)
return tagList
def appendToMyLog(self, s):
f1=open('/home/ye91009/logs/testfile', 'a')
print >> f1, s
f1.close()
class SOAPWSResponseHandler(BaseResponseHandler):
_content_type = 'text/xml'
_stringify_res_dict = True
def dictToList(self,dd):
#convert dictionary to list i fpossible, to ensure order in type_order is preserved to giev valid xml export format
retList=[]
for typ in self.sinfo.typemanager.type_order:
if not(isinstance(typ, list)):
ld={}
for k,v,props in typ['attributes']:
ld[k]=None
if set(ld)==set(dd):
for k,v,props in typ['attributes']:
retList.append(k)
self.appendToMyLog(retList)
return retList
for k,v in dd.items():
retList.append(k)
return retList
def value_to_soapxml(self,value,parent,doc,is_toplevel=False):
if isinstance(value, dict):
locList = self.dictToList(value)
self.appendToMyLog(value)
for aname in locList:
#for attr_name,attr_val in value.items():
#self.appendToMyLog(aname)
attr_name = aname
attr_val = value[aname]
xml_attr_name = attr_name.replace('_','-')
if isinstance(attr_val, (list,tuple)):
for item in attr_val:
attr_elem = doc.createElement(xml_attr_name)
if is_toplevel:
attr_elem = doc.createElement('return')
parent.appendChild(attr_elem)
self.value_to_soapxml(item,attr_elem,doc)
else:
attr_elem = doc.createElement(xml_attr_name)
if is_toplevel:
attr_elem = doc.createElement('return')
parent.appendChild(attr_elem)
self.value_to_soapxml(attr_val,attr_elem,doc)
else:
if is_toplevel:
value_parent = doc.createElement('return')
value_parent.setAttribute('dodo','http://schemas.xmlsoap.org/soap/encoding/')
parent.appendChild(value_parent)
else:
value_parent = parent
if isinstance(value, (list, tuple)):
if not len(value):
# Translate empty response arrays to SOAP Null (xsi:nil) value
value_parent.setAttribute('xsi:nil','true')
else:
for item in value:
item_element = doc.createElement(value_parent.nodeName)
self.value_to_soapxml(item,item_element,doc)
value_parent.appendChild(item_element)
else:
if value==None:
# Set xsi:nil to true if value is None
value_parent.setAttribute('xsi:nil','true')
else:
value_parent.appendChild(doc.createTextNode(value))
def build_response(self,res_dict,sinfo,encoding):
import xml.dom.minidom as md
self.sinfo=sinfo
doc = md.Document()
envelope = doc.createElement('SOAP-ENV:Envelope')
envelope.setAttribute('xmlns:SOAP-ENV','http://schemas.xmlsoap.org/soap/envelope/')
envelope.setAttribute('xmlns:SOAP-ENC','http://schemas.xmlsoap.org/soap/encoding/')
envelope.setAttribute('xmlns:xsd','http://www.w3.org/2001/XMLSchema')
envelope.setAttribute('xmlns:ns','urn:%s' % res_dict['servicename'])
envelope.setAttribute('xmlns:xsi','http://www.w3.org/2001/XMLSchema-instance')
doc.appendChild(envelope)
body_elem = doc.createElement('SOAP-ENV:Body')
body_elem.setAttribute('SOAP-ENV:encodingStyle','http://schemas.xmlsoap.org/soap/encoding/')
envelope.appendChild(body_elem)
method_elem = doc.createElement("tns:%sResponse" % res_dict['method'])
method_elem.setAttribute('xmlns:tns','urn:%s' % res_dict['servicename'])
#self.appendToMyLog(res_dict)
if 'result' in res_dict['result']:
self.value_to_soapxml(res_dict['result'],method_elem,doc,is_toplevel=True)
else:
self.value_to_soapxml({'result':res_dict['result']},method_elem,doc,is_toplevel=True)
body_elem.appendChild(method_elem)
return doc.toxml(encoding=encoding)
def appendToMyLog(self, s):
f1=open('/home/ye91009/logs/testfile', 'a')
print >> f1, s
f1.close()
class SOAPWSFaultHandler(BaseFaultHandler):
_content_type = 'text/xml'
_stringify_res_dict = True
soapfault_template = """<?xml version='1.0' encoding='UTF-8'?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode xsi:type="xsd:string"></faultcode>
<faultstring xsi:type="xsd:string"></faultstring>
<detail></detail>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>"""
def build_fault_response(self,service_exc,sinfo,methodname,encoding):
import xml.dom.minidom as md
if service_exc.detail:
detail = service_exc.detail
else:
detail = traceback.format_exc()
d = md.parseString(self.soapfault_template)
# Extract fault DOM elements
faultcode_elem = d.getElementsByTagName('faultcode')[0]
faultstring_elem = d.getElementsByTagName('faultstring')[0]
detail_elem = d.getElementsByTagName('detail')[0]
# Set the fault values
faultcode_elem.appendChild(d.createTextNode(service_exc.faultcode))
faultstring_elem.appendChild(d.createTextNode(service_exc.faultstring))
detail_elem.appendChild(d.createTextNode(detail))
# Return the SoapFault XML object
return d.toxml(encoding=encoding)
@expose
class SOAPWSInterface(BaseInterface):
def __init__(self,sinfo,**kw):
def_kw = {
'service_descriptor': SOAPWSServiceDescriptor,
'request_handler': SOAPWSRequestHandler,
'response_handler': SOAPWSResponseHandler,
'fault_handler': SOAPWSFaultHandler}
def_kw.update(kw)
BaseInterface.__init__(self,sinfo,**def_kw)
@staticmethod
def _interface_name():
return 'soapws'
@staticmethod
def _accept_basetype(typ):
return pytype_support.count(typ)>0
@staticmethod
def _accept_list():
return True
@staticmethod
def _accept_dict():
return False
--
You received this question notification because you are a member of
Ladon Developer, which is an answer contact for ladon.