There may be circumstances where the out-of-the-box functionality for consuming SOAP web services (Call Web Service) may not be possible due to how the service is defined.
Rather than resorting to creating a Custom Smart Service or Custom Function to consume the service, first consider if the web service definition can be modified to allow it to be consumed natively in Appian. Integrating with an Integration Object should be attempted before considering customisation.
Modifying the web service definition involves changing the services WSDL and hosting the modified version where Appian can consume it. This document details the benefits, considerations and how to modify the WSDL.
A modified WSDL does not incur any extra risk that isn't already inherent to dealing with web services in general. Modifying a WSDL is the equivalent to using the Integration Object or creating a Custom Smart Service or Custom Function. All require a similar level of analysis and design to identify the problematic elements blocking out-of-the-box consumption and capturing the essential aspects of the interface. However each has their own level of effort and maintenance.
WSDL modification does not require Java expertise or extensive knowledge about how HTTP works. The modifications can be made quickly, reducing the time to test and minimizing the risks associated with custom development (support, maintenance, local development costs, etc).
Known issues with a WSDL that can be addressed by modifying the WSDL:
Before attempting to perform WSDL modification the following considerations and options must have been reviewed:
Follow the flow diagram below to identify the most appropriate solution:
To create a new WSDL from scratch, it is first necessary to understand the basic structure of a WSDL. Since there are plenty of tutorials on this only the basics will be covered. See tutorialspoint WSDL tutorial for more details.
There are 5 main sections for a WSDL to keep in mind:
Here's an example SOAP request and response. Highlighted in red are the standard SOAP elements and in blue the key pieces of information specific to this example:
Request
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:examples:helloservice"> <soapenv:Body> <urn:sayHello soapenv:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'> <firstName xsi:type='xsd:string'>John</firstName> </urn:sayHello> </soapenv:Body> </soapenv:Envelope>
Response
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:examples:helloservice"> <soapenv:Body> <urn:sayHelloResponse soapenv:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'> <greeting xsi:type='xsd:string'>Hello John</greeting> </urn:sayHelloResponse> </soapenv:Body> </soapenv:Envelope>
To create a new WSDL, we need a basic WSDL structure that we can fill out with the details identified in the request and response. Here's an example below. Highlighted in red are all the elements that need to be filled out:
Basic Blank WSDL Structure
<?xml version="1.0"?> <definitions name="?" targetNamespace="?" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="?" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <types> </types> <message name="?"> </message> <portType name="?"> <operation name="?"> <input message="?"/> <output message="?"/> </operation> </portType> <binding name="?" type="?"> <soap:binding style='?' transport='?'/> <operation name='?'> <soap:operation soapAction='?'/> <input> <soap:body encodingStyle='?' namespace='?' use='?'/> </input> <output> <soap:body encodingStyle='?' namespace='?' use='?'/> </output> </operation> </binding> <service name='?'> <documentation>WSDL Base Structure Example</documentation> <port binding='?' name='?'> <soap:address location='?'/> </port> </service> </definitions>
Here's the WSDL filled out with the information that's available in the request and response. Highlighted in red are the elements that are still missing and in blue the elements just filled out:
WSDL Filled with Request and Response Data
<?xml version="1.0"?> <definitions name="?" targetNamespace="?" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="?" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <types> <!-- No special type definitions needed. --> <!-- Only the basic xsd:string type is used in this example. --> </types> <message name="?"> <part name="firstName" type="xsd:string"/> </message> <message name="?"> <part name="greeting" type="xsd:string"/> </message> <portType name="?"> <operation name="sayHello"> <input message="?"/> <output message="?"/> </operation> </portType> <binding name="?" type="?"> <soap:binding style='?' transport='?'/> <operation name='sayHello'> <soap:operation soapAction='?'/> <input> <soap:body encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' namespace='urn:examples:helloservice' use='encoded'/> </input> <output> <soap:body encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' namespace='urn:examples:helloservice' use='encoded'/> </output> </operation> </binding> <service name='?'> <documentation>WSDL File for Hello Service - Manual</documentation> <port binding='?' name='?'> <soap:address location='?'/> </port> </service> </definitions>
The missing elements are of 2 types:
Next, we are going to fill out the generic elements. We will just choose random values different from the original tutorialspoint example to illustrate how it doesn't change the end result. Highlighted in red are the elements that are still missing and in blue the elements just filled out:
WSDL Filled With Generic Data
<?xml version="1.0"?> <definitions name="SayHelloWSDL" targetNamespace="urn:examples:helloservice:defaultNamespace" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="urn:examples:helloservice:defaultNamespace" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <types> <!-- No special type definitions needed. --> <!-- Only the basic xsd:string type is used in this example. --> </types> <message name="firstNameMessage"> <part name="firstName" type="xsd:string"/> </message> <message name="greetingMessage"> <part name="greeting" type="xsd:string"/> </message> <portType name="sayHelloPortType"> <operation name="sayHello"> <input message="tns:firstNameMessage"/> <output message="tns:greetingMessage"/> </operation> </portType> <binding name="sayHelloBinding" type="tns:sayHelloPortType"> <soap:binding style='?' transport='?'/> <operation name='sayHello'> <soap:operation soapAction='?'/> <input> <soap:body encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' namespace='urn:examples:helloservice' use='encoded'/> </input> <output> <soap:body encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' namespace='urn:examples:helloservice' use='encoded'/> </output> </operation> </binding> <service name='?'> <documentation>WSDL File for Hello Service - Manual</documentation> <port binding='tns:sayHelloBinding' name='SayHelloPort'> <soap:address location='?'/> </port> </service> </definitions>
Finally, we fill out the Web Service Configuration elements. For this example, we will just use the sample values from the tutorialspoint original example. In practice this information would be requested from the Web Service owner. Highlighted in blue are the last elements filled out.
WSDL Filled With Web Service Configuration Data
<?xml version="1.0"?> <definitions name="SayHelloWSDL" targetNamespace="urn:examples:helloservice:defaultNamespace" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="urn:examples:helloservice:defaultNamespace" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <types> <!-- No special type definitions needed. --> <!-- Only the basic xsd:string type is used in this example. --> </types> <message name="firstNameMessage"> <part name="firstName" type="xsd:string"/> </message> <message name="greetingMessage"> <part name="greeting" type="xsd:string"/> </message> <portType name="sayHelloPortType"> <operation name="sayHello"> <input message="tns:firstNameMessage"/> <output message="tns:greetingMessage"/> </operation> </portType> <binding name="sayHelloBinding" type="tns:sayHelloPortType"> <soap:binding style='rpc' transport='http://schemas.xmlsoap.org/soap/http'/> <operation name='sayHello'> <soap:operation soapAction='sayHello'/> <input> <soap:body encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' namespace='urn:examples:helloservice' use='encoded'/> </input> <output> <soap:body encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' namespace='urn:examples:helloservice' use='encoded'/> </output> </operation> </binding> <service name='Hello_Service'> <documentation>WSDL File for Hello Service - Manual</documentation> <port binding='tns:sayHelloBinding' name='SayHelloPort'> <soap:address location='http://www.examples.com/SayHello/'/> </port> </service> </definitions>
Sometimes a WSDL might reference other files, especially when it contains a complex definition. Any piece of the WSDL can be defined in its own separate file and referenced on the main WSDL via the "import" tag.
These files would be one of 2 possible types:
To ensure the Appian Web Service node reads the full WSDL definition properly when it fails to do so for multipart WSDLs, you can merge all the referenced files into a single WSDL by following these steps:
The extension tag works in the same way as Java inheritance via class extension. The extension tag is used as a placeholder to indicate that the referenced data type’s properties should be included as well on the data type using the extension tag. See w3schools extension element tutorial for more details.
Using the example from the w3schools tutorial, here's an illustration on how to transform extension tags to generate an equivalent data type definition that doesn't rely on the extension tag.
Highlighted in red are the elements that need to be removed from the original data type with extension, and in blue the elements that have to be moved/added to the data type without extension.
Data Type With Extension
<?xml version="1.0"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="employee" type="fullpersoninfo"/> <xs:complexType name="personinfo"> <xs:sequence> <xs:element name="firstname" type="xs:string"/> <xs:element name="lastname" type="xs:string"/> </xs:sequence> </xs:complexType> <xs:complexType name="fullpersoninfo"> <xs:complexContent> <xs:extension base="personinfo"> <xs:sequence> <xs:element name="address" type="xs:string"/> <xs:element name="city" type="xs:string"/> <xs:element name="country" type="xs:string"/> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> </xs:schema>
Data Type Without Extension
<?xml version="1.0"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="employee" type="fullpersoninfo"/> <xs:complexType name="fullpersoninfo"> <xs:sequence> <xs:element name="firstname" type="xs:string"/> <xs:element name="lastname" type="xs:string"/> <xs:element name="address" type="xs:string"/> <xs:element name="city" type="xs:string"/> <xs:element name="country" type="xs:string"/> </xs:sequence> </xs:complexType> </xs:schema>
To add a SOAP header to a WSDL definition, there are 3 sections that need to be updated:
The SOAP header itself is defined in the operation input/output definitions. It points to a message definition, which contains the different parts of the header. The header parts are mapped to elements, which have a defined data type associated it to them, be it simple or complex.
Here's a minimalist example of a WSDL definition that contains a SOAP header to capture user credentials. Highlighted in blue are the elements involved in defining the SOAP header.
<?xml version="1.0"?> <definitions name="HelloService" targetNamespace="http://www.examples.com/wsdl/HelloService.wsdl" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://www.examples.com/wsdl/HelloService.wsdl" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:types="urn:examples:helloservice:types"> <types> <xsd:schema targetNamespace="urn:examples:helloservice:types"> <xsd:element name="SecurityElement" type="types:SecurityType"/> <xsd:complexType name="SecurityType"> <xsd:sequence> <xsd:element name="username" type="xsd:string"/> <xsd:element name="password" type="xsd:string"/> </xsd:sequence> </xsd:complexType> </xsd:schema> </types> <message name="SayHelloRequest"> <part name="firstName" type="xsd:string"/> </message> <message name="SayHelloResponse"> <part name="greeting" type="xsd:string"/> </message> <message name="SecurityMessage"> <part name="SecurityHeader" element="types:SecurityElement"/> </message> <portType name="Hello_PortType"> <operation name="sayHello"> <input message="tns:SayHelloRequest"/> <output message="tns:SayHelloResponse"/> </operation> </portType> <binding name="Hello_Binding" type="tns:Hello_PortType"> <soap:binding style='rpc' transport='http://schemas.xmlsoap.org/soap/http'/> <operation name='sayHello'> <soap:operation soapAction='sayHello'/> <input> <soap:header message='tns:SecurityMessage' part='SecurityHeader' use='literal'/> <soap:body encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' namespace='urn:examples:helloservice' use='encoded'/> </input> <output> <soap:body encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' namespace='urn:examples:helloservice' use='encoded'/> </output> </operation> </binding> <service name='Hello_Service'> <documentation>WSDL File for HelloService</documentation> <port binding='tns:Hello_Binding' name='Hello_Port'> <soap:address location='http://www.examples.com/SayHello/'/> </port> </service> </definitions>
A service may be secured using WS-Security UsernameToken, but not define it in the published WSDL. The WSDL can be updated to include this policy.
This is a minimalist example of a WSDL definition that contains a WS-Security UsernameToken policy definition. Highlighted in red is the policy identifier and in blue the key pieces of information added to this example:
<?xml version="1.0"?> <definitions name="HelloService" targetNamespace="http://www.examples.com/wsdl/HelloService.wsdl" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://www.examples.com/wsdl/HelloService.wsdl" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:sp="http://schemas.xmlsoap.org/ws/2002/12/secext"> <wsp:UsingPolicy wsdl:required="true"/> <wsp:Policy wsu:Id="UsernameTokenPolicy"> <wsp:ExactlyOne xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"> <wsp:All> <wsp:Policy xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> <wsp:ExactlyOne> <wsp:All> <sp:SupportingTokens xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"> <wsp:Policy xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"> <sp:UsernameToken/> </wsp:Policy> </sp:SupportingTokens> </wsp:All> </wsp:ExactlyOne> </wsp:Policy> </wsp:All> </wsp:ExactlyOne> </wsp:Policy> <types> <!-- No special type definitions needed. --> <!-- Only the basic xsd:string type is used in this example. --> </types> <message name="SayHelloRequest"> <part name="firstName" type="xsd:string"/> </message> <message name="SayHelloResponse"> <part name="greeting" type="xsd:string"/> </message> <portType name="Hello_PortType"> <operation name="sayHello"> <input message="tns:SayHelloRequest"/> <output message="tns:SayHelloResponse"/> </operation> </portType> <binding name="Hello_Binding" type="tns:Hello_PortType"> <wsp:Policy> <wsp:PolicyReference URI="UsernameTokenPolicy"/> </wsp:Policy> <soap:binding style='rpc' transport='http://schemas.xmlsoap.org/soap/http'/> <operation name='sayHello'> <soap:operation soapAction='sayHello'/> <input> <soap:body encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' namespace='urn:examples:helloservice' use='encoded'/> </input> <output> <soap:body encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' namespace='urn:examples:helloservice' use='encoded'/> </output> </operation> </binding> <service name='Hello_Service'> <documentation>WSDL File for HelloService</documentation> <port binding='tns:Hello_Binding' name='Hello_Port'> <soap:address location='http://www.examples.com/SayHello/'/> </port> </service> </definitions>
SoapUI can be used to test the different examples illustrated above.