Лучший подход для добавления опции для обхода упаковки ComplexType многопараметрических методов - PullRequest
1 голос
/ 10 июля 2020

Недавно мы начали использовать Spyne, и он кажется очень мощным. В нашем случае нам нужно воспроизвести устаревший SOAP API, который мы заменяем. Чтобы обеспечить совместимость с текущими клиентами, мы хотим, чтобы WSDL, сгенерированный spyne, максимально соответствовал текущему WSDL.

Как было отмечено в этом вопросе SO:

Как я могу запретить Spyne обертывать аргументы в complexType?

Spyne оборачивает входящие параметры для методов с несколькими простыми параметрами в структуру ComplexType.

В прежних версиях wsdl, эта операция

<operation name="UCDprovision_password" parameterOrder="p_msisdn p_pin p_password">
    <input name="UCDprovision_passwordRequest" message="impl:UCDprovision_passwordRequest"/>
    <output name="UCDprovision_passwordResponse" message="impl:UCDprovision_passwordResponse"/>
</operation>

связана с этим сообщением:

   <message name="UCDprovision_passwordRequest">
      <part name="p_msisdn" type="xsd:string"/>
      <part name="p_pin" type="xsd:string"/>
      <part name="p_password" type="xsd:string"/>
   </message>

и этим ComplexType:

         <complexType name="ObjResultadoBasico">
            <sequence>
               <element name="PError" type="xsd:int"/>
               <element name="SError" nillable="true" type="xsd:string"/>
            </sequence>
         </complexType>

Мы создали следующий шпионский код для репликации операция:

class ObjResultadoBasico(ComplexModel):
    PError = Integer
    SError = String

class UCDService(ServiceBase):
    @rpc(String, String, String, _returns=ObjResultadoBasico, _in_message_name='UCDprovision_passwordRequest')
    def UCDprovision_password(ctx,p_msisdn,p_pin,p_password):
        return ObjResultadoBasico('1',p_msisdn)



И это дает следующий WSDL (нерелевантные части опущены)

<xs:complexType name="ObjResultadoBasico">
            <xs:sequence>
               <xs:element name="PError" type="xs:integer" minOccurs="0" nillable="true"/>
               <xs:element name="SError" type="xs:string" minOccurs="0" nillable="true"/>
            </xs:sequence>
</xs:complexType>

<xs:complexType name="UCDprovision_passwordRequest">
    <xs:sequence>
        <xs:element name="p_msisdn" type="xs:string" minOccurs="0" nillable="true"/>
        <xs:element name="p_pin" type="xs:string" minOccurs="0" nillable="true"/>
        <xs:element name="p_password" type="xs:string" minOccurs="0" nillable="true"/>
    </xs:sequence>
</xs:complexType>

<wsdl:operation name="UCDprovision_password" parameterOrder="UCDprovision_passwordRequest">
    <wsdl:input name="UCDprovision_passwordRequest" message="tns:UCDprovision_passwordRequest"/>
    <wsdl:output name="UCDprovision_passwordResponse" message="tns:UCDprovision_passwordResponse"/></wsdl:operation>

Разница, над которой мы пытаемся работать, - это ComplexType, оборачивающий UCDprovision_passwordRequest, который был подробно описан в устаревшем WSDL.

РЕДАКТИРОВАТЬ: добавление вывода, когда сообщение запроса предварительно определено в собственном сложном типе, и поэтому сама операция может использовать «голый» стиль, используя код, предложенный Бурак Арслан ниже:

<xs:complexType name="UCDprovision_passwordRequest">
<xs:sequence>
<xs:element name="p_msisdn" type="xs:string" minOccurs="0" nillable="true"/>
<xs:element name="p_pin" type="xs:string" minOccurs="0" nillable="true"/>
<xs:element name="p_password" type="xs:string" minOccurs="0" nillable="true"/>
</xs:sequence>
</xs:complexType>

<xs:element name="UCDprovision_password" type="tns:UCDprovision_passwordRequest"/>


<wsdl:message name="UCDprovision_password">
<wsdl:part name="UCDprovision_password" element="tns:UCDprovision_password"/>
</wsdl:message>

Проблема с этим решением заключается в том, что я не хочу заключать в оболочку не саму операцию, а входное сообщение для этой операции . Я предполагаю, что это результат, потому что входное сообщение UCDprovision_passwordRequest объявлено как ComplexModel.

Используя SOAPUI, это примеры запросов:

