creating any object with python zeep - python-3.x

i am pretty new to zeep, and soap. i`am trying to make client request to soap ws function. wsdl of function:
-<s:element name="GetRetailTransactions">
-<s:complexType>
-<s:sequence>
-<s:element name="parameters" maxOccurs="1" minOccurs="0">
-<s:complexType mixed="true">
-<s:sequence>
<s:any/>
</s:sequence>
</s:complexType>
</s:element>
</s:sequence>
</s:complexType>
</s:element>
i dont fully understand how to create object with type any in zeep.
i have tried :
wsdl = 'http://domain/app.asmx?WSDL'
client = Client(wsdl=wsdl)
params = {
'RetailTransactionsParameters': {
'GetNotExportedOnly': '0',
'GetNotRetrunsOnly': '0',
'FromDate': '20170518',
'ToDate': '20170518',
'TransactionTypeFilter': {
'TransactionType': '2'
},
},
}
parameters = client.get_element('ns0:GetRetailTransactions')
param = xsd.AnyObject(parameters, parameters(params))
result = client.service.GetRetailTransactions(param)
but i get error:
File "/home/user/lib/python3.6/site-packages/zeep/xsd/elements/indicators.py", line 227, in render
if name in value:
TypeError: argument of type 'AnyObject' is not iterable
on soapui i can make request and successfully get answer with :
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<GetRetailTransactions xmlns="example.com/">
<parameters>
<RetailTransactionsParameters>
<GetNotExportedOnly>0</GetNotExportedOnly>
<GetNotRetrunsOnly>0</GetNotRetrunsOnly>
<FromDate>20170518</FromDate>
<ToDate>20170518</ToDate>
<TransactionTypeFilter>
<TransactionType>2</TransactionType>
</TransactionTypeFilter>
</RetailTransactionsParameters>
</parameters>
</GetRetailTransactions>
</Body>
</Envelope>
maybe someone can guide me how to correctly make such a request with zeep..
thanks in advance

I had the same problem today. The get_element method returns the type. To create the object you need to instantiate it. You can either do:
parameters = client.get_element('ns0:GetRetailTransactions')(params)
or you can explicitly set each property:
parameters = client.get_element('ns0:GetRetailTransactions')()
parameters.GetNotExportedOnly = 0
parameters.GetNotRetrunsOnly = 0
...
or you can pass the dict object and zeep does the conversion to the type
http://docs.python-zeep.org/en/master/datastructures.html#creating-objects

Related

HOW TO BUILD A COMPLEX ARGUMENT FOR A SOAP FUNCTION WITH PYTHON AND ZEEP

I'm connecting to a SOAP v2 webservice with Python3.7 and zeep. One call to create an object requires a complex structure to be passed as an argument. This is the WSDL for the call and it's arguments:
<message name="catalogProductAttributeAddOptionRequest">
<part name="sessionId" type="xsd:string"/>
<part name="attribute" type="xsd:string"/>
<part name="data" type="typens:catalogProductAttributeOptionEntityToAdd"/>
</message>
<complexType name="catalogProductAttributeOptionEntityToAdd">
<all>
<element name="label" type="typens:catalogProductAttributeOptionLabelArray"/>
<element name="order" type="xsd:int"/>
<element name="is_default" type="xsd:int"/>
</all>
</complexType>
<complexType name="catalogProductAttributeOptionLabelArray">
<complexContent>
<restriction base="soapenc:Array">
<attribute ref="soapenc:arrayType" wsdl:arrayType="typens:catalogProductAttributeOptionLabelEntity[]"/>
</restriction>
</complexContent>
</complexType>
<complexType name="catalogProductAttributeOptionLabelEntity">
<all>
<element name="store_id" type="typens:ArrayOfString"/>
<element name="value" type="xsd:string"/>
</all>
</complexType>
The problem that I have is how to pass the "data" argument to the function in Python 3 using zeep. I have an example on how to do this in php:
$label = array (
array(
"store_id" => array("0"),
"value" => "some random data"
)
);
$data = array(
"label" => $label,
"order" => "10",
"is_default" => "1"
);
$orders = $client->catalogProductAttributeAddOption($session, $attributeCode, $data);
This code is supossed to work altough I haven't tested it. So, the $data structure should have the equivalent in a python dict as this one:
data=[
{
"label": [
[
{
"store_id":["0"],
"value":"some random data"
}
]
],
"order":10,
"is_default":1
}
]
And I call the function this way:
client.service.catalogProductAttributeAddOption(sessionId=sessionid,attribute="manufacturer",data=data)
If I do that I receive this exception:
TypeError: Any element received object of type 'list', expected lxml.etree._Element or builtins.dict or zeep.objects.catalogProductAttributeOptionLabelEntity
See http://docs.python-zeep.org/en/master/datastructures.html#any-objects for more information
So I start investigating about the Any elements and I discover a way of casting parts of the structure to its type in the wsdl namespace, so I do this:
entity={
"store_id":["0"],
"value":"some random data"
}
catalogProductAttributeOptionLabelEntity=client.get_type('ns0:catalogProductAttributeOptionLabelEntity')
retyped_entity=catalogProductAttributeOptionLabelEntity(entity)
label=[
retyped_entity
]
catalogProductAttributeOptionLabelArray = client.get_type('ns0:catalogProductAttributeOptionLabelArray')
retyped_label=catalogProductAttributeOptionLabelArray(label)
data=[
{
"label": retyped_label,
"order":10,
"is_default":1
}
]
catalogProductAttributeOptionEntityToAdd=client.get_type('ns0:catalogProductAttributeOptionEntityToAdd')
retyped_data=catalogProductAttributeOptionEntityToAdd(data)
client.service.catalogProductAttributeAddOption(sessionId=sessionid,attribute="manufacturer",data=retyped_data)
And then, I got this error:
ValidationError: 'Missing element for Any'
I've investigated and it seems that this error comes when the structure is not in the format that is required... I mean, like if the dict I wrote before were not equivalent to the php structure written before, and if the new structure created with castings to the required types were not in the structure required.
At this point I'm stucked and I don't know how to go on. Any expert eye can see where is my error?
By the way, if this is solved, this also answer the question "How to write a manufacturer in Magento 1 using SOAP v2 webservice with Python and Zeep". Question which is not solved anywhere.
Thanks in advance.
I've been able to execute the function by using this structure:
ArrayOfString=client.get_type('ns0:ArrayOfString')
data=[
{
"label":[{
"store_id":ArrayOfString(["0"]),
"value":"some random data"
}],
"order":"10",
"is_default":"1"
}
]
client.service.catalogProductAttributeAddOption(sessionId=sessionid,attribute="manufacturer",data=data)
I choosed a general title because my question was about trying to find out how to convert the wsdl structure needed by the function into a python dict, but this question solves a very particular question which is more interesting, and that is: "How to write a manufacturer in the magento soap V2 webservice using Python 3 and zeep".

Passing 2/n-number of (multiple) parameters to 'int-jdbc:stored-proc-outbound-gateway'

Hi guys i need help here :)
Previously when I tried to pass only one variable, it was working fine. Now as soon as I am trying to pass 2/n-number of parameters by using Spring Integration, getting following 'payload' exceptions, which I am not clear about.
Exception I am getting is as follows:
[2014-10-31 12:12:43,943][WARN ]GatewayProxyFactoryBean$MethodInvocationGateway.doSendAndReceive: failure occurred in gateway sendAndReceive
org.springframework.messaging.converter.MessageConversionException: failed to convert object to Message
at org.springframework.integration.support.converter.SimpleMessageConverter.toMessage(SimpleMessageConverter.java:85)
at org.springframework.messaging.core.AbstractMessagingTemplate.convertSendAndReceive(AbstractMessagingTemplate.java:112)
at org.springframework.messaging.core.AbstractMessagingTemplate.convertSendAndReceive(AbstractMessagingTemplate.java:103)
...
Caused by: org.springframework.messaging.MessagingException: At most one parameter (or expression via method-level #Payload) may be mapped to the payload or Message. Found more than one on method [public abstract java.util.List com.dao.PersonalinfoDao.queryExecute(java.lang.String,java.lang.String)]
at org.springframework.integration.gateway.GatewayMethodInboundMessageMapper.throwExceptionForMultipleMessageOrPayloadParameters(GatewayMethodInboundMessageMapper.java:235)
at org.springframework.integration.gateway.GatewayMethodInboundMessageMapper.access$400(GatewayMethodInboundMessageMapper.java:77)
at org.springframework.integration.gateway.GatewayMethodInboundMessageMapper$DefaultMethodArgsMessageMapper.toMessage(GatewayMethodInboundMessageMapper.java:337)
...
Bellow showing what I am doing for passing 2 parameters:
In my PersonalinfoDao.java file I have this:
public interface PersonalinfoDao {
/** Method to call a SQL query using spring integration mechanism */
public List<PersonalInfo> queryExecute(String firstname, String lastname);
}
In my PersonalinfoService.java file I have this:
public class PersonalinfoService {
#Autowired
private PersonalinfoDao personalinfoDao;
public List<PersonalInfo> dbConnect(String firstname, String lastname) {
List<PersonalInfo> personalinfoList = personalinfoDao.queryExecute(firstname, lastname);
return personalinfoList;
}
}
In Gateway definition file I have the following:
<!-- Mapper Declarations -->
<bean id="personalinfoMapper" class="com.support.PersonalinfoMapper"/>
<!-- Service Inheritance -->
<bean id="personalinfoService" class="com.service.PersonalinfoService"/>
<!-- Channels = For calling DAO interface methods in Spring Integration Mechanism one has to create request & response channels -->
<int:channel id="procedureRequestChannel"/>
<!-- Gateway = DAO Interface Method Mapped to Request & Response Channels -->
<int:gateway id="gateway_personalinfo" default-request-timeout="5000"
default-reply-timeout="5000"
service-interface="com.dao.PersonalinfoDao">
<int:method name="queryExecute" request-channel="procedureRequestChannel" />
</int:gateway>
<!-- Stored Procedure Outbound-Gateway = To call a database stored procedure -->
<int-jdbc:stored-proc-outbound-gateway id="outbound-gateway-storedproc-personalinfo"
request-channel="procedureRequestChannel"
data-source="dataSource"
stored-procedure-name="pkg_personalinfo_spring.proc_personalinfo_spring"
expect-single-result="true"
ignore-column-meta-data="true">
<!-- Parameter Definitions -->
<int-jdbc:sql-parameter-definition name="firstname" direction="IN"/>
<int-jdbc:sql-parameter-definition name="lastname" direction="IN"/>
<int-jdbc:sql-parameter-definition name="get_ResultSet" type="#{T(oracle.jdbc.OracleTypes).CURSOR}" direction="OUT"/>
<!-- Parameter Mappings Before Passing & Receiving -->
<int-jdbc:parameter name="firstname" expression="payload"/>
<int-jdbc:parameter name="lastname" expression="payload"/>
<int-jdbc:returning-resultset name="get_ResultSet" row-mapper="com.support.PersonalinfoMapper"/>
</int-jdbc:stored-proc-outbound-gateway>
I know that i am doing something wrong in this above gateway definition specially when using expression="payload"...because for any given Getway i can use only one payload. But as I am not clear how to do that by using Map/Array/List, can any one plz help me to resolve this?
Thank u so much :)
Probably the simplest way is to use a #Payload annotation:
public interface PersonalinfoDao {
/** Method to call a SQL query using spring integration mechanism */
#Payload("#args")
public List<PersonalInfo> queryExecute(String firstname, String lastname);
}
Or use a payload-expression="#args" in the XML declaration of the <gateway/>'s method.
The framework will then make the payload an Object[] and you can use payload[0], payload[1] etc in your expressions.

JAXB mark field as required in generated wsdl

I have a JAXB-annotiated class with some required fields:
...
#XmlElement(required = true, nillable = false)
private String credentials = null;
...
Using SoapUI, the generated wsdl is like this:
...
<xs:element name="credentials" type="xs:string"/>
...
Is there any way to get the required-information into the wsdl? If I understood this right, than the wsdl should look like this (maybe that):
...
<xs:element wsdl:required="true" name="credentials" type="xs:string"/>
...
#XmlElement(required=true) corresponds to an element in the XML Schema with the attribute minOccurs="1". Since this the default it is not required to be specified.

JAXB Unmarshalling Fails

Getting following error while unmarshalling a response String(in xml format). Initially it was complaining about the and ,so I removed the soap part from the response.Now it comes up with this error.
Classes are created from provided WSDL using JAXB 2
**javax.xml.bind.UnmarshalException: unexpected element (uri:"http://ws.taxwareenterprise.com", local:"calculateDocumentResponse"). Expected elements are (none)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:662)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:258)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:253)**
Here is my response string (part of it)
**<calculateDocumentResponse xmlns="http://ws.taxwareenterprise.com">
<txDocRslt>
<jurSumRslts>
<jurSumRslt>
<xmptAmt>5.0</xmptAmt>
<txableAmt>15.0</txableAmt>
<txAmt>0.04</txAmt>
<txJurUID>...**
Here goes my xsd for "CalculationResponse"(part of it)
<xs:complexType name="CalculationResponse">
<xs:sequence>
<xs:element name="txDocRslt" type="tns:TxDocRslt"/>
</xs:sequence>
</xs:complexType>
and this is my code for unmarshalling:
**final JAXBContext context = JAXBContext.newInstance(CalculationResponse.class);
final Unmarshaller unmarshaller = context.createUnmarshaller();
response = processResponse(response);//for removing soap headers
responseObj = (CalculationResponse) unmarshaller.unmarshal(new StringReader(response));//Fails here...
final TxDocRslt txDocRslt = responseObj.getTxDocRslt();**

Using of Zend_Soap_Wsdl_Strategy_ArrayOfTypeComplex with string[]

I'm currently using Zend_Soap_AutoDiscover to generate my WSDL file, the problem is I want this wsdl to handle output of type ArrayOfString ( string[] ). so I changed the complex type strategy to Zend_Soap_Wsdl_Strategy_ArrayOfTypeSequence, it works properly but the problem is that the output isn't really an array of string the output xml is somthing like this :
<xsd:complexType name="ArrayOfString">
<xsd:sequence>
<xsd:element name="item" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
But I want output like this :
<xsd:complexType name="ArrayOfstring">
<xsd:complexContent>
<xsd:restriction base="soapenc:Array">
<xsd:attribute ref="soapenc:arrayType" wsdl:arrayType="xsd:string[]"/>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
so ,I used the new strategy , Zend_Soap_Wsdl_Strategy_ArrayOfTypeComplex, but the problem is that this strategy does not handle string[].
Finally -> What should I do :D?!
Try creating a response class that has just one attribute, as follows:
class Response
{
/** #var string[] */
public $items;
}
Then define your service class to return an object of type Response, as follows:
class Service
{
/**
* #param string
* #return Response
*/
public function process( $input )
{
$response = new Response();
// Populate $response->items[] object with strings...
return $response;
}
}
Then use the 'Zend_Soap_Wsdl_Strategy_ArrayOfTypeComplex' strategy when using Zend_Soap_Autodiscover to create the WSDL. Although this probably won't produce precisely the output you're after, it should produce something that is semantically closer than what you're currently getting. The key with this approach is to get the PHPDoc right.
If that still doesn't work, post the key bits of your code, as that will help resolve the issue more quickly.

Resources