soap: handle recursive complexType (#81643)
Reference to already converted complexType are converted to JSON schema references.
This commit is contained in:
parent
117743e0a6
commit
8266740b52
|
@ -15,6 +15,7 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import collections
|
||||
import hashlib
|
||||
|
||||
import zeep
|
||||
import zeep.helpers
|
||||
|
@ -184,42 +185,61 @@ class SOAPConnector(BaseResource, HTTPResource):
|
|||
operations_and_schemas.append((name, input_schema, output_schema))
|
||||
return operations_and_schemas
|
||||
|
||||
def type2schema(self, xsd_type, keep_root=False, compress=False):
|
||||
# simplify schema: when a type contains a unique element, it will try
|
||||
# to match any dict or list with it on input and will flatten the
|
||||
# schema on output.
|
||||
if (
|
||||
isinstance(xsd_type, zeep.xsd.ComplexType)
|
||||
and len(xsd_type.elements) == 1
|
||||
and not keep_root
|
||||
and compress
|
||||
):
|
||||
if xsd_type.elements[0][1].max_occurs != 1:
|
||||
@classmethod
|
||||
def type2schema(cls, xsd_type, keep_root=False, compress=False):
|
||||
seen = set()
|
||||
|
||||
def to_id(s):
|
||||
return f'ref-{hashlib.md5(str(s).encode()).hexdigest()}'
|
||||
|
||||
def t2s(xsd_type):
|
||||
type_name = xsd_type.qname or xsd_type.name
|
||||
if isinstance(xsd_type, zeep.xsd.ComplexType):
|
||||
if type_name in seen:
|
||||
return {'$ref': '#' + to_id(type_name)}
|
||||
seen.add(type_name)
|
||||
# simplify schema: when a type contains a unique element, it will try
|
||||
# to match any dict or list with it on input and will flatten the
|
||||
# schema on output.
|
||||
if (
|
||||
isinstance(xsd_type, zeep.xsd.ComplexType)
|
||||
and len(xsd_type.elements) == 1
|
||||
and not keep_root
|
||||
and compress
|
||||
# and is not recursive
|
||||
and xsd_type.elements[0][1].type != xsd_type
|
||||
):
|
||||
if xsd_type.elements[0][1].max_occurs != 1:
|
||||
schema = {
|
||||
'type': 'array',
|
||||
'items': t2s(xsd_type.elements[0][1].type),
|
||||
}
|
||||
else:
|
||||
schema = t2s(xsd_type.elements[0][1].type)
|
||||
elif isinstance(xsd_type, zeep.xsd.ComplexType):
|
||||
properties = collections.OrderedDict()
|
||||
schema = {
|
||||
'type': 'array',
|
||||
'items': self.type2schema(xsd_type.elements[0][1].type, compress=compress),
|
||||
'type': 'object',
|
||||
'properties': properties,
|
||||
'$anchor': to_id(type_name),
|
||||
}
|
||||
for key, element in xsd_type.elements:
|
||||
if element.min_occurs > 0:
|
||||
schema.setdefault('required', []).append(key)
|
||||
element_schema = t2s(element.type)
|
||||
if element.max_occurs == 'unbounded' or element.max_occurs > 1:
|
||||
element_schema = {'type': 'array', 'items': element_schema}
|
||||
properties[key] = element_schema
|
||||
if not properties:
|
||||
schema = {'type': 'null'}
|
||||
elif isinstance(xsd_type, zeep.xsd.BuiltinType):
|
||||
schema = {'type': 'string'}
|
||||
else:
|
||||
schema = self.type2schema(xsd_type.elements[0][1].type, compress=compress)
|
||||
elif isinstance(xsd_type, zeep.xsd.ComplexType):
|
||||
properties = collections.OrderedDict()
|
||||
schema = {
|
||||
'type': 'object',
|
||||
'properties': properties,
|
||||
}
|
||||
for key, element in xsd_type.elements:
|
||||
if element.min_occurs > 0:
|
||||
schema.setdefault('required', []).append(key)
|
||||
element_schema = self.type2schema(element.type, compress=compress)
|
||||
if element.max_occurs == 'unbounded' or element.max_occurs > 1:
|
||||
element_schema = {'type': 'array', 'items': element_schema}
|
||||
properties[key] = element_schema
|
||||
if not properties:
|
||||
schema = {'type': 'null'}
|
||||
elif isinstance(xsd_type, zeep.xsd.BuiltinType):
|
||||
schema = {'type': 'string'}
|
||||
else:
|
||||
schema = {}
|
||||
if xsd_type.qname:
|
||||
schema['description'] = str(xsd_type.qname).replace('{http://www.w3.org/2001/XMLSchema}', 'xsd:')
|
||||
return schema
|
||||
schema = {}
|
||||
if xsd_type.qname:
|
||||
schema['description'] = str(xsd_type.qname).replace(
|
||||
'{http://www.w3.org/2001/XMLSchema}', 'xsd:'
|
||||
)
|
||||
return schema
|
||||
|
||||
return t2s(xsd_type)
|
||||
|
|
|
@ -109,8 +109,10 @@ xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
|
|||
</soap:Body>
|
||||
</soap:Envelope>'''
|
||||
INPUT_SCHEMA = {
|
||||
'$anchor': 'ref-6adf97f83acf6453d4a6a4b1070f3754',
|
||||
'properties': {
|
||||
'firstName': {
|
||||
'$anchor': 'ref-dbd3a37522045c54032a5b96864a500d',
|
||||
'description': '{http://www.examples.com/wsdl/HelloService.wsdl}firstName',
|
||||
'properties': {
|
||||
'string': {'items': {'type': 'string', 'description': 'xsd:string'}, 'type': 'array'},
|
||||
|
@ -124,6 +126,7 @@ xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
|
|||
'type': 'object',
|
||||
}
|
||||
OUTPUT_SCHEMA = {
|
||||
'$anchor': 'ref-6adf97f83acf6453d4a6a4b1070f3754',
|
||||
'properties': {
|
||||
'greeting': {'type': 'string', 'description': 'xsd:string'},
|
||||
'who': {'type': 'string', 'description': 'xsd:string'},
|
||||
|
@ -157,6 +160,11 @@ class SOAP12(SOAP11):
|
|||
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:tns="urn:examples:helloservice"
|
||||
targetNamespace="urn:examples:helloservice">
|
||||
<xsd:complexType name="recurse">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="anotherme" type="tns:recurse" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<xsd:element name="sayHello">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
|
@ -170,6 +178,7 @@ class SOAP12(SOAP11):
|
|||
<xsd:sequence>
|
||||
<xsd:element name="greeting" type="xsd:string"/>
|
||||
<xsd:element name="who" type="xsd:string" maxOccurs="unbounded"/>
|
||||
<xsd:element name="recursion" type="tns:recurse" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
@ -223,10 +232,12 @@ class SOAP12(SOAP11):
|
|||
<sayHelloResponse xmlns="urn:examples:helloservice">
|
||||
<greeting>Hello</greeting>
|
||||
<who>John!</who>
|
||||
<recursion><anotherme/></recursion>
|
||||
</sayHelloResponse>
|
||||
</soap:Body>
|
||||
</soap:Envelope>'''
|
||||
INPUT_SCHEMA = {
|
||||
'$anchor': 'ref-5b712371a9c9bf61f983831c2ed3f364',
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'firstName': {'type': 'array', 'items': {'type': 'string', 'description': 'xsd:string'}},
|
||||
|
@ -236,6 +247,7 @@ class SOAP12(SOAP11):
|
|||
'description': '{urn:examples:helloservice}sayHello',
|
||||
}
|
||||
OUTPUT_SCHEMA = {
|
||||
'$anchor': 'ref-5e505e086d14d5417f2799da5c085712',
|
||||
'description': '{urn:examples:helloservice}sayHelloResponse',
|
||||
'properties': {
|
||||
'greeting': {'type': 'string', 'description': 'xsd:string'},
|
||||
|
@ -243,6 +255,16 @@ class SOAP12(SOAP11):
|
|||
'type': 'array',
|
||||
'items': {'type': 'string', 'description': 'xsd:string'},
|
||||
},
|
||||
'recursion': {
|
||||
'$anchor': 'ref-63d3d62358d2daf62cd2ebd07640165e',
|
||||
'description': '{urn:examples:helloservice}recurse',
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'anotherme': {
|
||||
'$ref': '#ref-63d3d62358d2daf62cd2ebd07640165e',
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
'required': ['greeting', 'who'],
|
||||
'type': 'object',
|
||||
|
@ -255,6 +277,7 @@ class SOAP12(SOAP11):
|
|||
OUTPUT_DATA = {
|
||||
'greeting': 'Hello',
|
||||
'who': ['John!'],
|
||||
'recursion': {'anotherme': None},
|
||||
}
|
||||
VALIDATION_ERROR = 'Expected at least 1 items (minOccurs check) 0 items found. (sayHello.firstName)'
|
||||
|
||||
|
|
Loading…
Reference in New Issue