Package gavo :: Package web :: Module wsdl
[frames] | no frames]

Source Code for Module gavo.web.wsdl

  1  """ 
  2  Code to expose our services via SOAP and WSDL. 
  3  """ 
  4   
  5  #c Copyright 2008-2019, the GAVO project 
  6  #c 
  7  #c This program is free software, covered by the GNU GPL.  See the 
  8  #c COPYING file in the source distribution. 
  9   
 10   
 11  import ZSI 
 12  from ZSI import TC 
 13   
 14  from gavo import base 
 15  from gavo.base import valuemappers 
 16  from gavo.utils.stanxml import (Element, schemaURL, registerPrefix) 
 17   
 18   
 19  SOAPNamespace = 'http://schemas.xmlsoap.org/wsdl/soap/' 
 20  HTTPNamespace = 'http://schemas.xmlsoap.org/wsdl/http/' 
 21  MIMENamespace = 'http://schemas.xmlsoap.org/wsdl/mime/' 
 22  WSDLNamespace = 'http://schemas.xmlsoap.org/wsdl/' 
 23  XSDNamespace = "http://www.w3.org/2001/XMLSchema" 
 24   
 25  registerPrefix("soap", SOAPNamespace, 
 26          schemaURL("wsdlsoap-1.1.xsd")) 
 27  registerPrefix("http", HTTPNamespace, 
 28          schemaURL("wsdlhttp-1.1.xsd")) 
 29  registerPrefix("mime", MIMENamespace, 
 30          schemaURL("wsdlmime-1.1.xsd")) 
 31  registerPrefix("wsdl", WSDLNamespace, 
 32          schemaURL("wsdl-1.1.xsd")) 
 33  registerPrefix("xsd", XSDNamespace, 
 34          schemaURL("XMLSchema.xsd")) 
 35   
 36   
 37   
