Link to home
Start Free TrialLog in
Avatar of erlythornton
erlythornton

asked on

How do I Modify this WSDL to add specific Name Value Pairs?

Hello,

I have a WSDL that currently specifies that I can send an unbounded number of name value pairs inside a specific message tag.
..
<agent-parameter>
  <name>userSpecifiedItem1Name</name>
  <value>userSpecifieditem1Value</value>
</agent-parameter>
<agent-parameter>
  <name>userSpecifiedItem2Name</name>
  <value>userSpecifieditem2Value</value>
</agent-parameter>

What I would like to do is instead specify in the WSDL the contents of the name and let the user map the value.  In the WSDL it will already have contents for the inside the <name>Hardcoded</name> and let the user send whatever value they would like.  I would like to add as many hardcoded name value pairs as I would want by changing the WSDL to add the values as needed.

The result would be:

<agent-parameter>
  <name>hardCodedItem1Name</name>
  <value>userSpecifieditem1Value</value>
</agent-parameter>
<agent-parameter>
  <name>hardCodedItem2Name</name>
  <value>userSpecifieditem2Value</value>
</agent-parameter>

How do I modify this WSDL to perform that action.  Please look at the agent-parameters element...
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace="http://externaltask.api.newscale.com" xmlns:soapenc12="http://www.w3.org/2003/05/soap-encoding" xmlns:tns="http://externaltask.api.newscale.com" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc11="http://schemas.xmlsoap.org/soap/encoding/" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
  <wsdl:types>
    <xsd:schema elementFormDefault="qualified" targetNamespace="http://externaltask.api.newscale.com">
      <xsd:element name="message">
        <xsd:annotation>
          <xsd:documentation>Name: NewscaleXML External Task Web Service Listener
                              Name: Invoke ServiceLink via webservice to update extenal tasks</xsd:documentation>
        </xsd:annotation>
        <xsd:complexType>
          <xsd:annotation>
            <xsd:documentation>Inbound message may either contain add-comments or send-parameters or take-action or an combination of any of these.</xsd:documentation>
          </xsd:annotation>
          <xsd:sequence>
            <!--Message types-->
            <xsd:element minOccurs="0" name="add-comments">
              <xsd:annotation>
                <xsd:documentation>Inbound Comments. Submit atleast one comment</xsd:documentation>
              </xsd:annotation>
              <xsd:complexType>
                <xsd:sequence>
                  <xsd:element maxOccurs="unbounded" name="comment" type="xsd:string"/>
                </xsd:sequence>
              </xsd:complexType>
            </xsd:element>
            <xsd:element minOccurs="0" name="send-parameters">
              <xsd:annotation>
                <xsd:documentation>Inbound agent parameters. Submit atleast one parameter name value pair</xsd:documentation>
              </xsd:annotation>
              <xsd:complexType>
                <xsd:sequence>
                  <xsd:element maxOccurs="unbounded" ref="tns:agent-parameter"/>
                </xsd:sequence>
              </xsd:complexType>
            </xsd:element>
            <xsd:element minOccurs="0" name="take-action">
              <xsd:annotation>
                <xsd:documentation>Inbound.</xsd:documentation>
              </xsd:annotation>
              <xsd:complexType>
                <xsd:attribute name="action" type="tns:action-type" use="required"/>
              </xsd:complexType>
            </xsd:element>
          </xsd:sequence>
          <xsd:attribute name="channel-id" type="xsd:string" use="optional"/>
          <xsd:attribute name="topic-id" type="xsd:string" use="optional"/>
        </xsd:complexType>
      </xsd:element>
      <xsd:element name="agent-parameter">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="name" type="tns:nonempty-string"/>
            <xsd:element maxOccurs="unbounded" name="value" type="xsd:string"/>
          </xsd:sequence>
          <xsd:attribute default="false" name="multi-valued" type="xsd:boolean"/>
        </xsd:complexType>
      </xsd:element>
      <xsd:simpleType name="nonempty-string">
        <xsd:restriction base="xsd:string">
          <xsd:minLength value="1"/>
        </xsd:restriction>
      </xsd:simpleType>
      <!--Simple Type declarations-->
      <xsd:simpleType name="action-type">
        <xsd:restriction base="xsd:string">
          <xsd:enumeration value="done"/>
          <xsd:enumeration value="cancel"/>
          <xsd:enumeration value="ok"/>
          <xsd:enumeration value="approve"/>
          <xsd:enumeration value="reject"/>
          <xsd:enumeration value="skip"/>
        </xsd:restriction>
      </xsd:simpleType>
      <!--Response-->
      <xsd:element name="response">
        <xsd:annotation>
          <xsd:documentation>Name: NewscaleXML External Task Web Service Listener Response: Response from the webservice to the webservice request</xsd:documentation>
        </xsd:annotation>
        <xsd:complexType>
          <xsd:sequence>
            <!--Request  Status-->
            <xsd:element maxOccurs="1" minOccurs="1" name="status-code" type="tns:status-type">
              <xsd:annotation>
                <xsd:documentation>Outbound.</xsd:documentation>
              </xsd:annotation>
              <!--<xsd:complexType>
            <xsd:attribute name="action" type="tns:status-type" use="required"/>
          </xsd:complexType>-->
            </xsd:element>
            <!--Additonal Response Information-->
            <xsd:element maxOccurs="unbounded" minOccurs="0" name="status-message" type="xsd:string">
              <xsd:annotation>
                <xsd:documentation>Outbound.</xsd:documentation>
              </xsd:annotation>
            </xsd:element>
          </xsd:sequence>
          <xsd:attribute name="channel-id" type="xsd:string" use="optional"/>
        </xsd:complexType>
      </xsd:element>
      <!--Valid status-code declarations-->
      <xsd:simpleType name="status-type">
        <xsd:restriction base="xsd:string">
          <xsd:enumeration value="success"/>
          <xsd:enumeration value="failed"/>
        </xsd:restriction>
      </xsd:simpleType>
      <xsd:complexType name="FaultDetail">
        <xsd:sequence>
          <xsd:element minOccurs="0" name="errorCode" nillable="true" type="xsd:string"/>
          <xsd:element minOccurs="0" name="errorMessage" nillable="true" type="xsd:string"/>
        </xsd:sequence>
      </xsd:complexType>
      <xsd:element name="TaskFault" type="tns:FaultDetail"/>
    </xsd:schema>
  </wsdl:types>
  <wsdl:message name="processMessageRequest">
    <wsdl:part name="message" element="tns:message"/>
  </wsdl:message>
  <wsdl:message name="TaskFault">
    <wsdl:part name="TaskFault" element="tns:TaskFault"/>
  </wsdl:message>
  <wsdl:message name="processMessageResponse">
    <wsdl:part name="response" element="tns:response"/>
  </wsdl:message>
  <wsdl:portType name="TaskServicePortType">
    <wsdl:operation name="processMessage">
      <wsdl:input name="processMessageRequest" message="tns:processMessageRequest"/>
      <wsdl:output name="processMessageResponse" message="tns:processMessageResponse"/>
      <wsdl:fault name="TaskFault" message="tns:TaskFault"/>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="TaskServiceHttpBinding" type="tns:TaskServicePortType">
    <wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="processMessage">
      <wsdlsoap:operation soapAction="processMessage"/>
      <wsdl:input name="processMessageRequest">
        <wsdlsoap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="processMessageResponse">
        <wsdlsoap:body use="literal"/>
      </wsdl:output>
      <wsdl:fault name="TaskFault">
        <wsdlsoap:fault name="TaskFault" use="literal"/>
      </wsdl:fault>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="TaskService">
    <wsdl:port name="TaskServiceHttpPort" binding="tns:TaskServiceHttpBinding">
      <wsdlsoap:address location="http://localhost:8089/IntegrationServer/services/TaskService"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>
