There is an external SOAP server that I'm trying to connect and make requests using the python-django framework with the zeep library.
The WSDL dump is as follows:
Prefixes:
xsd: http://www.w3.org/2001/XMLSchema
ns0: http://tempuri.org/
Global elements:
ns0:GetJobCards(key: xsd:string, technicianID: xsd:string, jobCardNumber: xsd:string, jobcards: ns0:ArrayOfJobCardsEnt, message: xsd:string)
ns0:GetJobCardsResponse(GetJobCardsResult: xsd:boolean, jobcards:ns0:ArrayOfJobCardsEnt, message: xsd:string)
Global types:
xsd:anyType
ns0:ArrayOfJobCardsEnt(JobCardsEnt: {schema: , _value_1: ANY}[])
xsd:ID
xsd:IDREF
xsd:IDREFS
xsd:NCName
xsd:NMTOKEN
xsd:NMTOKENS
xsd:NOTATION
xsd:Name
xsd:QName
xsd:anySimpleType
xsd:anyURI
xsd:base64Binary
xsd:boolean
xsd:byte
xsd:date
xsd:dateTime
xsd:decimal
xsd:double
xsd:duration
xsd:float
xsd:gDay
xsd:gMonth
xsd:gMonthDay
xsd:gYear
xsd:gYearMonth
xsd:hexBinary
xsd:int
xsd:integer
xsd:language
....
Bindings:
Soap11Binding: {http://tempuri.org/}CallSoap
Soap12Binding: {http://tempuri.org/}CallSoap12
Service: Call
Port: CallSoap (Soap11Binding: {http://tempuri.org/}CallSoap)
Operations:
GetJobCards(key: xsd:string, technicianID: xsd:string,
jobCardNumber: xsd:string, jobcards: ns0:ArrayOfJobCardsEnt,
message: xsd:string) -> GetJobCardsResult: xsd:boolean,
jobcards: ns0:ArrayOfJobCardsEnt, message: xsd:string
....(some other operations)
One of the calls is to get a list of jobCards with the following schema
<s:element name="GetJobCards">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="key" type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="technicianID" type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="jobCardNumber" type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="jobcards" type="tns:ArrayOfJobCardsEnt"/>
<s:element minOccurs="0" maxOccurs="1" name="message" type="s:string"/>
</s:sequence>
</s:complexType>
</s:element>
<s:complexType name="ArrayOfJobCardsEnt">
<s:sequence>
<s:element minOccurs="0" maxOccurs="unbounded" name="JobCardsEnt" nillable="true">
<s:complexType>
<s:sequence>
<s:element ref="s:schema"/>
<s:any/>
</s:sequence>
</s:complexType>
</s:element>
</s:sequence>
</s:complexType>
When I make the call using Postman I send the following request and it works:
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetJobCards xmlns="http://tempuri.org/">
<key>some_valid_key</key>
<technicianID>some_technician_id</technicianID>
<jobCardNumber></jobCardNumber>
<jobcards></jobcards>
<message>""</message>
</GetJobCards>
</soap:Body>
Please note that the values for "jobCardNumber" and "jobcards" need to be empty to get all the jobCards. But I cannot seem to replicate this same call using the zeep library.
Here is what I have tried.
1 - tried adding xsd.SkipValue for the empty values
request_data = {
'key': some_valid_key,
'technicianID': some_technician_ID,
'jobCardNumber': xsd.SkipValue,
'jobcards': xsd.SkipValue,
'message': ''
}
response_body = client.service.GetJobCards(**request_data)
2 - setting the values to Nil
from zeep.xsd import Nil
jobCardEnt = client.get_type('ns0:ArrayOfJobCardsEnt')
jobCardEnt = jobCardEnt()
request_data = {
'key': some_valid_key,
'technicianID': some_technician_ID,
'jobCardNumber': Nil,
'jobcards': Nil,
'message': ''
}
response_body = client.service.GetJobCards(**request_data)
None of these seems to works and I'm not sure how to get it to work.
Can someone please guide me on this?
Thanks in advance!
Here is how I got it working
with self.client.settings(strict=False):
response_body = self.client.service.GetJobCards(
key=some_valid_key,
technicianID=some_technician_id,
message=''
)
And omitted the values for 'jobCardNumber' and 'jobcards'
Related
I'm trying to send an e-mail from within a C++ application via a Microsoft Exchange 2010 server. For this, I use the EWS protocol using the library ews-cpp.
The following trivial example, based on the one from https://github.com/otris/ews-cpp/blob/master/examples/send_message.cpp, is failing (mail addresses and credentials stripped from my actual code):
#include <iostream>
#include <ews/ews.hpp>
struct {
std::string domain = "mailserver";
std::string username = "...";
std::string password = "...";
std::string server_uri = "https://mailserver/EWS/Exchange.asmx";
} env;
int main()
{
ews::set_up();
try
{
auto service = ews::service(env.server_uri, env.domain, env.username,
env.password);
service.set_request_server_version(ews::server_version::exchange_2010_sp2);
auto message = ews::message();
message.set_subject("Test mail from outer space");
std::vector<ews::mailbox> recipients;
recipients.push_back(ews::mailbox("..."));
message.set_to_recipients(recipients);
auto text = ews::body("This is a test.");
message.set_body(text);
service.create_item(message,
ews::message_disposition::send_and_save_copy);
}
catch (ews::schema_validation_error& exc)
{
std::cout << exc.what() << std::endl;
std::cout << exc.violation() << std::endl;
}
ews::tear_down();
return 0;
}
The raw SOAP request is as follows (indentation added by me):
<m:CreateItem MessageDisposition="SendAndSaveCopy">
<m:Items>
<t:Message>
<t:Subject>Test mail from outer space</t:Subject>
<t:ToRecipients>
<t:Mailbox>
<t:EmailAddress>...</t:EmailAddress>
</t:Mailbox>
</t:ToRecipients>
<t:Body BodyType="Text">This is a test.</t:Body>
</t:Message>
</m:Items>
</m:CreateItem>
The function create_item throws an ews::schema_validation_error which is caught in the above code, printing:
The request failed schema validation
The element 'Message' in namespace ‘http://schemas.microsoft.com/exchange/services/2006/types’ has invalid child element 'Body' in namespace 'http://schemas.microsoft.com/exchange/services/2006/types'. List of possible elements expected: 'CcRecipients, BccRecipients, IsReadReceiptRequested, IsDeliveryReceiptRequested, ConversationIndex, ConversationTopic, From, InternetMessageId, IsRead, IsResponseRequested, References, ReplyTo, ReceivedBy, ReceivedRepresenting' in Namespace 'http://schemas.microsoft.com/exchange/services/2006/types'.
In other words, EWS doesn't expect <t:Body> within <t:Message>.
So, when leaving out that element in the SOAP request (comment out message.set_body(text)), the mail is sent smoothly. However, a mail without a body doesn't make much sense, does it?
I thought that the problem might be the fact that ews-cpp was written for Exchange 2013 (and that the schema changed between 2010 and 2013 in this regard). So I digged into the schema, which each Exchange server serves at /ews/types.xsd, to see if the schema allows such a child element.
In the schema definition file, I found the definition of the type MessageType (which is the type of the Message element we are talking about):
<xs:complexType name="MessageType">
<xs:complexContent>
<xs:extension base="t:ItemType">
<xs:sequence>
<xs:element name="Sender" minOccurs="0" type="t:SingleRecipientType"/>
<xs:element name="ToRecipients" type="t:ArrayOfRecipientsType" minOccurs="0"/>
<xs:element name="CcRecipients" type="t:ArrayOfRecipientsType" minOccurs="0"/>
<xs:element name="BccRecipients" type="t:ArrayOfRecipientsType" minOccurs="0"/>
<xs:element name="IsReadReceiptRequested" type="xs:boolean" minOccurs="0"/>
<xs:element name="IsDeliveryReceiptRequested" type="xs:boolean" minOccurs="0"/>
<xs:element name="ConversationIndex" type="xs:base64Binary" minOccurs="0"/>
<xs:element name="ConversationTopic" type="xs:string" minOccurs="0"/>
<xs:element name="From" type="t:SingleRecipientType" minOccurs="0"/>
<xs:element name="InternetMessageId" type="xs:string" minOccurs="0"/>
<xs:element name="IsRead" type="xs:boolean" minOccurs="0"/>
<xs:element name="IsResponseRequested" type="xs:boolean" minOccurs="0"/>
<xs:element name="References" type="xs:string" minOccurs="0"/>
<xs:element name="ReplyTo" type="t:ArrayOfRecipientsType" minOccurs="0"/>
<xs:element name="ReceivedBy" type="t:SingleRecipientType" minOccurs="0"/>
<xs:element name="ReceivedRepresenting" type="t:SingleRecipientType" minOccurs="0"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
As you can see, there is no Body child element, but MessageType is based on ItemType, which is defined as follows (excerpt):
<xs:complexType name="ItemType">
<xs:sequence>
<xs:element name="MimeContent" type="t:MimeContentType" minOccurs="0"/>
<xs:element name="ItemId" type="t:ItemIdType" minOccurs="0"/>
<xs:element name="ParentFolderId" type="t:FolderIdType" minOccurs="0"/>
<xs:element name="ItemClass" type="t:ItemClassType" minOccurs="0"/>
<xs:element name="Subject" type="xs:string" minOccurs="0"/>
<xs:element name="Sensitivity" type="t:SensitivityChoicesType" minOccurs="0"/>
<xs:element name="Body" type="t:BodyType" minOccurs="0"/>
<xs:element name="Attachments" type="t:NonEmptyArrayOfAttachmentsType" minOccurs="0"/>
[...]
</xs:sequence>
</xs:complexType>
As you can see, it accepts the Body element as a child.
Note that the Subject element is also defined in the ItemType and not in MessageType, and that is accepted by EWS in the above code snippet.
What might be the cause of this strange validation failure?
Found the solution myself:
For the element Message, which is based on Item according to the schema definition, EWS first expects all child elements for Item, followed by the Message-specific child elements.
This sounds very insane (to me), but swapping <t:ToRecipients> and <t:Body> in the <t:Message> element indeed solved the problem.
I did this by swapping the two corresponding setters in my code:
auto message = ews::message();
// First, set properties of the general "item":
message.set_subject("Test mail from outer space");
auto text = ews::body("This is a test.");
message.set_body(text);
// Then, set properties of the concrete "message":
std::vector<ews::mailbox> recipients;
recipients.push_back(ews::mailbox("..."));
message.set_to_recipients(recipients);
I'm not sure if this is a bug in ews-cpp, in EWS / Exchange server, or in the EWS schema definition.
I'm using python 2.7 and Spyne for creating some web services in Django.
I'm not able to change the name of the returned values, for example:
Given this code:
class Consumer(ServiceBase):
#rpc(Integer,Integer, _returns = [Integer,Integer])
def addConsumer(ctx, topic_id, consumer_id):
...
...
The response in my wsdl looks like:
<xs:sequence>
<xs:element name="addConsumerResult0" type="xs:integer" minOccurs="0" nillable="true"/>
<xs:element name="addConsumerResult1" type="xs:integer" minOccurs="0" nillable="true"/>
</xs:sequence>
I am looking for something that allow me to change name="addConsumerResult0" to name="whateverXYZ"
You can use _out_variable_names to change return type names.
e.g.
class Consumer(ServiceBase):
#rpc(Integer,Integer, _returns = [Integer,Integer],
_out_variable_names=["whateverXYZ", "foo"])
def addConsumer(ctx, topic_id, consumer_id):
...
...
I had to fiddle with a WSDL document for one of the implementations and I came across Scalaxb! I'm now trying to generate some scala classes from the WSDL file that I have and as expected I'm hitting into some issues:
Here is a snippet of the WSDL file:
<?xml version="1.0" encoding="UTF-8" ?>
<wsdl:definitions
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://www.myservice.com/MyServices/2012/06/18/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:s="http://www.w3.org/2001/XMLSchema"
name="MyServices"
targetNamespace="http://www.myservice.com/MyServices/2012/06/18/">
<wsdl:types>
<s:schema elementFormDefault="qualified" targetNamespace="http://www.myservice.com/MyServices/2012/06/18/">
<s:complexType name="UserCredentials">
<s:sequence>
<s:element name="UserName" type="s:string" />
<s:element name="Password" type="s:string" />
</s:sequence>
</s:complexType>
<s:element name="UserCredentials" type="tns:UserCredentials" />
<s:complexType name="AnotherComplexType" >
<s:sequence>
<s:element name="Index" type="s:int" />
<s:element name="Name" type="s:string" />
<s:element name="Status" type="s:boolean" />
</s:sequence>
</s:complexType>
....
Assume that the rest of the WSDL file is completely fine, when I tried to compile the project, I hit the following error:
[error] /Users/joe/Desktop/scalaxb-soap-example/target/scala-2.11/src_managed/main/sbt-scalaxb/myservice/xmlprotocol.scala:1542: not found: value userCredentials
[error] scalaxb.toXML(userCredentials, Some("http://www.myservice.com/MyServices/2012/06/18/"), "UserCredentials", defaultScope), defaultScope, baseAddress, "POST", Some(new java.net.URI("http://1.1.1.1/cgi-bin/cgi.cgi?WebService=SetGPTimerChannel"))).transform({ case (header, body) =>
[error] ^
[error] /Users/joe/Desktop/scalaxb-soap-example/target/scala-2.11/src_managed/main/sbt-scalaxb/myservice/xmlprotocol.scala:1544: value toSeq is not a member of Any
[error] scala.xml.Elem(null, "Body", scala.xml.Null, defaultScope, true, body.toSeq: _*)
[error] ^
[error] /Users/joe/Desktop/scalaxb-soap-example/target/scala-2.11/src_managed/main/sbt-scalaxb/myservice/xmlprotocol.scala:1551: not found: value userCredentials
[error] scalaxb.toXML(userCredentials, Some("http://www.myservice.com/MyServices/2012/06/18/"), "UserCredentials", defaultScope), defaultScope, baseAddress, "POST", Some(new java.net.URI("http://1.1.1.1/cgi-bin/cgi.cgi?WebService=SomeServiceCall"))).transform({ case (header, body) =>
[error] ^
[error] /Users/joe/Desktop/scalaxb-soap-example/target/scala-2.11/src_managed/main/sbt-scalaxb/myservice/xmlprotocol.scala:1553: value toSeq is not a member of Any
[error] scala.xml.Elem(null, "Body", scala.xml.Null, defaultScope, true, body.toSeq: _*)
[error] ^
Any ideas what and why I'm facing this issue? Here is my build.sbt:
import ScalaxbKeys._
val scalaXml = "org.scala-lang.modules" %% "scala-xml" % "1.0.2"
val scalaParser = "org.scala-lang.modules" %% "scala-parser-combinators" % "1.0.1"
val dispatchV = "0.11.1" // change this to appropriate dispatch version
val dispatch = "net.databinder.dispatch" %% "dispatch-core" % dispatchV
organization := "com.eon"
name := "scalaxb-myservice-sample"
scalaVersion := "2.11.6"
scalaxbSettings
packageName in (Compile, scalaxb) := "rdmservice"
dispatchVersion in (Compile, scalaxb) := dispatchV
async in (Compile, scalaxb) := true
sourceGenerators in Compile <+= scalaxb in Compile
libraryDependencies ++= Seq(scalaXml, scalaParser, dispatch)
If you only need to generate WSDL client code, using scalaxb might be a bit too much. As an option, you could just use wsimport wrapped as a simple SBT task that would be executed before the main code compilation. Apart from one less dependency, it also keeps your repository pristine and frees it from committing the generated boilerplate Java code. Here's a sample template project if anyone is interested: https://github.com/sainnr/sbt-scala-wsdl-template.
I have been using Authorize.NET's GetCustomerProfile for many years and suddenly today the response is not returning a payment profile. It just returns an array of profiles, but all important fields (such as payment) are null.
(This is part of their CIM feature where an 'obfuscated' payment profile is returned and should be something like XXXX1234)
I am using a generated proxy within Visual Studio to the URL https://api.authorize.net/soap/v1/Service.asmx?WSDL (which generates a References.cs file)
I had a similar issue on the CreateCustomerPaymentProfile SOAP call (via .NET auto-generated proxy) that just started yesterday, 11/3/2015, even though we've been successfully interfacing the SOAP CIM for a couple years.
I was able to "fix" the issue by doing an "Update Service Reference" in Visual Studio to regenerate the proxy classes based on their latest WSDL. There were several changes in the WSDL.
Specifically, on my end it appeared as if the response was not including a value for the customerPaymentProfileId after being successfully created on their end. In reality, they were in fact still sending this value, but there was a new field above it in the XML response, customerProfileId. As Simon_Weaver mentioned in his answer, the proxy classes generated by Visual Studio have explicit ordering of fields that are required for proper deserialization. The addition of this previously "unknown" field above a known field caused it to break my code.
Luckily this new customerProfileId was included in their latest WSDL so an "Update Service Reference" and recompile fixed my issue.
I informed Authorize.net support of my issue in great detail and told them they need to include any new fields at the end of the "sequence" in the WSDL document in order to not break and clients using older versions of the WSDL. So far I have not heard a response from them, but I would encourage anyone else who experienced this issue, even if you've already worked-around it, to report it to them at support#authorize.net so they don't accidently do this again.
Well hello Simon!
It looks like Authorize.NET updated their service with new fields but forgot to add them to the WSDL.
This is a sample request that I sent (intercepted using Fiddler):
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<VsDebuggerCausalityData xmlns="http://schemas.microsoft.com/vstudio/diagnostics/servicemodelsink">uIDPo3vYq2eC/5VIuiUcm2hEtw8AABBBJr/dLQF7z02Y7UKwphq24W1n9j0XlQ1MiAlOjy5fO14ACQAA</VsDebuggerCausalityData>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<GetCustomerProfile xmlns="https://api.authorize.net/soap/v1/">
<merchantAuthentication>
<name>95U6bwXXXXX</name>
<transactionKey>8tf62gV7XXXXXX</transactionKey>
</merchantAuthentication>
<customerProfileId>37745529</customerProfileId>
</GetCustomerProfile>
</s:Body>
</s:Envelope>
This is the response:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<GetCustomerProfileResponse xmlns="https://api.authorize.net/soap/v1/">
<GetCustomerProfileResult>
<resultCode>Ok</resultCode>
<messages>
<MessagesTypeMessage>
<code>I00001</code>
<text>Successful.</text>
</MessagesTypeMessage>
</messages>
<profile>
<merchantCustomerId>33938</merchantCustomerId>
<email>4691705#EXAMPLE.COM</email>
<customerProfileId>37745529</customerProfileId>
<paymentProfiles>
<CustomerPaymentProfileMaskedType>
<billTo>
<firstName>TEST</firstName>
<lastName>USER</lastName>
<company>Defender Razor</company>
<address>1 RODEO DRIVE</address>
<city>BEVERLY HILLS</city>
<state>CA</state>
<zip>90210</zip>
<country>UNITED STATES</country>
</billTo>
<customerProfileId>0</customerProfileId>
<customerPaymentProfileId>34313485</customerPaymentProfileId>
<payment>
<creditCard>
<cardNumber>XXXX5108</cardNumber>
<expirationDate>XXXX</expirationDate>
</creditCard>
</payment>
</CustomerPaymentProfileMaskedType>
</paymentProfiles>
</profile>
</GetCustomerProfileResult>
</GetCustomerProfileResponse>
</soap:Body>
</soap:Envelope>
Everything here is correct - as you can see the payment node is being sent correctly.
However - with .NET deserialization the order of attributes matters - as specified in the generated References.cs file.
[System.Xml.Serialization.XmlElementAttribute(Order=0)]
It turns out that two new fields were added to the response billTo and customerProfileId but they weren't added to the WSDL.
So when trying to deserialize the field billTo is found but that isn't what was expected - so everything ends up null.
If you add these two lines (and be careful to add them to exactly this type) then you can regenerate the references.cs file (by right clicking on the service reference and regenerating the file).
If you were generating your proxy from the URL https://api.authorize.net/soap/v1/Service.asmx?WSDL then you will need to download this file locally as Service.wsdl and generate the proxy from there.
<s:element minOccurs="1" maxOccurs="1" name="billTo" type="tns:CustomerAddressType"/>
<s:element minOccurs="1" maxOccurs="1" name="customerProfileId" type="s:long" />
<s:complexType name="CustomerPaymentProfileMaskedType">
<s:complexContent mixed="false">
<s:extension base="tns:CustomerPaymentProfileBaseType">
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="billTo" type="tns:CustomerAddressType"/>
<s:element minOccurs="1" maxOccurs="1" name="customerProfileId" type="s:long" />
<s:element minOccurs="1" maxOccurs="1" name="customerPaymentProfileId" type="s:long" />
<s:element minOccurs="0" maxOccurs="1" name="payment" type="tns:PaymentMaskedType" />
<s:element minOccurs="0" maxOccurs="1" name="driversLicense" type="tns:DriversLicenseMaskedType" />
<s:element minOccurs="0" maxOccurs="1" name="taxId" type="s:string" />
</s:sequence>
</s:extension>
</s:complexContent>
</s:complexType>
I lost over 12 hours of payments before this was detected. Fortunately I have the customer's emails but this was very bad. You can't just add fields to a response where the order matters. And even worse than that you can't just forget to add them to the WSDL.
This was the quick fix for me. I may switch to using the proper API at some point - and I've reported this to Authorize.net hoping they may respond too.
This is the differences in References.cs after I made my change. As you can see the Order property has been incremented :
I would like to create xml schema on nusoap like the following:
<xsd:complexType name="WSMessage">
<xsd:sequence>
<xsd:element minOccurs="0" maxOccurs="1" name="ErrorMessage" type="s:string"/>
<xsd:element minOccurs="0" maxOccurs="1" name="ErrorCode" type="s:string"/>
</xsd:sequence>
</xsd:complexType>
<s:complexType name="ResultSet">
<s:complexContent mixed="false">
<s:extension base="tns:WSMessage">
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="TestData" type="tns:ArrayOfTestData"/>
</s:sequence>
</s:extension>
</s:complexContent>
</s:complexType>
I can't see any documentation on how I could do that. Does anyone know this already?
Thank you.
Add two complex type for input, what i understood in your question is its input and has output
$this->nusaop->wsdl->addComplexType(
'WSMessage',
'complexType',
'struct',
'all',
'',
array(
'ErrorMessage ' => array(
'MethodParameters' => 'ErrorMessage',
'type' => 'xsd:string'
),
'ErrorCode' => array(
'MethodParameters' => 'ErrorCode',
'type' => 'xsd:string'
)
)
);
/Input Data type/
$this->nusaop->wsdl->addComplexType(
'ResultSet',
'complexType',
'struct',
'all',
'',
array(
'TestData' => array(
'MethodParameters' => 'TestData',
'type' => 'xsd:string'
),
)
);
/Output Data type/
$this->FM_SoapServer->register(
'Methodcall',// parameter list:
array('Credentials'=>'tns:WSMessage'),// return value(s):
array('return'=>'tns:ResultSet'),// namespace:
false,// soapaction: (use default)
false,// style: rpc or document
'rpc',// use: encoded or literal
'encoded',// description: documentation for the method
''
);
function Methodcall(){
/// validations ....
code....
return array("ResultSet" => $return);
}
Untested but the logic is there