Calling a function from a webservice - web-services

I'm new in webservices and I have a question about function.
Purpose: I need to access a function hosted on a wcf webservice but I have trouble to do it.
I want to now if it is my mistake or if it is the code behind the webservice that doensn't work. I don't have any SoapFault or PHPException, the function should return something but does not.
The function I need to call is: "GetListCollaborateur_Adresse"
Here is my wsdl:
<wsdl:operation name="GetListCollaborateur_Adresse">
<wsdl:input wsaw:Action="Http://www.../GetListCollaborateur_Adresse" message="tns:IWcfBiings_GetListCollaborateur_Adresse_InputMessage"/>
<wsdl:output wsaw:Action="Http://www.../GetListCollaborateur_AdresseResponse" message="tns:IWcfBiings_GetListCollaborateur_Adresse_OutputMessage"/>
</wsdl:operation>
// => So it look like we have an input parameter of "IWcfBiings_GetListCollaborateur_Adresse_InputMessage" type
<wsdl:message name="IWcfBiings_GetListCollaborateur_Adresse_InputMessage">
<wsdl:part name="parameters" element="tns:GetListCollaborateur_Adresse"/>
</wsdl:message>
// => and here is my element "GetListCollaborateur_Adresse"
<xs:element name="GetListCollaborateur_Adresse">
<xs:complexType>
<xs:sequence/>
</xs:complexType>
</xs:element>
So, no type for the parameter is defined in the <xs:sequence/> :/
And here is what $client->__getFunctions(); return me (my SoapClient instanciation is correct).
Array
(
[0] => GetListCollaborateur_AdresseResponse GetListCollaborateur_Adresse(GetListCollaborateur_Adresse $parameters)
)
As the "GetListCollaborateur_Adresse" seems to be an "empty type" I tried to call the function theses ways
$response = $client->getListFonction();
$response = $client->getListFonction(null);
$response = $client->getListFonction(array());
But none of them return anything.
So my question is:
Do I call it in right way? (It is not impossible that the webservice is "broken" but I not sur about my code neither). The <xs:sequence/> let me perplexe.
Any help/comment would be appreciate, thank you.
Have a good day :)
Michaƫl

Related

gSOAP regex validation

