def to_xml(self, *data): if len(self.params): if len(data) != len(self.params): raise Exception("Parameter number mismatch expected [%s] " "got [%s] for response %s" % (len(self.params), len(data), self.name)) nsmap = NamespaceLookup(self.ns) element = create_xml_element(self.name, nsmap, self.ns) for i in range(0, len(self.params)): name, serializer = self.params[i] d = data[i] e = serializer.to_xml(d, name, nsmap) if type(e) in (list, tuple): elist = e for e in elist: element.append(e) elif e == None: pass else: element.append(e) ElementTree.cleanup_namespaces(element) return element
def to_xml(self, *data): if len(self.params): if len(self.params) >= 2: data = data[0] if len(data) != len(self.params): raise Exception( "Parameter number mismatch expected [%s] " "got [%s] for response %s" % (len(self.params), len(data), self.name) ) nsmap = NamespaceLookup(self.ns) element = create_xml_element(self.name, nsmap, self.ns) for i in range(0, len(self.params)): name, serializer = self.params[i] d = data[i] e = serializer.to_xml(d, name, nsmap) if type(e) in (list, tuple): elist = e for e in elist: element.append(e) elif e == None: pass else: element.append(e) ElementTree.cleanup_namespaces(element) return element
def index_html(self, REQUEST, RESPONSE): """Handle an incoming SOAP request or a non-SOAP WSDL query.""" if REQUEST.get('SOAPXML', None) == None: # Not a SOAP Request, return WSDL return self.service_description(REQUEST, RESPONSE) try: # Deserialize the Body of the SOAP Request payload, header = from_soap(REQUEST.SOAPXML) methodname = payload.tag.split('}')[-1] # Look-up the method within our class and obtain its SOAP descriptor # specifying its arguments signature and return type. try: func = getattr(self, methodname) descriptor = func(_soap_descriptor=True) except AttributeError: faultstring = 'No Such SOAP Method: %s' % repr(methodname) faultcode = 'Server' fault = make_soap_fault(faultstring, faultcode, detail=None) resp = ElementTree.tostring(fault, encoding=string_encoding) RESPONSE.setStatus('NotImplemented', reason=faultstring) RESPONSE.setHeader('Content-Type', 'text/xml') return resp except TypeError, e: if "unexpected keyword argument '_soap_descriptor'" not in str(e): raise faultstring = 'Method %s Exists But Not SOAP-Callable' % repr(methodname) faultcode = 'Server' fault = make_soap_fault(faultstring, faultcode, detail=None) resp = ElementTree.tostring(fault, encoding=string_encoding) RESPONSE.setStatus('BadRequest', reason=faultstring) RESPONSE.setHeader('Content-Type', 'text/xml') return resp # Run the supplied arguments through the arguments signature and catch # any argument type or count mismatches. try: params = descriptor.inMessage.from_xml(payload) except Exception, e: faultstring = "%s: Argument Parse Error" % str(e) faultcode = '%sFault' % methodname fault = make_soap_fault(faultstring, faultcode, detail=None) resp = ElementTree.tostring(fault, encoding=string_encoding) RESPONSE.setStatus('BadRequest', reason=faultstring) RESPONSE.setHeader('Content-Type', 'text/xml') return resp
def join_attachment(id, envelope, payload, prefix=True): ''' Helper function for swa_to_soap. Places the data from an attachment back into a SOAP message, replacing its xop:Include element or href. @param id content-id or content-location of attachment @param prefix Set this to true if id is content-id or false if it is content-location. It prefixes a "cid:" to the href value. @param envelope soap envelope string to be operated on @param payload attachment data @return tuple of length 2 with the new message and the number of replacements made ''' # grab the XML element of the message in the SOAP body soapmsg = StringIO(envelope) soaptree = ElementTree.parse(soapmsg) soapns = soaptree.getroot().tag.split('}')[0].strip('{') soapbody = soaptree.getroot().find("{%s}Body" % soapns) message = None for child in list(soapbody): if child.tag != "%sFault" % (soapns, ): message = child break numreplaces = 0 idprefix = '' if prefix == True: idprefix = "cid:" id = "%s%s" % ( idprefix, id, ) # Make replacement. for param in message: # Look for Include subelement. for sub in param: if sub.tag.split('}')[-1] == 'Include' and \ sub.attrib.get('href') == id: param.remove(sub) param.text = payload numreplaces += 1 if numreplaces < 1 and param.attrib.get('href') == id: del (param.attrib['href']) param.text = payload numreplaces += 1 soapmsg.close() soapmsg = StringIO() soaptree.write(soapmsg) joinedmsg = soapmsg.getvalue() soapmsg.close() return (joinedmsg, numreplaces)
def __call__(self): faultstring = self.request['faultexc'].__class__.__name__ self.request.response.setStatus('InternalServerError', reason=faultstring) faultcode = 'Server' fault = make_soap_fault(faultstring, faultcode, detail=None) self.request.response.setHeader('Content-Type', 'text/xml') return ElementTree.tostring(fault, encoding=string_encoding)
def join_attachment(id, envelope, payload, prefix=True): ''' Helper function for swa_to_soap. Places the data from an attachment back into a SOAP message, replacing its xop:Include element or href. @param id content-id or content-location of attachment @param prefix Set this to true if id is content-id or false if it is content-location. It prefixes a "cid:" to the href value. @param envelope soap envelope string to be operated on @param payload attachment data @return tuple of length 2 with the new message and the number of replacements made ''' # grab the XML element of the message in the SOAP body soapmsg = StringIO(envelope) soaptree = ElementTree.parse(soapmsg) soapns = soaptree.getroot().tag.split('}')[0].strip('{') soapbody = soaptree.getroot().find("{%s}Body" % soapns) message = None for child in list(soapbody): if child.tag != "%sFault" % (soapns, ): message = child break numreplaces = 0 idprefix = '' if prefix == True: idprefix = "cid:" id = "%s%s" % (idprefix, id, ) # Make replacement. for param in message: # Look for Include subelement. for sub in param: if sub.tag.split('}')[-1] == 'Include' and \ sub.attrib.get('href') == id: param.remove(sub) param.text = payload numreplaces += 1 if numreplaces < 1 and param.attrib.get('href') == id: del(param.attrib['href']) param.text = payload numreplaces += 1 soapmsg.close() soapmsg = StringIO() soaptree.write(soapmsg) joinedmsg = soapmsg.getvalue() soapmsg.close() return (joinedmsg, numreplaces)
def test_join_attachment(self): id="http://tempuri.org/1/634133419330914808" payload="ANJNSLJNDYBC SFDJNIREMX:CMKSAJN" envelope = '<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><DownloadPartFileResponse xmlns="http://tempuri.org/"><DownloadPartFileResult xmlns:a="http://schemas.datacontract.org/2004/07/KlanApi.Common" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><a:ErrorCode>0</a:ErrorCode><a:ErrorMessage i:nil="true"/><a:Data><xop:Include href="cid:http%3A%2F%2Ftempuri.org%2F1%2F634133419330914808" xmlns:xop="http://www.w3.org/2004/08/xop/include"/></a:Data></DownloadPartFileResult></DownloadPartFileResponse></s:Body></s:Envelope>' (joinedmsg, numreplaces) = join_attachment(id, envelope, payload) soapmsg = StringIO(joinedmsg) soaptree = ElementTree.parse(soapmsg) soapns = "http://schemas.xmlsoap.org/soap/envelope/" r = DownloadPartFileResult.from_xml( soaptree.getroot().find("{%s}Body" % soapns).getchildren()[0].getchildren()[0] ) self.assertEquals(payload, r.Data)
def test_join_attachment(self): id = "http://tempuri.org/1/634133419330914808" payload = "ANJNSLJNDYBC SFDJNIREMX:CMKSAJN" envelope = '<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><DownloadPartFileResponse xmlns="http://tempuri.org/"><DownloadPartFileResult xmlns:a="http://schemas.datacontract.org/2004/07/KlanApi.Common" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><a:ErrorCode>0</a:ErrorCode><a:ErrorMessage i:nil="true"/><a:Data><xop:Include href="cid:http%3A%2F%2Ftempuri.org%2F1%2F634133419330914808" xmlns:xop="http://www.w3.org/2004/08/xop/include"/></a:Data></DownloadPartFileResult></DownloadPartFileResponse></s:Body></s:Envelope>' (joinedmsg, numreplaces) = join_attachment(id, envelope, payload) soapmsg = StringIO(joinedmsg) soaptree = ElementTree.parse(soapmsg) soapns = "http://schemas.xmlsoap.org/soap/envelope/" r = DownloadPartFileResult.from_xml(soaptree.getroot().find( "{%s}Body" % soapns).getchildren()[0].getchildren()[0]) self.assertEquals(payload, r.Data)
def from_soap(xml_string): ''' Parses the xml string into the header and payload ''' root, xmlids = ElementTree.XMLID(xml_string) if xmlids: resolve_hrefs(root, xmlids) body = None header = None # find the body and header elements for e in root.getchildren(): name = e.tag.split('}')[-1].lower() if name == 'body': body = e elif name == 'header': header = e payload = None if len(body.getchildren()): payload = body.getchildren()[0] return payload, header
def wsdl(self, url): ''' This method generates and caches the wsdl for this object based on the soap methods designated by the soapmethod or soapdocument descriptors @param url the url that this service can be found at. This must be passed in by the caller because this object has no notion of the server environment in which it runs. @returns the string of the wsdl ''' if not self.__wsdl__ == None: # return the cached __wsdl__ return self.__wsdl__ url = url.replace('.wsdl', '') # otherwise build it serviceName = self.__class__.__name__.split('.')[-1] tns = self.__tns__ methods = self.methods() hasCallbacks = self._hasCallbacks() nsmap = NamespaceLookup(tns, True) if hasCallbacks: nsmap.set('wsa', 'http://schemas.xmlsoap.org/ws/2003/03/addressing') root = create_xml_element( "definitions", nsmap, 'http://schemas.xmlsoap.org/wsdl/') root.set('targetNamespace', tns) root.set('name', serviceName) types = create_xml_subelement(root, "types") self._add_schema(types, methods, nsmap) self._add_messages_for_methods(root, methods, nsmap) # add necessary async headers # WS-Addressing -> RelatesTo ReplyTo MessageID # callback porttype if hasCallbacks: wsaSchemaNode = create_xml_subelement(types, "schema") wsaSchemaNode.set("targetNamespace", tns+'Callback') wsaSchemaNode.set("xmlns", "http://www.w3.org/2001/XMLSchema") wsaSchemaNode.set("elementFormDefault", "qualified") importNode = create_xml_subelement(wsaSchemaNode, "import") importNode.set("namespace", "http://schemas.xmlsoap.org/ws/2003/03/addressing") importNode.set("schemaLocation", "http://schemas.xmlsoap.org/ws/2003/03/addressing/") reltMessage = create_xml_subelement(root, 'message') reltMessage.set('name', 'RelatesToHeader') reltPart = create_xml_subelement(reltMessage, 'part') reltPart.set('name', 'RelatesTo') reltPart.set('element', 'wsa:RelatesTo') replyMessage = create_xml_subelement(root, 'message') replyMessage.set('name', 'ReplyToHeader') replyPart = create_xml_subelement(replyMessage, 'part') replyPart.set('name', 'ReplyTo') replyPart.set('element', 'wsa:ReplyTo') idHeader = create_xml_subelement(root, 'message') idHeader.set('name', 'MessageIDHeader') idPart = create_xml_subelement(idHeader, 'part') idPart.set('name', 'MessageID') idPart.set('element', 'wsa:MessageID') # make portTypes callbackPortType = create_xml_subelement(root, 'portType') callbackPortType.set('name', '%sCallback' % serviceName) cbServiceName = '%sCallback' % serviceName cbService = create_xml_subelement(root, 'service') cbService.set('name', cbServiceName) cbWsdlPort = create_xml_subelement(cbService, 'port') cbWsdlPort.set('name', cbServiceName) cbWsdlPort.set('binding', 'tns:%s' % cbServiceName) cbAddr = create_xml_subelement(cbWsdlPort, nsmap.get('soap') + 'address') cbAddr.set('location', url) serviceName = self.__class__.__name__.split('.')[-1] portType = create_xml_subelement(root, 'portType') portType.set('name', serviceName) for method in methods: if method.isCallback: operation = create_xml_subelement(callbackPortType, 'operation') else: operation = create_xml_subelement(portType, 'operation') operation.set('name', method.name) params = [] for name, param in method.inMessage.params: params.append(name) documentation = create_xml_subelement(operation, 'documentation') documentation.text = method.doc operation.set('parameterOrder', method.inMessage.typ) opInput = create_xml_subelement(operation, 'input') opInput.set('name', method.inMessage.typ) opInput.set('message', 'tns:%s' % method.inMessage.typ) if (len(method.outMessage.params) > 0 and not method.isCallback and not method.isAsync): opOutput = create_xml_subelement(operation, 'output') opOutput.set('name', method.outMessage.typ) opOutput.set('message', 'tns:%s' % method.outMessage.typ) # make partner link plink = create_xml_subelement(root, nsmap.get('plnk') + 'partnerLinkType') plink.set('name', serviceName) role = create_xml_subelement(plink, nsmap.get('plnk') + 'role') role.set('name', serviceName) plinkPortType = create_xml_subelement(role, nsmap.get('plnk') + 'portType') plinkPortType.set('name', 'tns:%s' % serviceName) if hasCallbacks: role = create_xml_subelement(plink, nsmap.get('plnk') + 'role') role.set('name', '%sCallback' % serviceName) plinkPortType = create_xml_subelement(role, nsmap.get('plnk') + 'portType') plinkPortType.set('name', 'tns:%sCallback' % serviceName) self._add_bindings_for_methods(root, serviceName, methods, nsmap) service = create_xml_subelement(root, 'service') service.set('name', serviceName) wsdlPort = create_xml_subelement(service, 'port') wsdlPort.set('name', serviceName) wsdlPort.set('binding', 'tns:%s' % serviceName) addr = create_xml_subelement(wsdlPort, nsmap.get('soap') + 'address') addr.set('location', url) wsdl = ElementTree.tostring(root) wsdl = "<?xml version='1.0' encoding='utf-8' ?>%s" % (wsdl) #cache the wsdl for next time self.__wsdl__ = wsdl return self.__wsdl__
def apply_mtom(headers, envelope, params, paramvals): ''' Apply MTOM to a SOAP envelope, separating attachments into a MIME multipart message. References: XOP http://www.w3.org/TR/xop10/ MTOM http://www.w3.org/TR/soap12-mtom/ http://www.w3.org/Submission/soap11mtom10/ @param headers Headers dictionary of the SOAP message that would originally be sent. @param envelope SOAP envelope string that would have originally been sent. @param params params attribute from the Message object used for the SOAP @param paramvals values of the params, passed to Message.to_xml @return tuple of length 2 with dictionary of headers and string of body that can be sent with HTTPConnection ''' # grab the XML element of the message in the SOAP body soapmsg = StringIO(envelope) soaptree = ElementTree.parse(soapmsg) soapns = soaptree.getroot().tag.split('}')[0].strip('{') soapbody = soaptree.getroot().find("{%s}Body" % soapns) message = None for child in list(soapbody): if child.tag != "%sFault" % (soapns, ): message = child break # Get additional parameters from original Content-Type ctarray = [] for n, v in headers.items(): if n.lower() == 'content-type': ctarray = v.split(';') break roottype = ctarray[0].strip() rootparams = {} for ctparam in ctarray[1:]: n, v = ctparam.strip().split('=') rootparams[n] = v.strip("\"'") # Set up initial MIME parts mtompkg = MIMEMultipart('related', boundary='?//<><>soaplib_MIME_boundary<>') rootpkg = None try: rootpkg = MIMEApplication(envelope, 'xop+xml', encode_7or8bit) except NameError: rootpkg = MIMENonMultipart("application", "xop+xml") rootpkg.set_payload(envelope) encode_7or8bit(rootpkg) # Set up multipart headers. del(mtompkg['mime-version']) mtompkg.set_param('start-info', roottype) mtompkg.set_param('start', '<soaplibEnvelope>') if 'SOAPAction' in headers: mtompkg.add_header('SOAPAction', headers.get('SOAPAction')) # Set up root SOAP part headers. del(rootpkg['mime-version']) rootpkg.add_header('Content-ID', '<soaplibEnvelope>') for n, v in rootparams.items(): rootpkg.set_param(n, v) rootpkg.set_param('type', roottype) mtompkg.attach(rootpkg) # Extract attachments from SOAP envelope. for i in range(len(params)): name, typ = params[i] if typ == Attachment: id = "soaplibAttachment_%s" % (len(mtompkg.get_payload()), ) param = message[i] param.text = "" incl = create_xml_subelement(param, "{http://www.w3.org/2004/08/xop/include}Include") incl.attrib["href"] = "cid:%s" % id if paramvals[i].fileName and not paramvals[i].data: paramvals[i].load_from_file() data = paramvals[i].data attachment = None try: attachment = MIMEApplication(data, _encoder=encode_7or8bit) except NameError: attachment = MIMENonMultipart("application", "octet-stream") attachment.set_payload(data) encode_7or8bit(attachment) del(attachment['mime-version']) attachment.add_header('Content-ID', '<%s>' % (id, )) mtompkg.attach(attachment) # Update SOAP envelope. soapmsg.close() soapmsg = StringIO() soaptree.write(soapmsg) rootpkg.set_payload(soapmsg.getvalue()) soapmsg.close() # extract body string from MIMEMultipart message bound = '--%s' % (mtompkg.get_boundary(), ) marray = mtompkg.as_string().split(bound) mtombody = bound mtombody += bound.join(marray[1:]) # set Content-Length mtompkg.add_header("Content-Length", str(len(mtombody))) # extract dictionary of headers from MIMEMultipart message mtomheaders = {} for name, value in mtompkg.items(): mtomheaders[name] = value if len(mtompkg.get_payload()) <= 1: return (headers, envelope) return (mtomheaders, mtombody)
def apply_mtom(headers, envelope, params, paramvals): ''' Apply MTOM to a SOAP envelope, separating attachments into a MIME multipart message. References: XOP http://www.w3.org/TR/xop10/ MTOM http://www.w3.org/TR/soap12-mtom/ http://www.w3.org/Submission/soap11mtom10/ @param headers Headers dictionary of the SOAP message that would originally be sent. @param envelope SOAP envelope string that would have originally been sent. @param params params attribute from the Message object used for the SOAP @param paramvals values of the params, passed to Message.to_xml @return tuple of length 2 with dictionary of headers and string of body that can be sent with HTTPConnection ''' # grab the XML element of the message in the SOAP body soapmsg = StringIO(envelope) soaptree = ElementTree.parse(soapmsg) soapns = soaptree.getroot().tag.split('}')[0].strip('{') soapbody = soaptree.getroot().find("{%s}Body" % soapns) message = None for child in list(soapbody): if child.tag != "%sFault" % (soapns, ): message = child break # Get additional parameters from original Content-Type ctarray = [] for n, v in headers.items(): if n.lower() == 'content-type': ctarray = v.split(';') break roottype = ctarray[0].strip() rootparams = {} for ctparam in ctarray[1:]: n, v = ctparam.strip().split('=') rootparams[n] = v.strip("\"'") # Set up initial MIME parts mtompkg = MIMEMultipart('related', boundary='?//<><>soaplib_MIME_boundary<>') rootpkg = None try: rootpkg = MIMEApplication(envelope, 'xop+xml', encode_7or8bit) except NameError: rootpkg = MIMENonMultipart("application", "xop+xml") rootpkg.set_payload(envelope) encode_7or8bit(rootpkg) # Set up multipart headers. del (mtompkg['mime-version']) mtompkg.set_param('start-info', roottype) mtompkg.set_param('start', '<soaplibEnvelope>') if 'SOAPAction' in headers: mtompkg.add_header('SOAPAction', headers.get('SOAPAction')) # Set up root SOAP part headers. del (rootpkg['mime-version']) rootpkg.add_header('Content-ID', '<soaplibEnvelope>') for n, v in rootparams.items(): rootpkg.set_param(n, v) rootpkg.set_param('type', roottype) mtompkg.attach(rootpkg) # Extract attachments from SOAP envelope. for i in range(len(params)): name, typ = params[i] if typ == Attachment: id = "soaplibAttachment_%s" % (len(mtompkg.get_payload()), ) param = message[i] param.text = "" incl = create_xml_subelement( param, "{http://www.w3.org/2004/08/xop/include}Include") incl.attrib["href"] = "cid:%s" % id if paramvals[i].fileName and not paramvals[i].data: paramvals[i].load_from_file() data = paramvals[i].data attachment = None try: attachment = MIMEApplication(data, _encoder=encode_7or8bit) except NameError: attachment = MIMENonMultipart("application", "octet-stream") attachment.set_payload(data) encode_7or8bit(attachment) del (attachment['mime-version']) attachment.add_header('Content-ID', '<%s>' % (id, )) mtompkg.attach(attachment) # Update SOAP envelope. soapmsg.close() soapmsg = StringIO() soaptree.write(soapmsg) rootpkg.set_payload(soapmsg.getvalue()) soapmsg.close() # extract body string from MIMEMultipart message bound = '--%s' % (mtompkg.get_boundary(), ) marray = mtompkg.as_string().split(bound) mtombody = bound mtombody += bound.join(marray[1:]) # set Content-Length mtompkg.add_header("Content-Length", str(len(mtombody))) # extract dictionary of headers from MIMEMultipart message mtomheaders = {} for name, value in mtompkg.items(): mtomheaders[name] = value if len(mtompkg.get_payload()) <= 1: return (headers, envelope) return (mtomheaders, mtombody)
def wsdl(self, url): ''' This method generates and caches the wsdl for this object based on the soap methods designated by the soapmethod or soapdocument descriptors @param url the url that this service can be found at. This must be passed in by the caller because this object has no notion of the server environment in which it runs. @returns the string of the wsdl ''' if not self.__wsdl__ == None: # return the cached __wsdl__ return self.__wsdl__ url = url.replace('.wsdl', '') # otherwise build it serviceName = self.__class__.__name__.split('.')[-1] tns = self.__tns__ methods = self.methods() hasCallbacks = self._hasCallbacks() nsmap = NamespaceLookup(tns, True) if hasCallbacks: nsmap.set('wsa', 'http://schemas.xmlsoap.org/ws/2003/03/addressing') root = create_xml_element( "definitions", nsmap, 'http://schemas.xmlsoap.org/wsdl/') root.set('targetNamespace', tns) root.set('name', serviceName) types = create_xml_subelement(root, "types") self._add_schema(types, methods, nsmap) self._add_messages_for_methods(root, methods, nsmap) # add necessary async headers # WS-Addressing -> RelatesTo ReplyTo MessageID # callback porttype if hasCallbacks: wsaSchemaNode = create_xml_subelement(types, "schema") wsaSchemaNode.set("targetNamespace", tns+'Callback') wsaSchemaNode.set("xmlns", "http://www.w3.org/2001/XMLSchema") wsaSchemaNode.set("elementFormDefault", "qualified") importNode = create_xml_subelement(wsaSchemaNode, "import") importNode.set("namespace", "http://schemas.xmlsoap.org/ws/2003/03/addressing") importNode.set("schemaLocation", "http://schemas.xmlsoap.org/ws/2003/03/addressing/") reltMessage = create_xml_subelement(root, 'message') reltMessage.set('name', 'RelatesToHeader') reltPart = create_xml_subelement(reltMessage, 'part') reltPart.set('name', 'RelatesTo') reltPart.set('element', 'wsa:RelatesTo') replyMessage = create_xml_subelement(root, 'message') replyMessage.set('name', 'ReplyToHeader') replyPart = create_xml_subelement(replyMessage, 'part') replyPart.set('name', 'ReplyTo') replyPart.set('element', 'wsa:ReplyTo') idHeader = create_xml_subelement(root, 'message') idHeader.set('name', 'MessageIDHeader') idPart = create_xml_subelement(idHeader, 'part') idPart.set('name', 'MessageID') idPart.set('element', 'wsa:MessageID') # make portTypes callbackPortType = create_xml_subelement(root, 'portType') callbackPortType.set('name', '%sCallback' % serviceName) cbServiceName = '%sCallback' % serviceName cbService = create_xml_subelement(root, 'service') cbService.set('name', cbServiceName) cbWsdlPort = create_xml_subelement(cbService, 'port') cbWsdlPort.set('name', cbServiceName) cbWsdlPort.set('binding', 'tns:%s' % cbServiceName) cbAddr = create_xml_subelement(cbWsdlPort, nsmap.get('soap') + 'address') cbAddr.set('location', url) serviceName = self.__class__.__name__.split('.')[-1] portType = create_xml_subelement(root, 'portType') portType.set('name', serviceName) for method in methods: if method.isCallback: operation = create_xml_subelement(callbackPortType, 'operation') else: operation = create_xml_subelement(portType, 'operation') operation.set('name', method.name) params = [] for name, param in method.inMessage.params: params.append(name) documentation = create_xml_subelement(operation, 'documentation') documentation.text = method.doc operation.set('parameterOrder', method.inMessage.typ) opInput = create_xml_subelement(operation, 'input') opInput.set('name', method.inMessage.typ) opInput.set('message', 'tns:%s' % method.inMessage.typ) if (len(method.outMessage.params) > 0 and not method.isCallback and not method.isAsync): opOutput = create_xml_subelement(operation, 'output') opOutput.set('name', method.outMessage.typ) opOutput.set('message', 'tns:%s' % method.outMessage.typ) self._add_bindings_for_methods(root, serviceName, methods, nsmap) service = create_xml_subelement(root, 'service') service.set('name', serviceName) wsdlPort = create_xml_subelement(service, 'port') wsdlPort.set('name', serviceName) wsdlPort.set('binding', 'tns:%s' % serviceName) addr = create_xml_subelement(wsdlPort, nsmap.get('soap') + 'address') addr.set('location', url) wsdl = ElementTree.tostring(root) wsdl = "<?xml version='1.0' encoding='utf-8' ?>%s" % (wsdl) #cache the wsdl for next time self.__wsdl__ = wsdl return self.__wsdl__
def wsdl(self, url): """ This method generates and caches the wsdl for this object based on the soap methods designated by the soapmethod or soapdocument descriptors @param url the url that this service can be found at. This must be passed in by the caller because this object has no notion of the server environment in which it runs. @returns the string of the wsdl """ if not self.__wsdl__ == None: # return the cached __wsdl__ return self.__wsdl__ url = url.replace(".wsdl", "") # otherwise build it serviceName = self.__class__.__name__.split(".")[-1] tns = self.__tns__ methods = self.methods() hasCallbacks = self._hasCallbacks() nsmap = NamespaceLookup(tns, True) if hasCallbacks: nsmap.set("wsa", "http://schemas.xmlsoap.org/ws/2003/03/addressing") root = create_xml_element("definitions", nsmap, "http://schemas.xmlsoap.org/wsdl/") root.set("targetNamespace", tns) root.set("name", serviceName) types = create_xml_subelement(root, "types") self._add_schema(types, methods, nsmap) self._add_messages_for_methods(root, methods, nsmap) # add necessary async headers # WS-Addressing -> RelatesTo ReplyTo MessageID # callback porttype if hasCallbacks: wsaSchemaNode = create_xml_subelement(types, "schema") wsaSchemaNode.set("targetNamespace", tns + "Callback") wsaSchemaNode.set("xmlns", "http://www.w3.org/2001/XMLSchema") importNode = create_xml_subelement(wsaSchemaNode, "import") importNode.set("namespace", "http://schemas.xmlsoap.org/ws/2003/03/addressing") importNode.set("schemaLocation", "http://schemas.xmlsoap.org/ws/2003/03/addressing/") reltMessage = create_xml_subelement(root, "message") reltMessage.set("name", "RelatesToHeader") reltPart = create_xml_subelement(reltMessage, "part") reltPart.set("name", "RelatesTo") reltPart.set("element", "wsa:RelatesTo") replyMessage = create_xml_subelement(root, "message") replyMessage.set("name", "ReplyToHeader") replyPart = create_xml_subelement(replyMessage, "part") replyPart.set("name", "ReplyTo") replyPart.set("element", "wsa:ReplyTo") idHeader = create_xml_subelement(root, "message") idHeader.set("name", "MessageIDHeader") idPart = create_xml_subelement(idHeader, "part") idPart.set("name", "MessageID") idPart.set("element", "wsa:MessageID") # make portTypes callbackPortType = create_xml_subelement(root, "portType") callbackPortType.set("name", "%sCallback" % serviceName) cbServiceName = "%sCallback" % serviceName cbService = create_xml_subelement(root, "service") cbService.set("name", cbServiceName) cbWsdlPort = create_xml_subelement(cbService, "port") cbWsdlPort.set("name", cbServiceName) cbWsdlPort.set("binding", "tns:%s" % cbServiceName) cbAddr = create_xml_subelement(cbWsdlPort, nsmap.get("soap") + "address") cbAddr.set("location", url) serviceName = self.__class__.__name__.split(".")[-1] portType = create_xml_subelement(root, "portType") portType.set("name", serviceName) for method in methods: if method.isCallback: operation = create_xml_subelement(callbackPortType, "operation") else: operation = create_xml_subelement(portType, "operation") operation.set("name", method.name) params = [] for name, param in method.inMessage.params: params.append(name) documentation = create_xml_subelement(operation, "documentation") documentation.text = method.doc operation.set("parameterOrder", method.inMessage.typ) opInput = create_xml_subelement(operation, "input") opInput.set("name", method.inMessage.typ) opInput.set("message", "tns:%s" % method.inMessage.typ) if method.outMessage.params != None and not method.isCallback and not method.isAsync: opOutput = create_xml_subelement(operation, "output") opOutput.set("name", method.outMessage.typ) opOutput.set("message", "tns:%s" % method.outMessage.typ) # make partner link plink = create_xml_subelement(root, nsmap.get("plnk") + "partnerLinkType") plink.set("name", serviceName) role = create_xml_subelement(plink, nsmap.get("plnk") + "role") role.set("name", serviceName) plinkPortType = create_xml_subelement(role, nsmap.get("plnk") + "portType") plinkPortType.set("name", "tns:%s" % serviceName) if hasCallbacks: role = create_xml_subelement(plink, nsmap.get("plnk") + "role") role.set("name", "%sCallback" % serviceName) plinkPortType = create_xml_subelement(role, nsmap.get("plnk") + "portType") plinkPortType.set("name", "tns:%sCallback" % serviceName) self._add_bindings_for_methods(root, serviceName, methods, nsmap) service = create_xml_subelement(root, "service") service.set("name", serviceName) wsdlPort = create_xml_subelement(service, "port") wsdlPort.set("name", serviceName) wsdlPort.set("binding", "tns:%s" % serviceName) addr = create_xml_subelement(wsdlPort, nsmap.get("soap") + "address") addr.set("location", url) wsdl = ElementTree.tostring(root) wsdl = "<?xml version='1.0' encoding='utf-8' ?>%s" % (wsdl) # cache the wsdl for next time self.__wsdl__ = wsdl return self.__wsdl__
envelope = make_soap_envelope(results, tns=self.__tns__) resp = ElementTree.tostring(envelope, encoding=string_encoding) RESPONSE.setHeader('Content-Type', 'text/xml') return resp except Exception, e: faultstring = str(e) if methodname: faultcode = '%sFault' % methodname else: faultcode = 'Server' fault = make_soap_fault(faultstring, faultcode, detail=None) resp = ElementTree.tostring(fault, encoding=string_encoding) RESPONSE.setStatus('InternalServerError', reason=faultstring) RESPONSE.setHeader('Content-Type', 'text/xml') return resp class ISOAPException(IException): pass class SOAPException(Exception): """Base exception class for all derived exceptions for SOAP""" implements(ISOAPException)