Avatar of Richard Quadling
Richard Quadling
Flag of United Kingdom of Great Britain and Northern Ireland image

What would the handler look like? I generate my wsdl files from my code using docblocks and type hints.

It sounds like you can have something like (PHP example)

$agent-parameter = array('unknownname1' => 'var1', 'unknownname2' => 'var2');

but want ...

$agent-parameter = array('knownname1' => 'var1', 'knownname2' => 'var2');


Personally, that would change the call from ...

method(array agent-parameter = null)

to ...

method(array agent-paramter = null, string knownname1 = null, string knownname2 = null)

A different method signature.

If you are using some sort of automated process on the server side, the WSDL file normally follows the method calls.

Avatar of erlythornton
erlythornton

ASKER

Unfortunately I need to edit by hand by manually adding code blocks to create this.   In the end I will have to have multiple text copies of the WSDL that will be used by a 3rd Party tool to generate outbound SOAP Messages customized per situation.    The reason for this is the 3rd Party tool incorporates a Wizard that imports a WSDL and allows the user to map internal data fields to the inputs in the WSDL.  My web service will take any name/value pairs.  The wizard however not seeing any explicit fields instead seing unbounded name/value pairs throws up and will not allow a field mapping.  So this is trying to trick the 3rd party tool wizard into generating content by giving it something to map to.   Once the method of how to do this is discovered through this forum hopefully, then I need to make it repeatable so another group can maintain the different WSDLs
Avatar of Kevin Cross
The WSDL is the WSDL in my humble opinion, as it is defining your web service. If your web service behaves a specific way that is what the WSDL will generate as. For each client that wants to consume your web service they will take the WSDL and generate proxy classes. It is perfectly fine for a specific client implementation to say create a plain old object that has specific properties such as Key1, Key2, Key3 and Key4 and then the client knows specifically what to fill in. When the client invokes | consumes the service though it passes the values in the same key/value array|list your web service is expecting.

