def add_to_schema(cls, schemaDict, nsmap): if not cls.get_datatype(nsmap) in schemaDict: for k, v in cls.soap_members.items(): v.add_to_schema(schemaDict, nsmap) schema_node = create_xml_element( nsmap.get("xs") + "complexType", nsmap) schema_node.set('name', cls.get_datatype()) sequence_node = create_xml_subelement(schema_node, nsmap.get('xs') + 'sequence') for k, v in cls.soap_members.items(): member_node = create_xml_subelement( sequence_node, nsmap.get('xs') + 'element') member_node.set('name', k) member_node.set('minOccurs', '0') member_node.set( 'type', "%s:%s" % (v.get_namespace_id(), v.get_datatype())) typeElement = create_xml_element( nsmap.get('xs') + 'element', nsmap) typeElement.set('name', cls.get_datatype()) typeElement.set( 'type', "%s:%s" % (cls.get_namespace_id(), cls.get_datatype())) schemaDict[cls.get_datatype(nsmap) + 'Complex'] = schema_node schemaDict[cls.get_datatype(nsmap)] = typeElement
def _add_messages_for_methods(self, root, methods, nsmap): ''' A private method for adding message elements to the wsdl @param the the root element of the wsdl @param the list of methods. ''' messages = [] #make messages for method in methods: methodName = method.name # making in part inMessage = create_xml_element('message', nsmap) inMessage.set('name', method.inMessage.typ) if len(method.inMessage.params) > 0: inPart = create_xml_subelement(inMessage, 'part') inPart.set('name', method.inMessage.name) inPart.set('element', 'tns:' + method.inMessage.typ) messages.append(inMessage) # making out part only if necessary if len(method.outMessage.params) > 0: outMessage = create_xml_element('message', nsmap) outMessage.set('name', method.outMessage.typ) outPart = create_xml_subelement(outMessage, 'part') outPart.set('name', method.outMessage.name) outPart.set('element', 'tns:' + method.outMessage.typ) messages.append(outMessage) for message in messages: root.append(message)
def _add_messages_for_methods(self, root, methods, nsmap): """ A private method for adding message elements to the wsdl @param the the root element of the wsdl @param the list of methods. """ messages = [] # make messages for method in methods: methodName = method.name # making in part inMessage = create_xml_element("message", nsmap) inMessage.set("name", method.inMessage.typ) if len(method.inMessage.params) > 0: inPart = create_xml_subelement(inMessage, "part") inPart.set("name", method.inMessage.name) inPart.set("element", "tns:" + method.inMessage.typ) messages.append(inMessage) # making out part outMessage = create_xml_element("message", nsmap) outMessage.set("name", method.outMessage.typ) if len(method.outMessage.params) > 0: outPart = create_xml_subelement(outMessage, "part") outPart.set("name", method.outMessage.name) outPart.set("element", "tns:" + method.outMessage.typ) messages.append(outMessage) for message in messages: root.append(message)
def make_soap_envelope(message, tns='', header_elements=None): ''' This method takes the results from a soap method call, and wraps them in the appropriate soap envelope with any specified headers @param the message of the soap envelope, either an element or a list of elements @param any header elements to be included in the soap response @returns the envelope element ''' nsmap = NamespaceLookup(tns) envelope = create_xml_element( nsmap.get('SOAP-ENV') + 'Envelope', nsmap, tns) if header_elements: headerElement = create_xml_subelement(envelope, nsmap.get('SOAP-ENV') + 'Header') for h in header_elements: headerElement.append(h) body = create_xml_subelement(envelope, nsmap.get('SOAP-ENV') + 'Body') if type(message) == list: for m in message: body.append(m) elif message != None: body.append(message) return envelope
def add_to_schema(cls, schemaDict, nsmap): if not cls.get_datatype(nsmap) in schemaDict: for k, v in cls.soap_members.items(): v.add_to_schema(schemaDict, nsmap) schema_node = create_xml_element( nsmap.get("xs") + "complexType", nsmap) schema_node.set('name', cls.__name__) sequence_node = create_xml_subelement( schema_node, nsmap.get('xs') + 'sequence') for k, v in cls.soap_members.items(): member_node = create_xml_subelement( sequence_node, nsmap.get('xs') + 'element') member_node.set('name', k) member_node.set('minOccurs', '0') member_node.set('type', "%s:%s" % (v.get_namespace_id(), v.get_datatype())) typeElement = create_xml_element( nsmap.get('xs') + 'element', nsmap) typeElement.set('name', cls.__name__) typeElement.set('type', "%s:%s" % (cls.get_namespace_id(), cls.__name__)) schemaDict[cls.get_datatype(nsmap)+'Complex'] = schema_node schemaDict[cls.get_datatype(nsmap)] = typeElement
def make_soap_fault(faultString, faultCode = 'Server', detail = None, header_elements = None): ''' This method populates a soap fault message with the provided fault string and details. @param faultString the short description of the error @param detail the details of the exception, such as a stack trace @param faultCode defaults to 'Server', but can be overridden @param header_elements A list of XML elements to add to the fault header. @returns the element corresponding to the fault message ''' nsmap = NamespaceLookup() envelope = create_xml_element(nsmap.get('SOAP-ENV') + 'Envelope', nsmap) if header_elements: header = create_xml_subelement( envelope, nsmap.get('SOAP-ENV') + 'Header') for element in header_elements: header.append(element) body = create_xml_subelement(envelope, nsmap.get('SOAP-ENV') + 'Body') f = Fault(faultCode,faultString,detail) body.append(Fault.to_xml(f, nsmap.get('SOAP-ENV') + "Fault", nsmap)) return envelope
def add_to_schema(self,schema_dict,nsmap): typ = self.get_datatype() self.serializer.add_to_schema(schema_dict, nsmap) if not schema_dict.has_key(typ): complexTypeNode = create_xml_element( nsmap.get('xs') + 'complexType', nsmap) complexTypeNode.set('name',self.get_datatype()) sequenceNode = create_xml_subelement( complexTypeNode, nsmap.get('xs') + 'sequence') elementNode = create_xml_subelement( sequenceNode, nsmap.get('xs') + 'element') elementNode.set('minOccurs','0') elementNode.set('maxOccurs','unbounded') print self.serializer.get_namespace_id() elementNode.set('type', "%s:%s" % (self.serializer.get_namespace_id(), self.serializer.get_datatype())) elementNode.set('name',self.serializer.get_datatype()) typeElement = create_xml_element( nsmap.get('xs') + 'element', nsmap) typeElement.set('name',typ) typeElement.set('type', "%s:%s" % (self.namespace_id, self.get_datatype())) schema_dict['%sElement'%(self.get_datatype(nsmap))] = typeElement schema_dict[self.get_datatype(nsmap)] = complexTypeNode
def make_soap_envelope(message, tns='', header_elements=None): ''' This method takes the results from a soap method call, and wraps them in the appropriate soap envelope with any specified headers @param the message of the soap envelope, either an element or a list of elements @param any header elements to be included in the soap response @returns the envelope element ''' nsmap = NamespaceLookup(tns) envelope = create_xml_element(nsmap.get('SOAP-ENV') + 'Envelope', nsmap, tns) if header_elements: headerElement = create_xml_subelement(envelope, nsmap.get('SOAP-ENV') + 'Header') for h in header_elements: headerElement.append(h) body = create_xml_subelement(envelope, nsmap.get('SOAP-ENV') + 'Body') if type(message) == list: for m in message: body.append(m) elif message != None: body.append(message) return envelope
def _add_messages_for_methods(self, root, methods, nsmap): ''' A private method for adding message elements to the wsdl @param the the root element of the wsdl @param the list of methods. ''' messages = [] #make messages for method in methods: methodName = method.name # making in part inMessage = create_xml_element('message', nsmap) inMessage.set('name', method.inMessage.typ) inPart = create_xml_subelement(inMessage, 'part') inPart.set('name', method.inMessage.name) inPart.set('element', 'tns:' + method.inMessage.typ) messages.append(inMessage) # making out part only if necessary if len(method.outMessage.params) > 0: outMessage = create_xml_element('message', nsmap) outMessage.set('name', method.outMessage.typ) outPart = create_xml_subelement(outMessage, 'part') outPart.set('name', method.outMessage.name) outPart.set('element', 'tns:' + method.outMessage.typ) messages.append(outMessage) for message in messages: root.append(message)
def add_to_schema(self, schema_dict, nsmap): typ = self.get_datatype() self.serializer.add_to_schema(schema_dict, nsmap) if not typ in schema_dict: complexTypeNode = create_xml_element( nsmap.get('xs') + 'complexType', nsmap) complexTypeNode.set('name', self.get_datatype()) sequenceNode = create_xml_subelement(complexTypeNode, nsmap.get('xs') + 'sequence') elementNode = create_xml_subelement(sequenceNode, nsmap.get('xs') + 'element') elementNode.set('minOccurs', '0') elementNode.set('maxOccurs', 'unbounded') elementNode.set( 'type', "%s:%s" % (self.serializer.get_namespace_id(), self.serializer.get_datatype())) elementNode.set('name', self.serializer.get_datatype()) typeElement = create_xml_element( nsmap.get('xs') + 'element', nsmap) typeElement.set('name', typ) typeElement.set('type', "%s:%s" % (self.namespace_id, self.get_datatype())) schema_dict['%sElement' % (self.get_datatype(nsmap))] = typeElement schema_dict[self.get_datatype(nsmap)] = complexTypeNode
def _add_schema(self, types, methods, nsmap): '''A private method for adding the appropriate entries to the schema for the types in the specified methods @param the schema node to add the schema elements to @param the list of methods. ''' schema_entries = {} for method in methods: params = method.inMessage.params returns = method.outMessage.params for name, param in params: param.add_to_schema(schema_entries, nsmap) if returns: returns[0][1].add_to_schema(schema_entries, nsmap) method.inMessage.add_to_schema(schema_entries, nsmap) method.outMessage.add_to_schema(schema_entries, nsmap) schemaNode = create_xml_subelement(types, "schema") schemaNode.set("targetNamespace", self.__tns__) schemaNode.set("xmlns", "http://www.w3.org/2001/XMLSchema") schemaNode.set("elementFormDefault", "qualified") for xxx, node in schema_entries.items(): schemaNode.append(node) return schemaNode
def add_to_schema(cls,schema_dict,nsmap): complexTypeNode = create_xml_element('complexType', nsmap) complexTypeNode.set('name', cls.get_datatype()) sequenceNode = create_xml_subelement(complexTypeNode, 'sequence') faultTypeElem = create_xml_subelement(sequenceNode,'element') faultTypeElem.set('name','detail') faultTypeElem.set(nsmap.get('xsi') + 'type', 'xs:string') faultTypeElem = create_xml_subelement(sequenceNode,'element') faultTypeElem.set('name','message') faultTypeElem.set(nsmap.get('xsi') + 'type', 'xs:string') schema_dict[cls.get_datatype()] = complexTypeNode typeElementItem = create_xml_element('element', nsmap) typeElementItem.set('name', 'ExceptionFaultType') typeElementItem.set(nsmap.get('xsi') + 'type', cls.get_datatype(nsmap)) schema_dict['%sElement'%(cls.get_datatype(nsmap))] = typeElementItem
def add_to_schema(cls, schema_dict, nsmap): complexTypeNode = create_xml_element('complexType', nsmap) complexTypeNode.set('name', cls.get_datatype()) sequenceNode = create_xml_subelement(complexTypeNode, 'sequence') faultTypeElem = create_xml_subelement(sequenceNode, 'element') faultTypeElem.set('name', 'detail') faultTypeElem.set(nsmap.get('xsi') + 'type', 'xs:string') faultTypeElem = create_xml_subelement(sequenceNode, 'element') faultTypeElem.set('name', 'message') faultTypeElem.set(nsmap.get('xsi') + 'type', 'xs:string') schema_dict[cls.get_datatype()] = complexTypeNode typeElementItem = create_xml_element('element', nsmap) typeElementItem.set('name', 'ExceptionFaultType') typeElementItem.set(nsmap.get('xsi') + 'type', cls.get_datatype(nsmap)) schema_dict['%sElement' % (cls.get_datatype(nsmap))] = typeElementItem
def add_to_schema(self, schemaDict, nsmap): complexType = create_xml_element(nsmap.get("xs") + "complexType", nsmap) complexType.set("name", self.typ) sequence = create_xml_subelement(complexType, nsmap.get("xs") + "sequence") if self.params: for name, serializer in self.params: e = create_xml_subelement(sequence, nsmap.get("xs") + "element") e.set("name", name) e.set("type", "%s:%s" % (serializer.get_namespace_id(), serializer.get_datatype())) element = create_xml_element(nsmap.get("xs") + "element", nsmap) element.set("name", self.typ) element.set("type", "%s:%s" % ("tns", self.typ)) schemaDict[self.typ] = complexType schemaDict[self.typ + "Element"] = element
def add_to_schema(self,schemaDict, nsmap): complexType = create_xml_element(nsmap.get('xs') + 'complexType', nsmap) complexType.set('name',self.typ) sequence = create_xml_subelement(complexType, nsmap.get('xs') + 'sequence') if self.params: for name,serializer in self.params: e = create_xml_subelement(sequence, nsmap.get('xs') + 'element') e.set('name',name) e.set('type', "%s:%s" % (serializer.get_namespace_id(), serializer.get_datatype())) element = create_xml_element(nsmap.get('xs') + 'element', nsmap) element.set('name',self.typ) element.set('type','%s:%s' % ('tns',self.typ)) schemaDict[self.typ] = complexType schemaDict[self.typ+'Element'] = element
def make_soap_envelope(message, tns='', header_elements=None): ''' This method takes the results from a soap method call, and wraps them in the appropriate soap envelope with any specified headers @param the message of the soap envelope, either an element or a list of elements @param any header elements to be included in the soap response @returns the envelope element ''' nsmap = NamespaceLookup(tns) envelope = create_xml_element(nsmap.get('SOAP-ENV') + 'Envelope', nsmap, tns) if header_elements: headerElement = create_xml_subelement(envelope, nsmap.get('SOAP-ENV') + 'Header') for h in header_elements: headerElement.append(h) body = create_xml_subelement(envelope, nsmap.get('SOAP-ENV') + 'Body') ###################################### # RJBALEST: # Get the default namespace # xmlns = message.nsmap[None] ###################################### if type(message) == list: for m in message: body.append(m) elif message != None: body.append(message) ###################################### # RJBALEST: # Set xmlns as a normal attribute so it doesn't # get cleanup up and is guaranteed to remain. # message.set('xmlns', xmlns) ###################################### return envelope
def add_to_schema(self, schemaDict, nsmap): complexType = create_xml_element( nsmap.get('xs') + 'complexType', nsmap) complexType.set('name', self.typ) sequence = create_xml_subelement(complexType, nsmap.get('xs') + 'sequence') if self.params: for name, serializer in self.params: e = create_xml_subelement(sequence, nsmap.get('xs') + 'element') e.set('name', name) e.set( 'type', "%s:%s" % (serializer.get_namespace_id(), serializer.get_datatype())) element = create_xml_element(nsmap.get('xs') + 'element', nsmap) element.set('name', self.typ) element.set('type', '%s:%s' % ('tns', self.typ)) schemaDict[self.typ] = complexType schemaDict[self.typ + 'Element'] = element
def make_soap_fault(faultString, faultCode='Server', detail=None, header_elements=None): ''' This method populates a soap fault message with the provided fault string and details. @param faultString the short description of the error @param detail the details of the exception, such as a stack trace @param faultCode defaults to 'Server', but can be overridden @param header_elements A list of XML elements to add to the fault header. @returns the element corresponding to the fault message ''' nsmap = NamespaceLookup() envelope = create_xml_element(nsmap.get('SOAP-ENV') + 'Envelope', nsmap) if header_elements: header = create_xml_subelement(envelope, nsmap.get('SOAP-ENV') + 'Header') for element in header_elements: header.append(element) body = create_xml_subelement(envelope, nsmap.get('SOAP-ENV') + 'Body') f = Fault(faultCode, faultString, detail) body.append(Fault.to_xml(f, nsmap.get('SOAP-ENV') + "Fault", nsmap)) return envelope
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 _add_bindings_for_methods(self, root, serviceName, methods, nsmap): """ A private method for adding bindings to the wsdld @param the root element of the wsdl @param the name of this service @param the methods to be add to the binding node """ hasCallbacks = self._hasCallbacks() # make binding binding = create_xml_subelement(root, "binding") binding.set("name", serviceName) binding.set("type", "tns:%s" % serviceName) sbinding = create_xml_subelement(binding, nsmap.get("soap") + "binding") sbinding.set("style", "document") sbinding.set("transport", "http://schemas.xmlsoap.org/soap/http") if hasCallbacks: callbackBinding = create_xml_subelement(root, "binding") callbackBinding.set("name", "%sCallback" % serviceName) callbackBinding.set("type", "typens:%sCallback" % serviceName) sbinding = create_xml_subelement(callbackBinding, nsmap.get("soap") + "binding") sbinding.set("transport", "http://schemas.xmlsoap.org/soap/http") for method in methods: operation = create_xml_element("operation", nsmap) operation.set("name", method.name) soapOperation = create_xml_subelement(operation, nsmap.get("soap") + "operation") soapOperation.set("soapAction", method.soapAction) soapOperation.set("style", "document") input = create_xml_subelement(operation, "input") input.set("name", method.inMessage.typ) soapBody = create_xml_subelement(input, nsmap.get("soap") + "body") soapBody.set("use", "literal") if method.outMessage.params != None and not method.isAsync and not method.isCallback: output = create_xml_subelement(operation, "output") output.set("name", method.outMessage.typ) soapBody = create_xml_subelement(output, nsmap.get("soap") + "body") soapBody.set("use", "literal") if method.isCallback: relatesTo = create_xml_subelement(input, nsmap.get("soap") + "header") relatesTo.set("message", "tns:RelatesToHeader") relatesTo.set("part", "RelatesTo") relatesTo.set("use", "literal") callbackBinding.append(operation) else: if method.isAsync: rtHeader = create_xml_subelement(input, nsmap.get("soap") + "header") rtHeader.set("message", "tns:ReplyToHeader") rtHeader.set("part", "ReplyTo") rtHeader.set("use", "literal") midHeader = create_xml_subelement(input, nsmap.get("soap") + "header") midHeader.set("message", "tns:MessageIDHeader") midHeader.set("part", "MessageID") midHeader.set("use", "literal") binding.append(operation)
def to_xml(cls, value, name, nsmap=ns): fault = create_xml_element(name, nsmap) create_xml_subelement(fault, 'faultcode').text = value.faultcode create_xml_subelement(fault, 'faultstring').text = value.faultstring detail = create_xml_subelement(fault, 'detail').text = value.detail return fault
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 _add_bindings_for_methods(self, root, serviceName, methods, nsmap): ''' A private method for adding bindings to the wsdld @param the root element of the wsdl @param the name of this service @param the methods to be add to the binding node ''' hasCallbacks = self._hasCallbacks() # make binding binding = create_xml_subelement(root, 'binding') binding.set('name', serviceName) binding.set('type', 'tns:%s'%serviceName) sbinding = create_xml_subelement(binding, nsmap.get('soap') + 'binding') sbinding.set('style', 'document') sbinding.set('transport', 'http://schemas.xmlsoap.org/soap/http') if hasCallbacks: callbackBinding = create_xml_subelement(root, 'binding') callbackBinding.set('name', '%sCallback' % serviceName) callbackBinding.set('type', 'typens:%sCallback' % serviceName) sbinding = create_xml_subelement(callbackBinding, nsmap.get('soap') + 'binding') sbinding.set('transport', 'http://schemas.xmlsoap.org/soap/http') for method in methods: operation = create_xml_element('operation', nsmap) operation.set('name', method.name) soapOperation = create_xml_subelement(operation, nsmap.get('soap') + 'operation') soapOperation.set('soapAction', method.soapAction) soapOperation.set('style', 'document') input = create_xml_subelement(operation, 'input') input.set('name', method.inMessage.typ) soapBody = create_xml_subelement(input, nsmap.get('soap') + 'body') soapBody.set('use', 'literal') if (len(method.outMessage.params) > 0 and not method.isAsync and not method.isCallback): output = create_xml_subelement(operation, 'output') output.set('name', method.outMessage.typ) soapBody = create_xml_subelement(output, nsmap.get('soap') + 'body') soapBody.set('use', 'literal') if method.isCallback: relatesTo = create_xml_subelement(input, nsmap.get('soap') + 'header') relatesTo.set('message', 'tns:RelatesToHeader') relatesTo.set('part', 'RelatesTo') relatesTo.set('use', 'literal') callbackBinding.append(operation) else: if method.isAsync: rtHeader = create_xml_subelement(input, nsmap.get('soap') + 'header') rtHeader.set('message', 'tns:ReplyToHeader') rtHeader.set('part', 'ReplyTo') rtHeader.set('use', 'literal') midHeader = create_xml_subelement(input, nsmap.get('soap') + 'header') midHeader.set('message', 'tns:MessageIDHeader') midHeader.set('part', 'MessageID') midHeader.set('use', 'literal') binding.append(operation)
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__