Commits

Anonymous committed a96a57e

Adde auto service generation for Exchange ill-formed WSDL

Comments (0)

Files changed (2)

   * Allow to <choice/> elements act as params
   * Added params check while method execution
   * Added <Fault/> with http 200 status workaround
+  * Added auto service generation if no one exists in Exchange's WSDL
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the (LGPL) GNU Lesser General Public License as
-# published by the Free Software Foundation; either version 3 of the 
+# published by the Free Software Foundation; either version 3 of the
 # License, or (at your option) any later version.
 #
 # This program is distributed in the hope that it will be useful,
 
     tags =\
     {
-        'import' : lambda x,y: Import(x,y), 
-        'types' : lambda x,y: Types(x,y), 
-        'message' : lambda x,y: Message(x,y), 
+        'import' : lambda x,y: Import(x,y),
+        'types' : lambda x,y: Types(x,y),
+        'message' : lambda x,y: Message(x,y),
         'portType' : lambda x,y: PortType(x,y),
         'binding' : lambda x,y: Binding(x,y),
         'service' : lambda x,y: Service(x,y),
     }
-    
+
     @classmethod
     def create(cls, root, definitions):
         """
         @param definitions: A definitions object.
         @type definitions: L{Definitions}
         @return: The created object.
-        @rtype: L{WObject} 
+        @rtype: L{WObject}
         """
         fn = cls.tags.get(root.name)
         if fn is not None:
     @ivar root: The XML I{root} element.
     @type root: L{Element}
     """
-    
+
     def __init__(self, root, definitions=None):
         """
         @param root: An XML root element.
         pmd.excludes = ['root']
         pmd.wrappers = dict(qname=lambda x: repr(x))
         self.__metadata__.__print__ = pmd
-        
+
     def resolve(self, definitions):
         """
         Resolve named references to other WSDL objects.
         """
         pass
 
-        
+
 class NamedObject(WObject):
     """
     A B{named} WSDL object.
     @ivar service: The service object.
     @type service: L{Service}
     """
-    
+
     Tag = 'definitions'
 
     def __init__(self, url, options):
         self.resolve()
         self.build_schema()
         self.set_wrapped()
+
+        self._ensure_service()
         for s in self.services:
             self.add_methods(s)
+
         log.debug("wsdl at '%s' loaded:\n%s", url, self)
-        
+
+    def _ensure_service(self):
+        """Trying to add service if no one definied explicitly. EWS workaround."""
+        if self.services\
+            or not self.bindings\
+            or len(self.bindings) > 1\
+            or not self.bindings.keys()[0][0].startswith('Exchange'):
+            return
+
+        service = Element('service')
+        service.set('name', 'ExchangeServices')
+
+        port = Element('port', service)
+        port.set('name', 'ExchangeServicePort')
+        port.set('binding', self.bindings.keys()[0][0])
+
+        address = Element('address', port)
+        address.set('location', urljoin(self.url, 'exchange.asmx'))
+
+        port.append(address)
+        service.append(port)
+
+        service = Factory.create(service, self)
+        service.resolve(self)
+
+        self.children.append(service)
+        self.services.append(service)
+
+        log.debug('Auto created service: %s', service)
+
     def mktns(self, root):
         """ Get/create the target namespace """
         tns = root.get('targetNamespace')
             log.debug('warning: tns (%s), not mapped to prefix', tns)
             prefix = 'tns'
         return (prefix, tns)
-        
+
     def add_children(self, root):
         """ Add child objects using the factory """
         for c in root.getChildren(ns=wsdlns):
             if isinstance(child, Service):
                 self.services.append(child)
                 continue
-                
+
     def open_imports(self):
         """ Import the I{imported} WSDLs. """
         for imp in self.imports:
             base = self.url
             imp.load(self)
-                
+
     def resolve(self):
         """ Tell all children to resolve themselves """
         for c in self.children:
             c.resolve(self)
-                
+
     def build_schema(self):
         """ Process L{Types} objects and create the schema collection """
         container = SchemaCollection(self)
         for s in [t.schema() for t in self.types if t.imported()]:
             self.schema.merge(s)
         return self.schema