Старый WSDL

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ucd="http://UCD">
   <soapenv:Header/>
   <soapenv:Body>
      <ucd:UCDprovision_pin>
         <p_msisdn>?</p_msisdn>
         <p_usuario>?</p_usuario>
         <p_nemonico>?</p_nemonico>
         <p_pin>?</p_pin>
      </ucd:UCDprovision_pin>
   </soapenv:Body>
</soapenv:Envelope>

Spyne WSDL

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ucd="UCD">
   <soapenv:Header/>
   <soapenv:Body>
      <ucd:UCDprovision_pin>
         <ucd:p_msisdn>?</ucd:p_msisdn>
         <ucd:p_usuario>?</ucd:p_usuario>
         <ucd:p_nemonico>?</ucd:p_nemonico>
         <ucd:p_pin>?</ucd:p_pin>
      </ucd:UCDprovision_pin>
   </soapenv:Body>
</soapenv:Envelope>

В конце концов, как вы видите, с точки зрения клиента отличается только пространство имен параметров, и это, вероятно, поправимо (я не не пробовал, как бы вы это сделали?).

Поскольку нам просто нужно принять то, что клиент отправляет со старым WSDL, я протестировал «мягкую» проверку и запросил со старым WSDL принимается кодом Spyne (который генерирует новый WSDL).

Мы продолжим тестирование, но я думаю, что в конце, если это не рекомендуется, я просто приму определения ComplexType и буду использовать «мягкая» валидация, которая решает нашу проблему. Единственный вопрос, как создать WSDL без пространства имен в атрибутах ComplexType

РЕДАКТИРОВАТЬ: следующий текст не имеет значения, если патч не требуется.

Как указано в справочном вопросе выше, если бы у нас был только один in_parameter, мы могли бы использовать _body_type = 'bare' в rp c декоратор, чтобы избежать этого, но это не разрешено в spyne для 0 или более чем 1 параметра. Я уверен, что для этого, конечно, есть веские причины.

Все сказанное, и с целью максимально возможной репликации исходного WSDL мы хотели бы разработать патч, чтобы добавить опцию для этот мультипараметр должен быть представлен как в WSDL. Если возможно, мы также хотели бы внести этот патч в апстрим, если у нас будет достаточно хорошая реализация.

А теперь вопрос: как лучше всего подойти к этому патчу?

В первом обзоре код, в котором создается этот сегмент XML, выглядит следующим образом: в decorator.py:

    message = None
    if body_style_str == 'bare':
        if len(in_params) > 1:
            raise LogicError("body_style='bare' can handle at most one "
                                                           "function argument.")

        if len(in_params) == 0:
            message = ComplexModel.produce(type_name=in_message_name,
                                           namespace=ns, members=in_params)
        else:
            message, = in_params.values()
            message = message.customize(sub_name=in_message_name, sub_ns=ns)

            if issubclass(message, ComplexModelBase) and not message._type_info:
                raise LogicError("body_style='bare' does not allow empty "
                                                               "model as param")

            # there can't be multiple arguments here.
            if message.__type_name__ is ModelBase.Empty:
                message._fill_empty_type_name(ns, in_message_name,
                                                    "%s_arg0" % in_message_name)

    else:
        message = ComplexModel.produce(type_name=in_message_name,
                                       namespace=ns, members=in_params)
        message.__namespace__ = ns

Достаточно ли нам добавить дополнительный флаг в декоратор и использовать его? чтобы создать объект сообщения так, как мы хотим, вместо того, чтобы идти по маршруту ComplexType? А ля:

        if len(in_params) > 1:
            if (parameters_as_message):
                message = message.special_way_to_construct(type_name=in_message_name,
                                           namespace=ns, members=in_params)

            else:
                raise LogicError("body_style='bare' can handle at most one "
                                                           "function argument.")

Спасибо, что дочитали до этого места, и если Бурак Арслан прочитает это, спасибо за всю вашу работу!

1 Ответ

1 голос
/ 13 июля 2020

Голые методы не могут иметь> 1 аргумента, это ограничение SOAP.

Что не так с приведенным ниже кодом?


class ObjResultadoBasico(ComplexModel):
    PError = Integer
    SError = Unicode


class UCDprovision_passwordRequest(ComplexModel):
    p_msisdn = Unicode
    p_pin = Unicode
    p_password = Unicode


class UCDService(ServiceBase):
    @rpc(UCDprovision_passwordRequest, _returns=ObjResultadoBasico,
         _body_style='bare')
    def UCDprovision_password(ctx, passreq):
        # ComplexModel doesn't support *args in its default ctor, use **kwargs
        # or use a custom ctor
        return ObjResultadoBasico(PError=1, SError=passreq.p_msisdn)


...