You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1298 lines
46 KiB

  1. # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
  2. #
  3. # This software is subject to the provisions of the Zope Public License,
  4. # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
  5. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
  6. # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  7. # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
  8. # FOR A PARTICULAR PURPOSE.
  9. ident = "$Id$"
  10. from Utility import DOM, Collection, CollectionNS
  11. from XMLSchema import XMLSchema, SchemaReader, WSDLToolsAdapter
  12. from Namespaces import WSR, WSA
  13. from StringIO import StringIO
  14. import urllib
  15. class WSDLReader:
  16. """A WSDLReader creates WSDL instances from urls and xml data."""
  17. # Custom subclasses of WSDLReader may wish to implement a caching
  18. # strategy or other optimizations. Because application needs vary
  19. # so widely, we don't try to provide any caching by default.
  20. def loadFromStream(self, stream, name=None):
  21. """Return a WSDL instance loaded from a stream object."""
  22. document = DOM.loadDocument(stream)
  23. wsdl = WSDL()
  24. if name:
  25. wsdl.location = name
  26. elif hasattr(stream, 'name'):
  27. wsdl.location = stream.name
  28. wsdl.load(document)
  29. return wsdl
  30. def loadFromURL(self, url):
  31. """Return a WSDL instance loaded from the given url."""
  32. document = DOM.loadFromURL(url)
  33. wsdl = WSDL()
  34. wsdl.location = url
  35. wsdl.load(document)
  36. return wsdl
  37. def loadFromString(self, data):
  38. """Return a WSDL instance loaded from an xml string."""
  39. return self.loadFromStream(StringIO(data))
  40. def loadFromFile(self, filename):
  41. """Return a WSDL instance loaded from the given file."""
  42. file = open(filename, 'rb')
  43. try:
  44. wsdl = self.loadFromStream(file)
  45. finally:
  46. file.close()
  47. return wsdl
  48. class WSDL:
  49. """A WSDL object models a WSDL service description. WSDL objects
  50. may be created manually or loaded from an xml representation
  51. using a WSDLReader instance."""
  52. def __init__(self, targetNamespace=None, strict=1):
  53. self.targetNamespace = targetNamespace or 'urn:this-document.wsdl'
  54. self.documentation = ''
  55. self.location = None
  56. self.document = None
  57. self.name = None
  58. self.services = CollectionNS(self)
  59. self.messages = CollectionNS(self)
  60. self.portTypes = CollectionNS(self)
  61. self.bindings = CollectionNS(self)
  62. self.imports = Collection(self)
  63. self.types = Types(self)
  64. self.extensions = []
  65. self.strict = strict
  66. def __del__(self):
  67. if self.document is not None:
  68. self.document.unlink()
  69. self.document = None
  70. version = '1.1'
  71. def addService(self, name, documentation='', targetNamespace=None):
  72. if self.services.has_key(name):
  73. raise WSDLError(
  74. 'Duplicate service element: %s' % name
  75. )
  76. item = Service(name, documentation)
  77. if targetNamespace:
  78. item.targetNamespace = targetNamespace
  79. self.services[name] = item
  80. return item
  81. def addMessage(self, name, documentation='', targetNamespace=None):
  82. if self.messages.has_key(name):
  83. raise WSDLError(
  84. 'Duplicate message element: %s.' % name
  85. )
  86. item = Message(name, documentation)
  87. if targetNamespace:
  88. item.targetNamespace = targetNamespace
  89. self.messages[name] = item
  90. return item
  91. def addPortType(self, name, documentation='', targetNamespace=None):
  92. if self.portTypes.has_key(name):
  93. raise WSDLError(
  94. 'Duplicate portType element: name'
  95. )
  96. item = PortType(name, documentation)
  97. if targetNamespace:
  98. item.targetNamespace = targetNamespace
  99. self.portTypes[name] = item
  100. return item
  101. def addBinding(self, name, type, documentation='', targetNamespace=None):
  102. if self.bindings.has_key(name):
  103. raise WSDLError(
  104. 'Duplicate binding element: %s' % name
  105. )
  106. item = Binding(name, type, documentation)
  107. if targetNamespace:
  108. item.targetNamespace = targetNamespace
  109. self.bindings[name] = item
  110. return item
  111. def addImport(self, namespace, location):
  112. item = ImportElement(namespace, location)
  113. self.imports[namespace] = item
  114. return item
  115. def load(self, document):
  116. # We save a reference to the DOM document to ensure that elements
  117. # saved as "extensions" will continue to have a meaningful context
  118. # for things like namespace references. The lifetime of the DOM
  119. # document is bound to the lifetime of the WSDL instance.
  120. self.document = document
  121. definitions = DOM.getElement(document, 'definitions', None, None)
  122. if definitions is None:
  123. raise WSDLError(
  124. 'Missing <definitions> element.'
  125. )
  126. self.version = DOM.WSDLUriToVersion(definitions.namespaceURI)
  127. NS_WSDL = DOM.GetWSDLUri(self.version)
  128. self.targetNamespace = DOM.getAttr(definitions, 'targetNamespace',
  129. None, None)
  130. self.name = DOM.getAttr(definitions, 'name', None, None)
  131. self.documentation = GetDocumentation(definitions)
  132. # Resolve (recursively) any import elements in the document.
  133. imported = {}
  134. while 1:
  135. imports = []
  136. for element in DOM.getElements(definitions, 'import', NS_WSDL):
  137. location = DOM.getAttr(element, 'location')
  138. if not imported.has_key(location):
  139. imports.append(element)
  140. if not imports:
  141. break
  142. for element in imports:
  143. self._import(document, element)
  144. location = DOM.getAttr(element, 'location')
  145. imported[location] = 1
  146. reader = SchemaReader(base_url=self.location)
  147. for element in DOM.getElements(definitions, None, None):
  148. targetNamespace = DOM.getAttr(element, 'targetNamespace')
  149. localName = element.localName
  150. if not DOM.nsUriMatch(element.namespaceURI, NS_WSDL):
  151. if localName == 'schema':
  152. schema = reader.loadFromNode(WSDLToolsAdapter(self), element)
  153. schema.setBaseUrl(self.location)
  154. self.types.addSchema(schema)
  155. else:
  156. self.extensions.append(element)
  157. continue
  158. elif localName == 'message':
  159. name = DOM.getAttr(element, 'name')
  160. docs = GetDocumentation(element)
  161. message = self.addMessage(name, docs, targetNamespace)
  162. parts = DOM.getElements(element, 'part', NS_WSDL)
  163. message.load(parts)
  164. continue
  165. elif localName == 'portType':
  166. name = DOM.getAttr(element, 'name')
  167. docs = GetDocumentation(element)
  168. ptype = self.addPortType(name, docs, targetNamespace)
  169. #operations = DOM.getElements(element, 'operation', NS_WSDL)
  170. #ptype.load(operations)
  171. ptype.load(element)
  172. continue
  173. elif localName == 'binding':
  174. name = DOM.getAttr(element, 'name')
  175. type = DOM.getAttr(element, 'type', default=None)
  176. if type is None:
  177. raise WSDLError(
  178. 'Missing type attribute for binding %s.' % name
  179. )
  180. type = ParseQName(type, element)
  181. docs = GetDocumentation(element)
  182. binding = self.addBinding(name, type, docs, targetNamespace)
  183. operations = DOM.getElements(element, 'operation', NS_WSDL)
  184. binding.load(operations)
  185. binding.load_ex(GetExtensions(element))
  186. continue
  187. elif localName == 'service':
  188. name = DOM.getAttr(element, 'name')
  189. docs = GetDocumentation(element)
  190. service = self.addService(name, docs, targetNamespace)
  191. ports = DOM.getElements(element, 'port', NS_WSDL)
  192. service.load(ports)
  193. service.load_ex(GetExtensions(element))
  194. continue
  195. elif localName == 'types':
  196. self.types.documentation = GetDocumentation(element)
  197. for item in DOM.getElements(element, None, None):
  198. if item.localName == 'schema':
  199. schema = reader.loadFromNode(WSDLToolsAdapter(self), item)
  200. schema.setBaseUrl(self.location)
  201. self.types.addSchema(schema)
  202. else:
  203. self.types.addExtension(item)
  204. continue
  205. def _import(self, document, element):
  206. namespace = DOM.getAttr(element, 'namespace', default=None)
  207. location = DOM.getAttr(element, 'location', default=None)
  208. if namespace is None or location is None:
  209. raise WSDLError(
  210. 'Invalid import element (missing namespace or location).'
  211. )
  212. # Sort-of support relative locations to simplify unit testing. The
  213. # WSDL specification actually doesn't allow relative URLs, so its
  214. # ok that this only works with urls relative to the initial document.
  215. location = urllib.basejoin(self.location, location)
  216. obimport = self.addImport(namespace, location)
  217. obimport._loaded = 1
  218. importdoc = DOM.loadFromURL(location)
  219. try:
  220. if location.find('#') > -1:
  221. idref = location.split('#')[-1]
  222. imported = DOM.getElementById(importdoc, idref)
  223. else:
  224. imported = importdoc.documentElement
  225. if imported is None:
  226. raise WSDLError(
  227. 'Import target element not found for: %s' % location
  228. )
  229. imported_tns = DOM.findTargetNS(imported)
  230. if imported_tns != namespace:
  231. return
  232. if imported.localName == 'definitions':
  233. imported_nodes = imported.childNodes
  234. else:
  235. imported_nodes = [imported]
  236. parent = element.parentNode
  237. for node in imported_nodes:
  238. if node.nodeType != node.ELEMENT_NODE:
  239. continue
  240. child = DOM.importNode(document, node, 1)
  241. parent.appendChild(child)
  242. child.setAttribute('targetNamespace', namespace)
  243. attrsNS = imported._attrsNS
  244. for attrkey in attrsNS.keys():
  245. if attrkey[0] == DOM.NS_XMLNS:
  246. attr = attrsNS[attrkey].cloneNode(1)
  247. child.setAttributeNode(attr)
  248. finally:
  249. importdoc.unlink()
  250. class Element:
  251. """A class that provides common functions for WSDL element classes."""
  252. def __init__(self, name=None, documentation=''):
  253. self.name = name
  254. self.documentation = documentation
  255. self.extensions = []
  256. def addExtension(self, item):
  257. self.extensions.append(item)
  258. class ImportElement(Element):
  259. def __init__(self, namespace, location):
  260. self.namespace = namespace
  261. self.location = location
  262. _loaded = None
  263. class Types(Collection):
  264. default = lambda self,k: k.targetNamespace
  265. def __init__(self, parent):
  266. Collection.__init__(self, parent)
  267. self.documentation = ''
  268. self.extensions = []
  269. def addSchema(self, schema):
  270. name = schema.targetNamespace
  271. self[name] = schema
  272. return schema
  273. def addExtension(self, item):
  274. self.extensions.append(item)
  275. class Message(Element):
  276. def __init__(self, name, documentation=''):
  277. Element.__init__(self, name, documentation)
  278. self.parts = Collection(self)
  279. def addPart(self, name, type=None, element=None):
  280. if self.parts.has_key(name):
  281. raise WSDLError(
  282. 'Duplicate message part element: %s' % name
  283. )
  284. if type is None and element is None:
  285. raise WSDLError(
  286. 'Missing type or element attribute for part: %s' % name
  287. )
  288. item = MessagePart(name)
  289. item.element = element
  290. item.type = type
  291. self.parts[name] = item
  292. return item
  293. def load(self, elements):
  294. for element in elements:
  295. name = DOM.getAttr(element, 'name')
  296. part = MessagePart(name)
  297. self.parts[name] = part
  298. elemref = DOM.getAttr(element, 'element', default=None)
  299. typeref = DOM.getAttr(element, 'type', default=None)
  300. if typeref is None and elemref is None:
  301. raise WSDLError(
  302. 'No type or element attribute for part: %s' % name
  303. )
  304. if typeref is not None:
  305. part.type = ParseTypeRef(typeref, element)
  306. if elemref is not None:
  307. part.element = ParseTypeRef(elemref, element)
  308. class MessagePart(Element):
  309. def __init__(self, name):
  310. Element.__init__(self, name, '')
  311. self.element = None
  312. self.type = None
  313. def getWSDL(self):
  314. """Return the WSDL object that contains this Message Part."""
  315. return self.parent().parent().parent().parent()
  316. def getTypeDefinition(self):
  317. wsdl = self.getWSDL()
  318. nsuri,name = self.type
  319. schema = wsdl.types.get(nsuri, {})
  320. return schema.get(name)
  321. def getElementDeclaration(self):
  322. wsdl = self.getWSDL()
  323. nsuri,name = self.element
  324. schema = wsdl.types.get(nsuri, {})
  325. return schema.get(name)
  326. class PortType(Element):
  327. '''PortType has a anyAttribute, thus must provide for an extensible
  328. mechanism for supporting such attributes. ResourceProperties is
  329. specified in WS-ResourceProperties. wsa:Action is specified in
  330. WS-Address.
  331. Instance Data:
  332. name -- name attribute
  333. resourceProperties -- optional. wsr:ResourceProperties attribute,
  334. value is a QName this is Parsed into a (namespaceURI, name)
  335. that represents a Global Element Declaration.
  336. operations
  337. '''
  338. def __init__(self, name, documentation=''):
  339. Element.__init__(self, name, documentation)
  340. self.operations = Collection(self)
  341. self.resourceProperties = None
  342. def getWSDL(self):
  343. return self.parent().parent()
  344. def getResourceProperties(self):
  345. return self.resourceProperties
  346. def addOperation(self, name, documentation='', parameterOrder=None):
  347. item = Operation(name, documentation, parameterOrder)
  348. self.operations[name] = item
  349. return item
  350. def load(self, element):
  351. self.name = DOM.getAttr(element, 'name')
  352. self.documentation = GetDocumentation(element)
  353. if DOM.hasAttr(element, 'ResourceProperties', WSR.PROPERTIES):
  354. rpref = DOM.getAttr(element, 'ResourceProperties', WSR.PROPERTIES)
  355. self.resourceProperties = ParseQName(rpref, element)
  356. NS_WSDL = DOM.GetWSDLUri(self.getWSDL().version)
  357. elements = DOM.getElements(element, 'operation', NS_WSDL)
  358. for element in elements:
  359. name = DOM.getAttr(element, 'name')
  360. docs = GetDocumentation(element)
  361. param_order = DOM.getAttr(element, 'parameterOrder', default=None)
  362. if param_order is not None:
  363. param_order = param_order.split(' ')
  364. operation = self.addOperation(name, docs, param_order)
  365. item = DOM.getElement(element, 'input', None, None)
  366. if item is not None:
  367. name = DOM.getAttr(item, 'name')
  368. docs = GetDocumentation(item)
  369. msgref = DOM.getAttr(item, 'message')
  370. message = ParseQName(msgref, item)
  371. action = DOM.getAttr(item, 'Action', WSA.ADDRESS, None)
  372. operation.setInput(message, name, docs, action)
  373. item = DOM.getElement(element, 'output', None, None)
  374. if item is not None:
  375. name = DOM.getAttr(item, 'name')
  376. docs = GetDocumentation(item)
  377. msgref = DOM.getAttr(item, 'message')
  378. message = ParseQName(msgref, item)
  379. action = DOM.getAttr(item, 'Action', WSA.ADDRESS, None)
  380. operation.setOutput(message, name, docs, action)
  381. for item in DOM.getElements(element, 'fault', None):
  382. name = DOM.getAttr(item, 'name')
  383. docs = GetDocumentation(item)
  384. msgref = DOM.getAttr(item, 'message')
  385. message = ParseQName(msgref, item)
  386. action = DOM.getAttr(item, 'Action', WSA.ADDRESS, None)
  387. operation.addFault(message, name, docs, action)
  388. class Operation(Element):
  389. def __init__(self, name, documentation='', parameterOrder=None):
  390. Element.__init__(self, name, documentation)
  391. self.parameterOrder = parameterOrder
  392. self.faults = Collection(self)
  393. self.input = None
  394. self.output = None
  395. def getPortType(self):
  396. return self.parent().parent()
  397. def getInputAction(self):
  398. """wsa:Action attribute"""
  399. return GetWSAActionInput(self)
  400. def getInputMessage(self):
  401. if self.input is None:
  402. return None
  403. wsdl = self.getPortType().getWSDL()
  404. return wsdl.messages[self.input.message]
  405. def getOutputAction(self):
  406. """wsa:Action attribute"""
  407. return GetWSAActionOutput(self)
  408. def getOutputMessage(self):
  409. if self.output is None:
  410. return None
  411. wsdl = self.getPortType().getWSDL()
  412. return wsdl.messages[self.output.message]
  413. def getFaultAction(self, name):
  414. """wsa:Action attribute"""
  415. return GetWSAActionFault(self, name)
  416. def getFaultMessage(self, name):
  417. wsdl = self.getPortType().getWSDL()
  418. return wsdl.messages[self.faults[name].message]
  419. def addFault(self, message, name, documentation='', action=None):
  420. if self.faults.has_key(name):
  421. raise WSDLError(
  422. 'Duplicate fault element: %s' % name
  423. )
  424. item = MessageRole('fault', message, name, documentation, action)
  425. self.faults[name] = item
  426. return item
  427. def setInput(self, message, name='', documentation='', action=None):
  428. self.input = MessageRole('input', message, name, documentation, action)
  429. return self.input
  430. def setOutput(self, message, name='', documentation='', action=None):
  431. self.output = MessageRole('output', message, name, documentation, action)
  432. return self.output
  433. class MessageRole(Element):
  434. def __init__(self, type, message, name='', documentation='', action=None):
  435. Element.__init__(self, name, documentation)
  436. self.message = message
  437. self.type = type
  438. self.action = action
  439. class Binding(Element):
  440. def __init__(self, name, type, documentation=''):
  441. Element.__init__(self, name, documentation)
  442. self.operations = Collection(self)
  443. self.type = type
  444. def getWSDL(self):
  445. """Return the WSDL object that contains this binding."""
  446. return self.parent().parent()
  447. def getPortType(self):
  448. """Return the PortType object associated with this binding."""
  449. return self.getWSDL().portTypes[self.type]
  450. def findBinding(self, kind):
  451. for item in self.extensions:
  452. if isinstance(item, kind):
  453. return item
  454. return None
  455. def findBindings(self, kind):
  456. return [ item for item in self.extensions if isinstance(item, kind) ]
  457. def addOperationBinding(self, name, documentation=''):
  458. item = OperationBinding(name, documentation)
  459. self.operations[name] = item
  460. return item
  461. def load(self, elements):
  462. for element in elements:
  463. name = DOM.getAttr(element, 'name')
  464. docs = GetDocumentation(element)
  465. opbinding = self.addOperationBinding(name, docs)
  466. opbinding.load_ex(GetExtensions(element))
  467. item = DOM.getElement(element, 'input', None, None)
  468. if item is not None:
  469. mbinding = MessageRoleBinding('input')
  470. mbinding.documentation = GetDocumentation(item)
  471. opbinding.input = mbinding
  472. mbinding.load_ex(GetExtensions(item))
  473. item = DOM.getElement(element, 'output', None, None)
  474. if item is not None:
  475. mbinding = MessageRoleBinding('output')
  476. mbinding.documentation = GetDocumentation(item)
  477. opbinding.output = mbinding
  478. mbinding.load_ex(GetExtensions(item))
  479. for item in DOM.getElements(element, 'fault', None):
  480. name = DOM.getAttr(item, 'name')
  481. mbinding = MessageRoleBinding('fault', name)
  482. mbinding.documentation = GetDocumentation(item)
  483. opbinding.faults[name] = mbinding
  484. mbinding.load_ex(GetExtensions(item))
  485. def load_ex(self, elements):
  486. for e in elements:
  487. ns, name = e.namespaceURI, e.localName
  488. if ns in DOM.NS_SOAP_BINDING_ALL and name == 'binding':
  489. transport = DOM.getAttr(e, 'transport', default=None)
  490. style = DOM.getAttr(e, 'style', default='document')
  491. ob = SoapBinding(transport, style)
  492. self.addExtension(ob)
  493. continue
  494. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'binding':
  495. verb = DOM.getAttr(e, 'verb')
  496. ob = HttpBinding(verb)
  497. self.addExtension(ob)
  498. continue
  499. else:
  500. self.addExtension(e)
  501. class OperationBinding(Element):
  502. def __init__(self, name, documentation=''):
  503. Element.__init__(self, name, documentation)
  504. self.input = None
  505. self.output = None
  506. self.faults = Collection(self)
  507. def getBinding(self):
  508. """Return the parent Binding object of the operation binding."""
  509. return self.parent().parent()
  510. def getOperation(self):
  511. """Return the abstract Operation associated with this binding."""
  512. return self.getBinding().getPortType().operations[self.name]
  513. def findBinding(self, kind):
  514. for item in self.extensions:
  515. if isinstance(item, kind):
  516. return item
  517. return None
  518. def findBindings(self, kind):
  519. return [ item for item in self.extensions if isinstance(item, kind) ]
  520. def addInputBinding(self, binding):
  521. if self.input is None:
  522. self.input = MessageRoleBinding('input')
  523. self.input.addExtension(binding)
  524. return binding
  525. def addOutputBinding(self, binding):
  526. if self.output is None:
  527. self.output = MessageRoleBinding('output')
  528. self.output.addExtension(binding)
  529. return binding
  530. def addFaultBinding(self, name, binding):
  531. fault = self.get(name, None)
  532. if fault is None:
  533. fault = MessageRoleBinding('fault', name)
  534. fault.addExtension(binding)
  535. return binding
  536. def load_ex(self, elements):
  537. for e in elements:
  538. ns, name = e.namespaceURI, e.localName
  539. if ns in DOM.NS_SOAP_BINDING_ALL and name == 'operation':
  540. soapaction = DOM.getAttr(e, 'soapAction', default=None)
  541. style = DOM.getAttr(e, 'style', default=None)
  542. ob = SoapOperationBinding(soapaction, style)
  543. self.addExtension(ob)
  544. continue
  545. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'operation':
  546. location = DOM.getAttr(e, 'location')
  547. ob = HttpOperationBinding(location)
  548. self.addExtension(ob)
  549. continue
  550. else:
  551. self.addExtension(e)
  552. class MessageRoleBinding(Element):
  553. def __init__(self, type, name='', documentation=''):
  554. Element.__init__(self, name, documentation)
  555. self.type = type
  556. def findBinding(self, kind):
  557. for item in self.extensions:
  558. if isinstance(item, kind):
  559. return item
  560. return None
  561. def findBindings(self, kind):
  562. return [ item for item in self.extensions if isinstance(item, kind) ]
  563. def load_ex(self, elements):
  564. for e in elements:
  565. ns, name = e.namespaceURI, e.localName
  566. if ns in DOM.NS_SOAP_BINDING_ALL and name == 'body':
  567. encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
  568. namespace = DOM.getAttr(e, 'namespace', default=None)
  569. parts = DOM.getAttr(e, 'parts', default=None)
  570. use = DOM.getAttr(e, 'use', default=None)
  571. if use is None:
  572. raise WSDLError(
  573. 'Invalid soap:body binding element.'
  574. )
  575. ob = SoapBodyBinding(use, namespace, encstyle, parts)
  576. self.addExtension(ob)
  577. continue
  578. elif ns in DOM.NS_SOAP_BINDING_ALL and name == 'fault':
  579. encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
  580. namespace = DOM.getAttr(e, 'namespace', default=None)
  581. name = DOM.getAttr(e, 'name', default=None)
  582. use = DOM.getAttr(e, 'use', default=None)
  583. if use is None or name is None:
  584. raise WSDLError(
  585. 'Invalid soap:fault binding element.'
  586. )
  587. ob = SoapFaultBinding(name, use, namespace, encstyle)
  588. self.addExtension(ob)
  589. continue
  590. elif ns in DOM.NS_SOAP_BINDING_ALL and name in (
  591. 'header', 'headerfault'
  592. ):
  593. encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
  594. namespace = DOM.getAttr(e, 'namespace', default=None)
  595. message = DOM.getAttr(e, 'message')
  596. part = DOM.getAttr(e, 'part')
  597. use = DOM.getAttr(e, 'use')
  598. if name == 'header':
  599. _class = SoapHeaderBinding
  600. else:
  601. _class = SoapHeaderFaultBinding
  602. message = ParseQName(message, e)
  603. ob = _class(message, part, use, namespace, encstyle)
  604. self.addExtension(ob)
  605. continue
  606. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'urlReplacement':
  607. ob = HttpUrlReplacementBinding()
  608. self.addExtension(ob)
  609. continue
  610. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'urlEncoded':
  611. ob = HttpUrlEncodedBinding()
  612. self.addExtension(ob)
  613. continue
  614. elif ns in DOM.NS_MIME_BINDING_ALL and name == 'multipartRelated':
  615. ob = MimeMultipartRelatedBinding()
  616. self.addExtension(ob)
  617. ob.load_ex(GetExtensions(e))
  618. continue
  619. elif ns in DOM.NS_MIME_BINDING_ALL and name == 'content':
  620. part = DOM.getAttr(e, 'part', default=None)
  621. type = DOM.getAttr(e, 'type', default=None)
  622. ob = MimeContentBinding(part, type)
  623. self.addExtension(ob)
  624. continue
  625. elif ns in DOM.NS_MIME_BINDING_ALL and name == 'mimeXml':
  626. part = DOM.getAttr(e, 'part', default=None)
  627. ob = MimeXmlBinding(part)
  628. self.addExtension(ob)
  629. continue
  630. else:
  631. self.addExtension(e)
  632. class Service(Element):
  633. def __init__(self, name, documentation=''):
  634. Element.__init__(self, name, documentation)
  635. self.ports = Collection(self)
  636. def getWSDL(self):
  637. return self.parent().parent()
  638. def addPort(self, name, binding, documentation=''):
  639. item = Port(name, binding, documentation)
  640. self.ports[name] = item
  641. return item
  642. def load(self, elements):
  643. for element in elements:
  644. name = DOM.getAttr(element, 'name', default=None)
  645. docs = GetDocumentation(element)
  646. binding = DOM.getAttr(element, 'binding', default=None)
  647. if name is None or binding is None:
  648. raise WSDLError(
  649. 'Invalid port element.'
  650. )
  651. binding = ParseQName(binding, element)
  652. port = self.addPort(name, binding, docs)
  653. port.load_ex(GetExtensions(element))
  654. def load_ex(self, elements):
  655. for e in elements:
  656. self.addExtension(e)
  657. class Port(Element):
  658. def __init__(self, name, binding, documentation=''):
  659. Element.__init__(self, name, documentation)
  660. self.binding = binding
  661. def getService(self):
  662. """Return the Service object associated with this port."""
  663. return self.parent().parent()
  664. def getBinding(self):
  665. """Return the Binding object that is referenced by this port."""
  666. wsdl = self.getService().getWSDL()
  667. return wsdl.bindings[self.binding]
  668. def getPortType(self):
  669. """Return the PortType object that is referenced by this port."""
  670. wsdl = self.getService().getWSDL()
  671. binding = wsdl.bindings[self.binding]
  672. return wsdl.portTypes[binding.type]
  673. def getAddressBinding(self):
  674. """A convenience method to obtain the extension element used
  675. as the address binding for the port, or None if undefined."""
  676. for item in self.extensions:
  677. if isinstance(item, SoapAddressBinding) or \
  678. isinstance(item, HttpAddressBinding):
  679. return item
  680. raise WSDLError(
  681. 'No address binding found in port.'
  682. )
  683. def load_ex(self, elements):
  684. for e in elements:
  685. ns, name = e.namespaceURI, e.localName
  686. if ns in DOM.NS_SOAP_BINDING_ALL and name == 'address':
  687. location = DOM.getAttr(e, 'location', default=None)
  688. ob = SoapAddressBinding(location)
  689. self.addExtension(ob)
  690. continue
  691. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'address':
  692. location = DOM.getAttr(e, 'location', default=None)
  693. ob = HttpAddressBinding(location)
  694. self.addExtension(ob)
  695. continue
  696. else:
  697. self.addExtension(e)
  698. class SoapBinding:
  699. def __init__(self, transport, style='rpc'):
  700. self.transport = transport
  701. self.style = style
  702. class SoapAddressBinding:
  703. def __init__(self, location):
  704. self.location = location
  705. class SoapOperationBinding:
  706. def __init__(self, soapAction=None, style=None):
  707. self.soapAction = soapAction
  708. self.style = style
  709. class SoapBodyBinding:
  710. def __init__(self, use, namespace=None, encodingStyle=None, parts=None):
  711. if not use in ('literal', 'encoded'):
  712. raise WSDLError(
  713. 'Invalid use attribute value: %s' % use
  714. )
  715. self.encodingStyle = encodingStyle
  716. self.namespace = namespace
  717. if type(parts) in (type(''), type(u'')):
  718. parts = parts.split()
  719. self.parts = parts
  720. self.use = use
  721. class SoapFaultBinding:
  722. def __init__(self, name, use, namespace=None, encodingStyle=None):
  723. if not use in ('literal', 'encoded'):
  724. raise WSDLError(
  725. 'Invalid use attribute value: %s' % use
  726. )
  727. self.encodingStyle = encodingStyle
  728. self.namespace = namespace
  729. self.name = name
  730. self.use = use
  731. class SoapHeaderBinding:
  732. def __init__(self, message, part, use, namespace=None, encodingStyle=None):
  733. if not use in ('literal', 'encoded'):
  734. raise WSDLError(
  735. 'Invalid use attribute value: %s' % use
  736. )
  737. self.encodingStyle = encodingStyle
  738. self.namespace = namespace
  739. self.message = message
  740. self.part = part
  741. self.use = use
  742. tagname = 'header'
  743. class SoapHeaderFaultBinding(SoapHeaderBinding):
  744. tagname = 'headerfault'
  745. class HttpBinding:
  746. def __init__(self, verb):
  747. self.verb = verb
  748. class HttpAddressBinding:
  749. def __init__(self, location):
  750. self.location = location
  751. class HttpOperationBinding:
  752. def __init__(self, location):
  753. self.location = location
  754. class HttpUrlReplacementBinding:
  755. pass
  756. class HttpUrlEncodedBinding:
  757. pass
  758. class MimeContentBinding:
  759. def __init__(self, part=None, type=None):
  760. self.part = part
  761. self.type = type
  762. class MimeXmlBinding:
  763. def __init__(self, part=None):
  764. self.part = part
  765. class MimeMultipartRelatedBinding:
  766. def __init__(self):
  767. self.parts = []
  768. def load_ex(self, elements):
  769. for e in elements:
  770. ns, name = e.namespaceURI, e.localName
  771. if ns in DOM.NS_MIME_BINDING_ALL and name == 'part':
  772. self.parts.append(MimePartBinding())
  773. continue
  774. class MimePartBinding:
  775. def __init__(self):
  776. self.items = []
  777. def load_ex(self, elements):
  778. for e in elements:
  779. ns, name = e.namespaceURI, e.localName
  780. if ns in DOM.NS_MIME_BINDING_ALL and name == 'content':
  781. part = DOM.getAttr(e, 'part', default=None)
  782. type = DOM.getAttr(e, 'type', default=None)
  783. ob = MimeContentBinding(part, type)
  784. self.items.append(ob)
  785. continue
  786. elif ns in DOM.NS_MIME_BINDING_ALL and name == 'mimeXml':
  787. part = DOM.getAttr(e, 'part', default=None)
  788. ob = MimeXmlBinding(part)
  789. self.items.append(ob)
  790. continue
  791. elif ns in DOM.NS_SOAP_BINDING_ALL and name == 'body':
  792. encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
  793. namespace = DOM.getAttr(e, 'namespace', default=None)
  794. parts = DOM.getAttr(e, 'parts', default=None)
  795. use = DOM.getAttr(e, 'use', default=None)
  796. if use is None:
  797. raise WSDLError(
  798. 'Invalid soap:body binding element.'
  799. )
  800. ob = SoapBodyBinding(use, namespace, encstyle, parts)
  801. self.items.append(ob)
  802. continue
  803. class WSDLError(Exception):
  804. pass
  805. def DeclareNSPrefix(writer, prefix, nsuri):
  806. if writer.hasNSPrefix(nsuri):
  807. return
  808. writer.declareNSPrefix(prefix, nsuri)
  809. def ParseTypeRef(value, element):
  810. parts = value.split(':', 1)
  811. if len(parts) == 1:
  812. return (DOM.findTargetNS(element), value)
  813. nsuri = DOM.findNamespaceURI(parts[0], element)
  814. return (nsuri, parts[1])
  815. def ParseQName(value, element):
  816. nameref = value.split(':', 1)
  817. if len(nameref) == 2:
  818. nsuri = DOM.findNamespaceURI(nameref[0], element)
  819. name = nameref[-1]
  820. else:
  821. nsuri = DOM.findTargetNS(element)
  822. name = nameref[-1]
  823. return nsuri, name
  824. def GetDocumentation(element):
  825. docnode = DOM.getElement(element, 'documentation', None, None)
  826. if docnode is not None:
  827. return DOM.getElementText(docnode)
  828. return ''
  829. def GetExtensions(element):
  830. return [ item for item in DOM.getElements(element, None, None)
  831. if item.namespaceURI != DOM.NS_WSDL ]
  832. def GetWSAActionFault(operation, name):
  833. """Find wsa:Action attribute, and return value or WSA.FAULT
  834. for the default.
  835. """
  836. attr = operation.faults[name].action
  837. if attr is not None:
  838. return attr
  839. return WSA.FAULT
  840. def GetWSAActionInput(operation):
  841. """Find wsa:Action attribute, and return value or the default."""
  842. attr = operation.input.action
  843. if attr is not None:
  844. return attr
  845. portType = operation.getPortType()
  846. targetNamespace = portType.getWSDL().targetNamespace
  847. ptName = portType.name
  848. msgName = operation.input.name
  849. if not msgName:
  850. msgName = operation.name + 'Request'
  851. if targetNamespace.endswith('/'):
  852. return '%s%s/%s' %(targetNamespace, ptName, msgName)
  853. return '%s/%s/%s' %(targetNamespace, ptName, msgName)
  854. def GetWSAActionOutput(operation):
  855. """Find wsa:Action attribute, and return value or the default."""
  856. attr = operation.output.action
  857. if attr is not None:
  858. return attr
  859. targetNamespace = operation.getPortType().getWSDL().targetNamespace
  860. ptName = operation.getPortType().name
  861. msgName = operation.output.name
  862. if not msgName:
  863. msgName = operation.name + 'Response'
  864. if targetNamespace.endswith('/'):
  865. return '%s%s/%s' %(targetNamespace, ptName, msgName)
  866. return '%s/%s/%s' %(targetNamespace, ptName, msgName)
  867. def FindExtensions(object, kind, t_type=type(())):
  868. if isinstance(kind, t_type):
  869. result = []
  870. namespaceURI, name = kind
  871. return [ item for item in object.extensions
  872. if hasattr(item, 'nodeType') \
  873. and DOM.nsUriMatch(namespaceURI, item.namespaceURI) \
  874. and item.name == name ]
  875. return [ item for item in object.extensions if isinstance(item, kind) ]
  876. def FindExtension(object, kind, t_type=type(())):
  877. if isinstance(kind, t_type):
  878. namespaceURI, name = kind
  879. for item in object.extensions:
  880. if hasattr(item, 'nodeType') \
  881. and DOM.nsUriMatch(namespaceURI, item.namespaceURI) \
  882. and item.name == name:
  883. return item
  884. else:
  885. for item in object.extensions:
  886. if isinstance(item, kind):
  887. return item
  888. return None
  889. class SOAPCallInfo:
  890. """SOAPCallInfo captures the important binding information about a
  891. SOAP operation, in a structure that is easier to work with than
  892. raw WSDL structures."""
  893. def __init__(self, methodName):
  894. self.methodName = methodName
  895. self.inheaders = []
  896. self.outheaders = []
  897. self.inparams = []
  898. self.outparams = []
  899. self.retval = None
  900. encodingStyle = DOM.NS_SOAP_ENC
  901. documentation = ''
  902. soapAction = None
  903. transport = None
  904. namespace = None
  905. location = None
  906. use = 'encoded'
  907. style = 'rpc'
  908. def addInParameter(self, name, type, namespace=None, element_type=0):
  909. """Add an input parameter description to the call info."""
  910. parameter = ParameterInfo(name, type, namespace, element_type)
  911. self.inparams.append(parameter)
  912. return parameter
  913. def addOutParameter(self, name, type, namespace=None, element_type=0):
  914. """Add an output parameter description to the call info."""
  915. parameter = ParameterInfo(name, type, namespace, element_type)
  916. self.outparams.append(parameter)
  917. return parameter
  918. def setReturnParameter(self, name, type, namespace=None, element_type=0):
  919. """Set the return parameter description for the call info."""
  920. parameter = ParameterInfo(name, type, namespace, element_type)
  921. self.retval = parameter
  922. return parameter
  923. def addInHeaderInfo(self, name, type, namespace, element_type=0,
  924. mustUnderstand=0):
  925. """Add an input SOAP header description to the call info."""
  926. headerinfo = HeaderInfo(name, type, namespace, element_type)
  927. if mustUnderstand:
  928. headerinfo.mustUnderstand = 1
  929. self.inheaders.append(headerinfo)
  930. return headerinfo
  931. def addOutHeaderInfo(self, name, type, namespace, element_type=0,
  932. mustUnderstand=0):
  933. """Add an output SOAP header description to the call info."""
  934. headerinfo = HeaderInfo(name, type, namespace, element_type)
  935. if mustUnderstand:
  936. headerinfo.mustUnderstand = 1
  937. self.outheaders.append(headerinfo)
  938. return headerinfo
  939. def getInParameters(self):
  940. """Return a sequence of the in parameters of the method."""
  941. return self.inparams
  942. def getOutParameters(self):
  943. """Return a sequence of the out parameters of the method."""
  944. return self.outparams
  945. def getReturnParameter(self):
  946. """Return param info about the return value of the method."""
  947. return self.retval
  948. def getInHeaders(self):
  949. """Return a sequence of the in headers of the method."""
  950. return self.inheaders
  951. def getOutHeaders(self):
  952. """Return a sequence of the out headers of the method."""
  953. return self.outheaders
  954. class ParameterInfo:
  955. """A ParameterInfo object captures parameter binding information."""
  956. def __init__(self, name, type, namespace=None, element_type=0):
  957. if element_type:
  958. self.element_type = 1
  959. if namespace is not None:
  960. self.namespace = namespace
  961. self.name = name
  962. self.type = type
  963. element_type = 0
  964. namespace = None
  965. default = None
  966. class HeaderInfo(ParameterInfo):
  967. """A HeaderInfo object captures SOAP header binding information."""
  968. def __init__(self, name, type, namespace, element_type=None):
  969. ParameterInfo.__init__(self, name, type, namespace, element_type)
  970. mustUnderstand = 0
  971. actor = None
  972. def callInfoFromWSDL(port, name):
  973. """Return a SOAPCallInfo given a WSDL port and operation name."""
  974. wsdl = port.getService().getWSDL()
  975. binding = port.getBinding()
  976. portType = binding.getPortType()
  977. operation = portType.operations[name]
  978. opbinding = binding.operations[name]
  979. messages = wsdl.messages
  980. callinfo = SOAPCallInfo(name)
  981. addrbinding = port.getAddressBinding()
  982. if not isinstance(addrbinding, SoapAddressBinding):
  983. raise ValueError, 'Unsupported binding type.'
  984. callinfo.location = addrbinding.location
  985. soapbinding = binding.findBinding(SoapBinding)
  986. if soapbinding is None:
  987. raise ValueError, 'Missing soap:binding element.'
  988. callinfo.transport = soapbinding.transport
  989. callinfo.style = soapbinding.style or 'document'
  990. soap_op_binding = opbinding.findBinding(SoapOperationBinding)
  991. if soap_op_binding is not None:
  992. callinfo.soapAction = soap_op_binding.soapAction
  993. callinfo.style = soap_op_binding.style or callinfo.style
  994. parameterOrder = operation.parameterOrder
  995. if operation.input is not None:
  996. message = messages[operation.input.message]
  997. msgrole = opbinding.input
  998. mime = msgrole.findBinding(MimeMultipartRelatedBinding)
  999. if mime is not None:
  1000. raise ValueError, 'Mime bindings are not supported.'
  1001. else:
  1002. for item in msgrole.findBindings(SoapHeaderBinding):
  1003. part = messages[item.message].parts[item.part]
  1004. header = callinfo.addInHeaderInfo(
  1005. part.name,
  1006. part.element or part.type,
  1007. item.namespace,
  1008. element_type = part.element and 1 or 0
  1009. )
  1010. header.encodingStyle = item.encodingStyle
  1011. body = msgrole.findBinding(SoapBodyBinding)
  1012. if body is None:
  1013. raise ValueError, 'Missing soap:body binding.'
  1014. callinfo.encodingStyle = body.encodingStyle
  1015. callinfo.namespace = body.namespace
  1016. callinfo.use = body.use
  1017. if body.parts is not None:
  1018. parts = []
  1019. for name in body.parts:
  1020. parts.append(message.parts[name])
  1021. else:
  1022. parts = message.parts.values()
  1023. for part in parts:
  1024. callinfo.addInParameter(
  1025. part.name,
  1026. part.element or part.type,
  1027. element_type = part.element and 1 or 0
  1028. )
  1029. if operation.output is not None:
  1030. try:
  1031. message = messages[operation.output.message]
  1032. except KeyError:
  1033. if self.strict:
  1034. raise RuntimeError(
  1035. "Recieved message not defined in the WSDL schema: %s" %
  1036. operation.output.message)
  1037. else:
  1038. message = wsdl.addMessage(operation.output.message)
  1039. print "Warning:", \
  1040. "Recieved message not defined in the WSDL schema.", \
  1041. "Adding it."
  1042. print "Message:", operation.output.message
  1043. msgrole = opbinding.output
  1044. mime = msgrole.findBinding(MimeMultipartRelatedBinding)
  1045. if mime is not None:
  1046. raise ValueError, 'Mime bindings are not supported.'
  1047. else:
  1048. for item in msgrole.findBindings(SoapHeaderBinding):
  1049. part = messages[item.message].parts[item.part]
  1050. header = callinfo.addOutHeaderInfo(
  1051. part.name,
  1052. part.element or part.type,
  1053. item.namespace,
  1054. element_type = part.element and 1 or 0
  1055. )
  1056. header.encodingStyle = item.encodingStyle
  1057. body = msgrole.findBinding(SoapBodyBinding)
  1058. if body is None:
  1059. raise ValueError, 'Missing soap:body binding.'
  1060. callinfo.encodingStyle = body.encodingStyle
  1061. callinfo.namespace = body.namespace
  1062. callinfo.use = body.use
  1063. if body.parts is not None:
  1064. parts = []
  1065. for name in body.parts:
  1066. parts.append(message.parts[name])
  1067. else:
  1068. parts = message.parts.values()
  1069. if parts:
  1070. # XXX no idea what this is for, but it breaks everything. jrb
  1071. #callinfo.setReturnParameter(
  1072. # parts[0].name,
  1073. # parts[0].element or parts[0].type,
  1074. # element_type = parts[0].element and 1 or 0
  1075. # )
  1076. #for part in parts[1:]:
  1077. for part in parts:
  1078. callinfo.addOutParameter(
  1079. part.name,
  1080. part.element or part.type,
  1081. element_type = part.element and 1 or 0
  1082. )
  1083. return callinfo