-                
+
     def add_methods(self, service):
         """ Build method view for service """
         bindings = {
                 m.binding.output = bindings.get(key)
                 op = ptype.operation(name)
                 p.methods[name] = m
-                
+
     def set_wrapped(self):
         """ set (wrapped|bare) flag on messages """
         for b in self.bindings.values():
                         if resolved.builtin():
                             continue
                         body.wrapped = True
-            
+
 
 
 class Import(WObject):
     @ivar imported: The imported object.
     @type imported: L{Definitions}
     """
-    
+
     def __init__(self, root, definitions):
         """
         @param root: An XML root element.
         self.imported = None
         pmd = self.__metadata__.__print__
         pmd.wrappers['imported'] = ( lambda x: x.id )
-        
+
     def load(self, definitions):
         """ Load the object by opening the URL """
         url = self.location
             self.import_schema(definitions, d)
             return
         raise Exception('document at "%s" is unknown' % url)
-    
+
     def import_definitions(self, definitions, d):
         """ import/merge wsdl definitions """
         definitions.types += d.types
         definitions.bindings.update(d.bindings)
         self.imported = d
         log.debug('imported (WSDL):\n%s', d)
-        
+
     def import_schema(self, definitions, d):
         """ import schema as <types/> content """
         if not len(definitions.types):
             types = definitions.types[:-1]
         types.root.append(d.root)
         log.debug('imported (XSD):\n%s', d.root)
-   
+
     def __gt__(self, other):
         return False
-        
+
 
 class Types(WObject):
     """
     Represents <types><schema/></types>.
     """
-    
+
     @classmethod
     def create(cls, definitions):
         root = Element('types', ns=wsdlns)
         """
         WObject.__init__(self, root, definitions)
         self.definitions = definitions
-        
+
     def contents(self):
         return self.root.getChildren('schema', Namespace.xsdns)
-    
+
     def schema(self):
         return self.definitions.schema
-    
+
     def local(self):
         return ( self.definitions.schema is None )
-    
+
     def imported(self):
         return ( not self.local() )
-        
+
     def __gt__(self, other):
         return isinstance(other, Import)
-    
+
 
 class Part(NamedObject):
     """
         tns = definitions.tns
         self.element = self.__getref('element', tns)
         self.type = self.__getref('type', tns)
-        
+
     def __getref(self, a, tns):
         """ Get the qualified value of attribute named 'a'."""
         s = self.root.get(a)
         if s is None:
             return s
         else:
-            return qualify(s, self.root, tns)  
+            return qualify(s, self.root, tns)
 
 
 class Message(NamedObject):
         for p in root.getChildren('part'):
             part = Part(p, definitions)
             self.parts.append(part)
-            
+
     def __gt__(self, other):
         return isinstance(other, (Import, Types))
-    
-    
+
+
 class PortType(NamedObject):
     """
     Represents <portType/>.
                 faults.append(f)
             op.faults = faults
             self.operations[op.name] = op
-            
+
     def resolve(self, definitions):
         """
         Resolve named references to other WSDL objects.
                 if msg is None:
                     raise Exception, "msg '%s', not-found" % f.message
                 f.message = msg
-                
+
     def operation(self, name):
         """
         Shortcut used to get a contained operation by name.
             return self.operations[name]
         except Exception, e:
             raise MethodNotFound(name)
-                
+
     def __gt__(self, other):
         return isinstance(other, (Import, Types, Message))
 
         self.soap = soap
         self.soap.style = sr.get('style', default='document')
         self.add_operations(self.root, definitions)
-        
+
     def soaproot(self):
         """ get the soap:binding """
         for ns in (soapns, soap12ns):
             if sr is not None:
                 return sr
         return None
-        
+
     def add_operations(self, root, definitions):
         """ Add <operation/> children """
         dsop = Element('operation', ns=soapns)
                 faults.append(f)
             soap.faults = faults
             self.operations[op.name] = op
-            
+
     def body(self, definitions, body, root):
         """ add the input/output body properties """
         if root is None:
         else:
             prefix = root.findPrefix(ns, 'b0')
             body.namespace = (prefix, ns)
-            
+
     def header(self, definitions, parent, root):
         """ add the input/output header properties """
         if root is None:
         part = root.get('part')
         if part is not None:
             header.part = part
-            
+
     def resolve(self, definitions):
         """
         Resolve named references to other WSDL objects.  This includes
             self.resolvesoapbody(definitions, op)
             self.resolveheaders(definitions, op)
             self.resolvefaults(definitions, op)
-        
+
     def resolveport(self, definitions):
         """
         Resolve port_type reference.
             raise Exception("portType '%s', not-found" % self.type)
         else:
             self.type = port_type
-            
+
     def resolvesoapbody(self, definitions, op):
         """
-        Resolve soap body I{message} parts by 
+        Resolve soap body I{message} parts by
         cross-referencing with operation defined in port type.
         @param definitions: A definitions object.
         @type definitions: L{Definitions}
             soap.output.body.parts = pts
         else:
             soap.output.body.parts = ptop.output.parts
-            
+
     def resolveheaders(self, definitions, op):
         """
         Resolve soap header I{message} references.
             if pn == header.part:
                 raise Exception, \
                     "message '%s' has not part named '%s'" % (ref, pn)
-                        
+
     def resolvefaults(self, definitions, op):
         """
         Resolve soap fault I{message} references by
                 continue
             raise Exception, \
                 "fault '%s' not defined in portType '%s'" % (fault.name, self.type.name)
-            
+
     def operation(self, name):
         """
         Shortcut used to get a contained operation by name.
             return self.operations[name]
         except:
             raise MethodNotFound(name)
-            
+
     def __gt__(self, other):
         return ( not isinstance(other, Service) )
 
     @ivar location: The service location (url).
     @type location: str
     """
-    
+
     def __init__(self, root, definitions, service):
         """
         @param root: An XML root element.
         address = root.getChild('address')
         self.location = address.get('location').encode('utf-8')
         self.methods = {}
-        
+
     def method(self, name):
         """
         Get a method defined in this portType by name.
         @rtype: I{Method}
         """
         return self.methods.get(name)
-        
+
 
 class Service(NamedObject):
     """
     @ivar methods: The contained methods for all ports.
     @type methods: [Method,..]
     """
-    
+
     def __init__(self, root, definitions):
         """
         @param root: An XML root element.
         for p in root.getChildren('port'):
             port = Port(p, definitions, self)
             self.ports.append(port)
-            
+
     def port(self, name):
         """
         Locate a port by name.
         @param name: A port name.
         @type name: str
         @return: The port object.
-        @rtype: L{Port} 
+        @rtype: L{Port}
         """
         for p in self.ports:
             if p.name == name:
                 return p
         return None
-    
+
     def setlocation(self, url, names=None):
         """
         Override the invocation location (url) for service method.
         for m in self.methods.values():
             if names is None or m.name in names:
                 m.location = url
-        
+
     def resolve(self, definitions):
         """
         Resolve named references to other WSDL objects.
             p.binding = binding
             filtered.append(p)
         self.ports = filtered
-        
+
     def __gt__(self, other):
         return True