38 -class WSDL(object):
39 """is a container for elements from the wsdl 1.1 schema. 40 """
41 - class WSDLElement(Element):
42 _prefix = "wsdl"
43
44 - class _tParam(WSDLElement):
45 _a_message = None 46 _a_name = None
47
48 - class binding(WSDLElement):
49 _a_name = None 50 _a_type = None
51
52 - class definitions(WSDLElement):
53 _additionalPrefixes = frozenset(["xsi"]) 54 _a_name = None 55 _a_targetNamespace = None 56 _a_xmlns_tns = None 57 _name_a_xmlns_tns = "xmlns:tns" 58 _a_xmlns_xsd = XSDNamespace 59 _name_a_xmlns_xsd = "xmlns:xsd"
60
61 - class documentation(WSDLElement): pass
62
63 - class fault(WSDLElement):
64 _a_name = None
65
66 - class import_(WSDLElement):
67 name_ = "import" 68 _a_location = None 69 _a_namespace = None
70
71 - class input(_tParam):
72 _mayBeEmpty = True
73
74 - class message(WSDLElement):
75 _a_name = None
76
77 - class operation(WSDLElement):
78 _a_name = None 79 _a_parameterOrder = None
80
81 - class output(_tParam):
82 _mayBeEmpty = True 83 _a_name = None 84 _a_message = None
85
86 - class part(WSDLElement):
87 _mayBeEmpty = True 88 _a_name = None 89 _a_type = None
90
91 - class port(WSDLElement):
92 _mayBeEmpty = True 93 _a_binding = None 94 _a_name = None
95
96 - class portType(WSDLElement):
97 _a_name = None
98
99 - class service(WSDLElement):
100 _a_name = None
101
102 - class types(WSDLElement): pass
103 104
105 -class SOAP(object):
106 - class SOAPElement(Element):
107 _prefix = "soap"
108
109 - class binding(SOAPElement):
110 _mayBeEmpty = True 111 _a_style = "rpc" 112 _a_transport = "http://schemas.xmlsoap.org/soap/http"
113
114 - class body(SOAPElement):
115 _mayBeEmpty = True 116 _a_use = "encoded" 117 _a_namespace = None 118 _a_encodingStyle = "http://schemas.xmlsoap.org/soap/encoding"
119
120 - class operation(SOAPElement):
121 _a_name = None 122 _a_soapAction = None 123 _a_style = "rpc"
124
125 - class address(SOAPElement):
126 _mayBeEmpty = True 127 _a_location = None
128 129
130 -class XSD(object):
131 """is a container for elements from XML schema. 132 """
133 - class XSDElement(Element):
134 _prefix = "xsd" 135 _local = True
136
137 - class schema(XSDElement):
140
141 - class element(XSDElement):
142 _mayBeEmpty = True 143 _a_name = None 144 _a_type = None
145
146 - class complexType(XSDElement):
147 _a_name = None
148
149 - class all(XSDElement): pass
150
151 - class list(XSDElement):
152 _mayBeEmpty = True 153 _a_itemType = None
154
155 - class simpleType(XSDElement):
156 _a_name = None
157 158
159 -def makeTypesForService(service, queryMeta):
160 """returns stanxml definitions for the (SOAP) type of service. 161 162 Only "atomic" input parameters are supported so far, so we can 163 skip those. The output type is always called outList and contains 164 of outRec elements. 165 """ 166 return WSDL.types[ 167 XSD.schema(targetNamespace=base.getMetaText(service, "identifier"))[ 168 XSD.element(name="outRec")[ 169 XSD.complexType[ 170 XSD.all[[ 171 XSD.element(name=f.name, type=base.sqltypeToXSD( 172 f.type))[ 173 WSDL.documentation[f.description], 174 WSDL.documentation[f.unit]] 175 for f in service.getCurOutputFields(queryMeta)]]]], 176 XSD.element(name="outList")[ 177 XSD.simpleType[ 178 XSD.list(itemType="outRec")]]]]
179 180
181 -def makeMessagesForService(service):
182 """returns stanxml definitions for the SOAP messages exchanged when 183 using the service. 184 185 Basically, the input message (called srvInput) consists of some 186 combination of the service's input fields, the output message 187 (called srvOutput) is just an outArr. 188 """ 189 return [ 190 WSDL.message(name="srvInput")[[ 191 WSDL.part(name=f.name, type="xsd:"+base.sqltypeToXSD( 192 f.type))[ 193 WSDL.documentation[f.description], 194 WSDL.documentation[f.unit]] 195 for f in service.getInputKeysFor("soap")]], 196 WSDL.message(name="srvOutput")[ 197 WSDL.part(name="srvOutput", type="tns:outList")]]
198 199
200 -def makePortTypeForService(service):
201 """returns stanxml for a port type named serviceSOAP. 202 """ 203 parameterOrder = " ".join([f.name 204 for f in service.getInputKeysFor("soap")]) 205 return WSDL.portType(name="serviceSOAP")[ 206 WSDL.operation(name="useService", parameterOrder=parameterOrder) [ 207 WSDL.input(name="inPars", message="tns:srvInput"), 208 WSDL.output(name="outPars", message="tns:srvOutput"), 209 # XXX TODO: Define fault 210 ]]
211 212
213 -def makeSOAPBindingForService(service):
214 """returns stanxml for a SOAP binding of service. 215 """ 216 tns = base.getMetaText(service, "identifier") 217 return WSDL.binding(name="soapBinding", type="tns:serviceSOAP")[ 218 SOAP.binding, 219 WSDL.operation(name="useService")[ 220 SOAP.operation(soapAction="", name="useService"), 221 WSDL.input(name="inPars")[ 222 SOAP.body(use="encoded", namespace=tns)], 223 WSDL.output(name="inPars")[ 224 SOAP.body(use="encoded", namespace=tns)], 225 ] 226 ]
227 228
229 -def makeSOAPServiceForService(service):
230 """returns stanxml for a WSDL service definition of the SOAP interface 231 to service. 232 """ 233 shortName = base.getMetaText(service, "shortName") 234 return WSDL.service(name=shortName)[ 235 WSDL.port(name="soap_%s"%shortName, binding="tns:soapBinding")[ 236 SOAP.address(location=service.getURL("soap")), 237 ] 238 ]
239 240
241 -def makeSOAPWSDLForService(service, queryMeta):
242 """returns an stanxml definitions element describing service. 243 244 The definitions element also introduces a namespace named after the 245 ivoa id of the service, accessible through the tns prefix. 246 """ 247 serviceId = base.getMetaText(service, "identifier") 248 return WSDL.definitions(targetNamespace=serviceId, 249 xmlns_tns=serviceId, 250 name="%s_wsdl"%base.getMetaText(service, "shortName").replace(" ", "_"))[ 251 WSDL.import_, 252 makeTypesForService(service, queryMeta), 253 makeMessagesForService(service), 254 makePortTypeForService(service), 255 makeSOAPBindingForService(service), 256 makeSOAPServiceForService(service), 257 ]
258 259
260 -class ToTcConverter(base.FromSQLConverter):
261 """is a quick and partial converter from SQL types to ZSI's type codes. 262 """ 263 typeSystem = "ZSITypeCodes" 264 simpleMap = { 265 "smallint": TC.Integer, 266 "integer": TC.Integer, 267 "int": TC.Integer, 268 "bigint": TC.Integer, 269 "real": TC.FPfloat, 270 "float": TC.FPfloat, 271 "boolean": ("boolean", "1"), 272 "double precision": TC.FPdouble, 273 "double": TC.FPdouble, 274 "text": TC.String, 275 "char": TC.String, 276 "date": TC.gDate, 277 "timestamp": TC.gDateTime, 278 "time": TC.gTime, 279 "raw": TC.String, 280 } 281
282 - def mapComplex(self, type, length):
283 if type in self._charTypes: 284 return TC.String
285 286 sqltypeToTC = ToTcConverter().convert 287 288 289 # rather than fooling around with ZSI.SoapWriter's serialization, I use 290 # the machinery used for VOTables and HTML to serialize weird values. 291 # It's in place anyway. 292 293 _wsdlMFRegistry = valuemappers.ValueMapperFactoryRegistry() 294 _registerMF = _wsdlMFRegistry.registerFactory 295 296
297 -def datetimeMapperFactory(colProps):
298 """returns mapper for datetime objects to python time tuples. 299 """ 300 if colProps["dbtype"] in ("date", "datetime"): 301 def mapper(val): 302 return val.timetuple()
303 return mapper 304 _registerMF(datetimeMapperFactory) 305
306 -def serializePrimaryTable(data, service):
307 """returns a SOAP serialization of the DataSet data's primary table. 308 """ 309 table = data.getPrimaryTable() 310 tns = base.getMetaText(service, "identifier") 311 class Row(TC.Struct): 312 def __init__(self): 313 TC.Struct.__init__(self, None, [ 314 sqltypeToTC(f.type)(pname=(tns, f.name)) 315 for f in table.tableDef], 316 pname=(tns, "outRow"))
317 318 class Table(list): 319 typecode = TC.Array((tns, 'outRow'), Row(), 320 pname=(tns, 'outList')) 321 322 mapped = Table( 323 base.SerManager(table, mfRegistry=_wsdlMFRegistry).getMappedValues()) 324 sw = ZSI.SoapWriter(nsdict={"tns": tns}) 325 sw.serialize(mapped).close() 326 return str(sw) 327 328
329 -def unicodeXML(obj):
330 """returns an XML-clean version of obj's unicode representation. 331 332 I'd expect ZSI to worry about this, but clearly they don't. 333 """ 334 return unicode(obj 335 ).replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
336 337
338 -def formatFault(exc, service):
339 if isinstance(exc, base.ValidationError): 340 val = ZSI.Fault(ZSI.Fault.Client, unicodeXML(exc)) 341 else: 342 val = ZSI.Fault(ZSI.Fault.Server, unicodeXML(exc)) 343 return val.AsSOAP( 344 nsdict={"tns": base.getMetaText(service, "identifier")})
345