In summary: make the clients change their implementation of consuming your service versus changing how your service is consumed!
But how is the client taking the WSDL file going to make valid calls? The server side will have to know these names too.The wsdl file is a contract. Not a suggestion.Please show some example server methods where this is going to be feasible.[EDITED : This was written BEFORE mwvisa1's comment, but posted AFTER .-]
Uhh, just a thought.  I have seen countless questions from folks confused by SOAP.  Never seen one yet from somebody that could not understand a half-well-documented RESTful API.  The Yahoo! Geocoder is a good example, although Yahoo has deprecated it in favor of this:
http://developer.yahoo.com/geo/placefinder/

In the REST paradigm, each call is atomic and each response is complete.  The caller has no need to know how the service produces its output.  Well-designed REST interfaces use some kind of version number in the URI so that new features can be added without disrupting the interface that existing clients may depend on.

Here is a skeletal example of REST in action.  Best of luck with your project, ~Ray
<?php // RAY_REST_get_last_name.php
error_reporting(E_ALL);


// DEMONSTRATE HOW A RESTFUL WEB SERVICE WORKS
// CALLING EXAMPLE:
// file_get_contents('http://laprbass.com/RAY_REST_get_last_name.php?key=ABC&name=Ray');


// OUR 'DATA MODEL'
$dataModel 
= array
( 'Brian'   => 'Portlock'
, 'Ray'     => 'Paseur'
, 'Richard' => 'Quadling'
)
;

// TEST THE API KEY
$key = FALSE;
if (isset($_GET["key"])) $key = $_GET["key"];
if ($key !== 'ABC') die('BOGUS API KEY');

// LOOK UP THE LAST NAME
$name="?";
if (isset($_GET["name"])) $name = $_GET["name"];
if (array_key_exists($name, $dataModel))
{
    die("$dataModel[$name]");
}
else die('UNKNOWN');

Open in new window

What I am dealing with specifically is BMC Remedy as the client.  There is not going to be any custom coded client.  If that were the case we would not have an issue.  The issue present is that this Enterprise Level Software Tool employed by Remedy in order to consume a Web Service has to map internal Form View fields to specific inputs on the web service.  

For example If it has a form fields of say:
Field1
Field2
Field3

The tool imports my WSDL and looks for a specific field to map Field1, a specific field to map Field 2 and so on.  My Web Service however does not have a specific mapping for those fields.  It will accept the information if you send it in the form:
<agent-parameter>
<name>Field1</name>
<value>Value of Field 1</value>
</agent-parameter>

And so on for the rest of the fields.  My question is how do change my WSDL manually to code ComplexTypes where I could define the above.

 I am not looking for a Perl program, especially since I am not a Perl programmer.  But thanks for the effort.
 I do not have the option of rewriting my web service and generating a WSDL as there could be tens of custom inputs (name value pairs) required for different circumstances.
I do not have the option of writing a new client tool.

I need to find out which lines of code to take my web service definition from the general, "Send me any name value pairs, I don't care" to send me these specific name value pairs but in the end make them look like the above.  And if you want to add any more, just add these lines of code...

The end result will still be a WSDL but would not be published but saved as a text file.  The Remedy tool has the ability to import the WSDL from text instead of having it be online.  

Is this clearer?
WSDL = Web Service Definition Language
The WSDL Is defining a specific web service. Simply changing a WSDL to what you want won't make the client interop with your service as you would expect. With the kind of web service you are describing it probably takes in an array or list of key/value pairs. If you define this as set fields then if your service expects one parameter that is an array and instead you send it six parameters that are strings it will fail indicating there is no such web method definition.

I have been in your situation and what you need to do is build a translator -- think EDI. What happens in EDI is that it provides each side of the business transaction to send or receive data how they expect and it does the mapping in the middle.

If this service is only used by Remedy then change the service and let the WSDL be generated properly. If other clients will use the service, then create another process which the Remedy system will talk to that then talks to your service.
@Ray, the most significant difference for me between SOAP and REST is that SOAP is a documented standard describing pretty much everything needed to supply data in a known format  to a server as well as the responses to expect from the server.

The WSDL file is a contract. If you don't match the contract, you don't get in (hopefully). A good WSDL file can fully document the service. So much so, a WSDL2xxxx script can produce all the necessary classes to completely wrap the client request in easy to use classes (PHP - via a sourceforge tool, Java and .NET all have tools for this specific purpose), along with any embedded documentation docblocks, allowing you to produce high quality documentation automatically (pear/phpdocumentor for PHP for example).

From what I understand with REST is that it is a convention.

Having said that, as with all things, not everyone interprets the standard in the same way. And as always, the standard's body allows the service developers too many choices in terms of how the encode the content.

And then there's the WSDL 2.0 which seems to have been a work-in-progress for a long time.


And, in the end, the end-user has to go with what the service provides. If there is only a SOAP interface, then SOAP it is.


Personally, I like the nice rigid structure of SOAP. I suspect, with a suitable Poka-yoke, REST is probably easier, but as a developer, I'd have to write more documentation. For me and the tools I use, I have to document my service to be able to test my service using standard tools. I don't write ANY client side code to translate the service. I use wsdl2php and pear/phpdocumentor. From there I'll build a small test bed.





@erlythornton

From what I understand, your ...


Field1
Value of Field 1


needs to look like ...


Value of Field 1


You cannot put the value of an element (i.e. Field1 being the value of ) into a WSDL file. The value (Field1) is NOT the responsibility of the WSDL file.

The WSDL file can describe complex types and you can assign the complex types as parameters to methods of the service.

That's what a WSDL file is. You cannot amend that.

Maybe you just want a simple XML mapping tool?

As mwvisa1 says "If you define this as set fields then if your service expects one parameter that is an array and instead you send it six parameters that are strings it will fail indicating there is no such web method definition." and as I said in my first comment (regarding parameters).

Basically, you're barking up the wrong tree and there's not a lot you can do about it.

Amending the WSDL file in the way you are describing will no longer be a WSDL file and SOAP service consumer/supplier toolkits won't work.

I don't know about other languages, but for PHP, we (i.e. the developers) don't generally take apart the XML requests, that is done by a very complex library which turns the request into an instance of a class which is expected to match the WSDL. In most cases, the WSDL file is produced based upon the service code (via docblocks for example). Not always, admittedly.
@RQuandling,

I need the input from the client to look like:

<agent-parameter>
<name>Field1</name>
<value>Value of Field 1</value>
</agent-parameter>
<agent-parameter>
<name>Field2</name>
<value>Value of Field 1</value>
</agent-parameter>

Your assertion that you cannot specify a value for what's in the <name/> tag makes sense though in experimentation I was able to add default="blah" in the tag and it would show up in SOAPUI automatically generated request.  

This Web service is used by multiple client systems and tools.  That is why it is so generic.  Once again, that is why I do not have the option to generate a custom web service for Remedy to send inbound messages to.

@mwvisa, I regularly use SOAPUI to send messages to this service for testing.  As long as I send it strings of Name value pairs it can accept them.   It is not looking for an ARRAY object, though I understand that object wise, that is what it looks like.  An array called agent-parameter with a bunch of name value rows.

It seems that I remember taking a class a long time ago when web services were first starting to be used (2000, 2001?) where we actually programmed a WSDL by hand. Perhaps the auto code generation tools were not widely used then.   I hoped that knowledge still existed.  I have clearly forgotten how which was why I ended up here in this forum.  I try not to use Experts Exchange until I have exhausted everything else.   I was hoping there was someone out there who still knew how to do that.

A WSDL defines the interface to a web service.  And yes it is automatically generated always nowadays but it is not the web service itself.  I was just trying to tweak the interface to be more specific yet still send name value pairs.

Question, is it possible to specify for example:

<agent-parameter>
<name></name>
<value></value>
</agent-parameter>
<agent-parameter>
<name></name>
<value></value>
</agent-parameter>

Possibly the Remedy tool would see 2 sets of Name Value pairs and be able to map 1 to 1 in that manner.

Regarding XML Mapping tool, how would this work in an implementation scenario?  Where would the tool run?
ASKER CERTIFIED SOLUTION
Avatar of Kevin Cross
Kevin Cross
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hopefully my memory serves me correctly with the above. Regarding the mapping tool, you can run this on same system as remedy or some other box. It just depends on how much resources this needs. When I have done it in in the past where we did it as a quick interconnect, we just had an extremely lightweight service that received requests on port 80 as if it were a web server did the XSL (transformation) of the XML from the one system to the others and then made the appropriate service call on other system and then handled the response back to the caller in its XML format.

You can see if WCF is good for this. Haven't gotten to play with that as much as I would like lately.
I'll sign off on this question now, but to Richard's comments about REST and SOAP

REST is an architectural style.  The WWW (what you're using right now) is a canonical example of REST in action.  The Wiki article is pretty accurate.
http://en.wikipedia.org/wiki/Representational_State_Transfer

See the part about the SOAP RPC comparison, especially the line that says, "encourages each application designer to define a new and arbitrary vocabulary."  I agree that the SOAP interface can be considered a contract, and like all contracts, it has a lot of moving parts that you've got to get right.  And that's why I prefer REST interfaces.  They are easier to document and understand (consider the "owners guide" versus the "maintenance contract").

Here's a link to one of mine.
http://www.carpool2camp.org/v2api/apidocs.php

Best to all, ~Ray
This got me on the right track.  The name attribute is invalid however for the agent-parameter namespace but I am able to make progress.  While I can create multiple name value pairs, I cannot assign a default value for the name tag in the agent-parameter for more than one pair.  The default value ends up being the same for all, but I'd like to have them being different.   I will accept this as a solution