I have a pattern restriction in my WSDL defined like this:
<complexType name="xxxXXX">
<attribute name="state" use="optional">
<simpleType>
<restriction base="xs:string">
<pattern value="((\-1)|[0-5])(;((\-1)|[0-5]))*"/>
</restriction>
</simpleType>
</attribute>
</complexType>
But when I do a request with state="" gSOAP accepts it. I saw that I have to define the function fsvalidate (here https://www.genivia.com/doc/databinding/html/) but it seems never called (doesn't trigger the breakpoint).
Futhermore, the generated gsoap's code doesn't contain the regex, even if the tool wsdl2h generates this :
class ns1__xxxXXX
{ public:
#/// Content pattern is "((\\-1)|[0-5])(;((\\-1)|[0-5]))*".
std::string
*state 0; ///< Optional attribute.
/// A handle to the soap struct that manages this instance (automatically set).
struct soap *soap ;
};
Maybe I'm missing some options for the generation but I didn't find anythink on wsdl2h (https://linux.die.net/man/1/wsdl2h) nor soapcpp2
Additional information:
I searched for fsvalidate in gSOAP's generated code and I found:
#ifndef WITH_LEANER
if (pattern && soap->fsvalidate && (soap->error = soap->fsvalidate(soap, pattern, s)))
return NULL;
#endif
However, it seems always called with a NULL pattern.. (!(t = soap_string_in(soap, 1, 0, -1, NULL))).
Note: I'm using gSOAP 2.8.23.
-- EDIT:
I followed #Alex's advice to extract the type from the complexType like this:
<xs:simpleType name="stateType">
<xs:restriction base="xs:string">
<xs:pattern value="((\-1)|[0-5])(;((\-1)|[0-5]))*"/>
</xs:restriction>
</xs:simpleType>
<complexType name="xxxXXX">
<attribute name="state" type="tns:stateType" use="optional" />
</complexType>
And the wsdl2h seems to generate something good:
/// Content pattern is "((\\-1)|[0-5])(;((\\-1)|[0-5]))*".
typedef std::string ns1__stateType "((\\-1)|[0-5])(;((\\-1)|[0-5]))*";
class ns1__xxxXXX
{ public:
/// Attribute "state" of XSD type "http://new.webservice.namespace":stateType.
#ns1__stateType* state 0;
But the final type generated by gsoap is a std::string:
class SOAP_CMAC ns1__xxxXXX
{
public:
std::string *state;
and, of course, it doesn't validate the regex.
It appears that local regex patterns are ignored (i.e. local to a class), but non-local patterns work fine. To enforce pattern validation, the pattern has to be lifted from the complexType (lifted from the class):
typedef std::string ns1__xxxXXX_string "((\\-1)|[0-5])(;((\\-1)|[0-5]))*";
class ns1__xxxXXX
{ public:
ns1__xxxXXX_string *state;
Then set the fsvalidate() callback to check the string content against the pattern.
It is an annoying limitation. I'd suggest to submit a bug/feature request to https://sourceforge.net/p/gsoap2/bugs/

Mock a message from a 3rd party system in Mule using MUnit

I'm writing a test suite (using Munit) for a Mule application that is processing new data coming from an instance of Magento. One of my flows is polling Magento for new customers and the message it receives is of type: com.magento.api.CustomerCustomerEntity
I'm wondering how I'd mock this so that in my test case, when the Magento message processor is called I can return a payload of the same type and make the appropriate assertations?
Currently my Munit test looks as follows:
<mock:config name="mock_MagentoToSalesforce" doc:name="Mock configuration"/>
<spring:beans>
<spring:import resource="classpath:MagentoToSalesforce.xml"/>
<spring:bean id="myBean" name="myBean" class="com.magento.api.CustomerCustomerEntity">
<spring:property name="email" value="test#test.com"/>
</spring:bean>
</spring:beans>
<munit:test name="MagentoToSalesforce-test-getCustomersFlowTest" description="Test">
<mock:when config-ref="mock_MagentoToSalesforce" messageProcessor=".*:.*" doc:name="Mock">
<mock:with-attributes>
<mock:with-attribute whereValue-ref="#[string:Get New Customers]" name="doc:name"/>
</mock:with-attributes>
<mock:then-return payload-ref="#[app.registry.myBean]"/>
</mock:when>
<flow-ref name="getCustomers" doc:name="Flow-ref to getCustomers"/>
</munit:test>
And the flow I'm trying to test is:
<flow name="getCustomers" processingStrategy="synchronous">
<poll doc:name="Poll">
<fixed-frequency-scheduler frequency="30" timeUnit="SECONDS"/>
<watermark variable="watermark" default-expression="#[new org.mule.el.datetime.DateTime().plusYears(-30)]" update-expression="#[new org.mule.el.datetime.DateTime().plusYears(-0)]" selector-expression="#[new org.mule.el.datetime.DateTime(payload.created_at, 'yyyy-MM-dd HH:mm:ss')]"/>
<magento:list-customers config-ref="Magento" filter="dsql:SELECT confirmation,created_at,created_in,customer_id,dob,email,firstname,group_id,increment_id,lastname,middlename,password_hash,prefix,store_id,suffix,taxvat,updated_at,website_id FROM CustomerCustomerEntity WHERE updated_at > '#[flowVars.watermark]'" doc:name="Get New Customers"/>
</poll>
<foreach doc:name="For Each">
<data-mapper:transform config-ref="MagentoCustomer_To_SalesforceContact" doc:name="Map Customer to SFDC Contact">
<data-mapper:input-arguments>
<data-mapper:input-argument key="ContactSource">Magento</data-mapper:input-argument>
</data-mapper:input-arguments>
</data-mapper:transform>
<flow-ref name="upsertSalesforceContactFlow" doc:name="upsertSalesforceContactFlow"/>
</foreach>
</flow>
Update following Ryan's answer:
Changed the expression to return a payload of #[ent = new com.magento.api.CustomerCustomerEntity(); ent.setEmail('test#test.com'); return [ent];] - note, changed the method to setEmail to match the documentation here. The error I get with this is:
ERROR 2015-06-22 09:58:34,719 [main] org.mule.exception.DefaultMessagingExceptionStrategy:
********************************************************************************
Message : Object "org.mule.transport.NullPayload" not of correct type. It must be of type "{interface java.lang.Iterable,interface java.util.Iterator,interface org.mule.routing.MessageSequence,interface java.util.Collection}" (java.lang.IllegalArgumentException). Message payload is of type: NullPayload
Code : MULE_ERROR--2
--------------------------------------------------------------------------------
Exception stack is:
1. Object "org.mule.transport.NullPayload" not of correct type. It must be of type "{interface java.lang.Iterable,interface java.util.Iterator,interface org.mule.routing.MessageSequence,interface java.util.Collection}" (java.lang.IllegalArgumentException)
org.mule.util.collection.EventToMessageSequenceSplittingStrategy:64 (null)
2. Object "org.mule.transport.NullPayload" not of correct type. It must be of type "{interface java.lang.Iterable,interface java.util.Iterator,interface org.mule.routing.MessageSequence,interface java.util.Collection}" (java.lang.IllegalArgumentException). Message payload is of type: NullPayload (org.mule.api.MessagingException)
org.mule.execution.ExceptionToMessagingExceptionExecutionInterceptor:32 (http://www.mulesoft.org/docs/site/current3/apidocs/org/mule/api/MessagingException.html)
--------------------------------------------------------------------------------
Root Exception stack trace:
java.lang.IllegalArgumentException: Object "org.mule.transport.NullPayload" not of correct type. It must be of type "{interface java.lang.Iterable,interface java.util.Iterator,interface org.mule.routing.MessageSequence,interface java.util.Collection}"
at org.mule.util.collection.EventToMessageSequenceSplittingStrategy.split(EventToMessageSequenceSplittingStrategy.java:64)
at org.mule.util.collection.EventToMessageSequenceSplittingStrategy.split(EventToMessageSequenceSplittingStrategy.java:25)
at org.mule.routing.CollectionSplitter.splitMessageIntoSequence(CollectionSplitter.java:29)
+ 3 more (set debug level logging or '-Dmule.verbose.exceptions=true' for everything)
********************************************************************************
One way is to build up the object yourself using the constructor or properties/setters.
From the docs:
http://mulesoft.github.io/magento-connector/2.1.2/java/com/magento/api/CustomerCustomerEntity.html
<mock:then-return payload-ref="#[ent = new com.magento.api.CustomerCustomerEntity(); ent.email('test#test.com'); return ent;]"/>
You can also create these objects aS reusable spring beans and reference them from MEL.
<bean class="com.magento.api.CustomerCustomerEntity" id="myEntityWithEmail">
<property name="email" value="test#test.com" />
</bean>
<mock:then-return payload-ref="#[app.registry.myEntityWithEmail]"/>
After your update I can see that you sre using a foreach which expects a collection or iterable. YOu can return a collection of you custom object simply in MEL using: [] for example:
#[ent = new com.magento.api.CustomerCustomerEntity(); ent.email('test#test.com'); return [ent];]
More on MEL here: https://developer.mulesoft.com/docs/display/current/Mule+Expression+Language+MEL
Or again you can use Spring to return a list:
<util:list id="entities">
<ref bean="myEntityWithEmail" />
</util:list>

How to make an array of strings return value in gsoap

I am struggling with gsoap's return parameter binding. I have a function which returns char** which is composed like this: {char*, ..., char*, NULL}. I want the generated web service to be able to transmit and process multiple strings in one parameter. Gsoap however generates only:
<element name="retVal" type="xsd:string" minOccurs="0" maxOccurs="1" nillable="true"/>
I would like it to return an array of strings, not a single string.
If I undesrstand your question
You can develop your own function which call the gSoap function to get the returned string from the gSoap function then split the gSoap sting to sub strings and put them into an array and then return your array of strings
I found an answer in the documentation. It turns out that I had to create a structure to be used to return the value and name the variables appropriately. In my case:
struct ns_cl_get_indexes_retVal {
int __sizeIndex;
char** index;
};
and the function in the WS-base header:
int ns__cl_get_indexes ( int32_t db_id, struct ns_cl_get_indexes_retVal& retVal );
Which resulted in a proper xml generation, as seen in wsdl:
<element name="index" type="xsd:string" minOccurs="0" maxOccurs="unbounded" nillable="true"/>

SOAP - nillable="true"

I am building a web service using the Zend Framework. I am using the Zend_Soap_AutoDiscover class for the generation of my WSDL. I am using various complex type in this web service form example:
StockItemEntity Class
class StockItemEntity {
/** #var string */
public $sStockCode;
/** #var string */
public $sQty;
public function __construct($sStockCode, $sQty){
$this->sStockCode = $sStockCode;
$this->sQty = $sQty;
}
}
WSDL Definition
<xsd:complexType name="StockItemEntity">
<xsd:all>
<xsd:element name="sStockCode" type="xsd:string" nillable="true"/>
<xsd:element name="sQty" type="xsd:string" nillable="true"/>
</xsd:all>
</xsd:complexType>
From what I understood from reading over the web the nillable="true" is there because properties of any object can be set to null. Thus the nillable="true" is need to maintain a valid XML document even if the StockItemEntity object has all its properties set to null.
My concern is that those two properties must always be passed to the web method. Is it possible to remove the "nillable=true" to sort of force the properties not to be null? Or else is there any way to force non null values in those properties. I was hoping to avoid having to validate them on the webservice side.
Thanks
Kind Regards
Gabriel
At some stage between Zend Framework 1.10.7 and 1.11.0, they introduced a piece of code that uses reflection to check if there's a default value defined for an attribute of a class and, if not, it decides that the class is "nillable" and adds nillable="True" to the attribute definition in the WSDL. This is apparently meant to improve interoperability with some flaky version of .Net.
To stop Zend Framework from adding the nillable flag, just initialize those properties when they are declared, eg.
/** #var string */
public $sStockCode = '';
/** #var string */
public $sQty = '';
Hope that helps.
nillable='true' indicates that the element CAN be null in an xml document instance.
If the element in an xml document IS null, then this is indicated by
<sStockCode xsi:nil='true' />
(the element itself must be empty)
It sounds like you need to change the xsd to nillable='false'. Also, string can be empty - if you want to prevent against empty strings with the minLength attribute in your xsd. You can also use default to specify a default value for the string if it isn't provided.
W3schools has good reference material for XSD's here
HTH

TRemotable Descendant Parent Members are not included in WSDL generated by delphi web services

I've made Web Services with Delphi in the past but most were pretty simple that just took a few parameters and returned a single value to the client. A new service I am working on calls for me to be able to send and receive complex types. Consider the following types are defined in my code:
TBaseRequest = Class(TRemotable)
private
FUsername: string;
FPassword: string;
published
Property Username: String read FUsername write FUsername;
Property Password: String read FPassword write FPassword;
End;
TBaseResponse = Class(TRemotable)
private
FStatusMessage: string;
FStatusCode: integer;
published
Property StatusMessage: string read FStatusMessage write FStatusMessage;
Property StatusCode: integer read FStatusCode write FStatusCode;
End;
TSepecialRequest = class(TBaseRequest)
private
FExtraParam: string;
published
Property ExtraParam: String read FExtraParam write FExtraParam;
end;
TSpecialResponse = class(TBaseResponse)
private
FExtraResult: string;
published
Property ExtraResult: String read FExtraResultwrite FExtraResult;
end;
All of these classes are registerd with RemClassRegistry.RegisterXSClass.
Now I've also got the following function defined in the interface for this webservice:
function SpecialMethod(request:TSepecialRequest): TSpecialResponse;
In the service code I can easily access the parent class properties like Username and Password, but if we look at the WSDL that is generated we see that the TSpecialRequest and TSpecialResponse class members are included in the schema section.
<xs:complexType name="TSpecialRequest">
<xs:complexContent>
<xs:extension base="TBaseRequest">
<xs:sequence>
<xs:element name="ExtraParam" type="xs:string"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="TSpecialResponse">
<xs:complexContent>
<xs:extension base="TBaseResponse">
<xs:sequence>
<xs:element name="ExtraResult" type="xs:string"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
This schema fragment in the WSDL shows that the TSpecials are extentions of the TBase classes and all is well except that the decription of the TBase classes are not included in the schema. I would expect there to also be a section like this, but it is missing:
<xs:complexType name="TBaseRequest">
<xs:sequence>
<xs:element name="Username" type="xs:string"/>
<xs:element name="Password" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="TBaseResponse">
<xs:sequence>
<xs:element name="StatusMessage" type="xs:string"/>
<xs:element name="StatusCode" type="xs:int"/>
</xs:sequence>
</xs:complexType>
However this schema fragment is missing from the generated WSDL. This means that any client attempting to use this service will not be able to correctly generate requests or interpret responses. For example, if I attempt to load the geneated WSDL into the WSDL importer in Delphi 2009, I get the following classes:
TSpecialRequest = class(TRemotable)
private
FExtraParam: WideString;
published
property ExtraParam: WideString read FExtraParam write FExtraParam;
end;
TSpecialResponse = class(TRemotable)
private
FStatusMessage: WideString;
FStatusCode: Integer;
published
property StatusMessag: WideString read FStatusMessage write FStatusMessage;
property StatusCode: Integer read FStatusCode write FStatusCode;
end;
The result is that the client code is unable to do things like set the username and password members that should be part of TSpecialRequest.
Does anyone have any clue why this is happening or what I can do about it?
I don't think I am going to get an answer but I have found a work around. I'm not really satisified with it, but It gets me passed the problem. The symptom described above can be avoided by using Object Composition instead of inheritance. This code functions as expected but it is less simple to use. I suppose I'll want to come up with some kind of message factory in my implementation but thats getting off topic.
This solution can be illustrated by the following code:
TBaseRequest = Class(TRemotable)
private
FUsername: string;
FPassword: string;
published
Property Username: String read FUsername write FUsername;
Property Password: String read FPassword write FPassword;
end;
TSepecialRequest = class(TRemotable)
private
FExtraParam: string;
FBaseRequest: TBaseRequest;
published
Property ExtraParam: String read FExtraParam write FExtraParam;
Property BaseRequest: TBaseRequest read FBaseRequest write FBaseRequest;
end;
Delphi only generates the schema for classes that are directly referenced in the web service's interface. Ideally it should include base classes back to TRemotable, but it doesn't.
I've found, though, if you add a dummy method to your interface with your base classes in the parameter list, Delphi will generate the missing definitions. For example:
procedure BaseClasses(BaseRequest: TBaseRequest; BaseResponse: TBaseResponse); stdcall;
This is definitely a hack and it's a bit annoying to have to expose a method like this, but it does accomplish what you want done.
I know this response is a bit late for you but maybe it'll useful for someone else down the road.