TCLWS (Web Services for Tcl) as SAP Client

HaO 2015-04-27: I am attacking the subject to write a more or less generic TCLWS Web Service client for SAP. This is my "progress report page", which might involve the next days when I progress.

Thanks to Gerald W. Lester for the great package !

Literature

This is the book I am using for reference: [L1 ]

Authentication

SAP has many authentication and transport security options. I tried HTTP Basic Authentication and no transport layer security.

Within this clt thread [L2 ], JMar wrote how to use basic authentication:

#-- http basic authentication
set authinfo [ base64::encode $szUsername:$szPassword ]
set httpHeader [ list Authorization "Basic $authinfo" ]

set szResponseDct [ ::WS::Client::DoCall $szServiceName myMethod $myRequest $httpHeader ] 

This worked out of the box.

WSDL File

The WSDL file delivered by SAP parses out of the box.

The URL may be taken from SAP "SOAMANAGER" and must include the bindings. URLs from the Web service wizard (SE80) don't contain the binding.

CALL

The main Information is that SAP only supports WSDL 1.1 [L3 ]Wikipedia WSDL .

When the service is called without setting any options in TCLWS, one get the error from SAP:

Fehler bei der Web-Service-Verarbeitung; Weitere Details im Web-Service-Fehlerprotokoll auf Provider-Seite (UTC-Zeitstempel ...; Transaktions-ID ...)

this is German, English would be something like: 'Error in Web service processing; more details in the error protocol of the provider'.

Here is the request sent with standard options:

<?xml version="1.0"  encoding="utf-8"?>
<SOAP-ENV:Envelope
  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
  xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns="urn:sap-com:document:sap:rfc:functions"
  xmlns:tns1="urn:sap-com:document:sap:rfc:functions"
  xmlns:w="http://schemas.xmlsoap.org/wsdl/"
  xmlns:d="http://schemas.xmlsoap.org/wsdl/soap/"
  xmlns:tns2="http://schemas.xmlsoap.org/wsdl/soap12/"
  xmlns:tns3="http://schemas.xmlsoap.org/wsdl/http/"
  xmlns:tns4="http://schemas.xmlsoap.org/wsdl/mime/"
  xmlns:tns5="http://schemas.xmlsoap.org/ws/2004/09/policy"
  xmlns:tns6="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
  <SOAP-ENV:Body>
    <tns1:BAPI_NAME>
      <tns1:MATERIAL>000000000000636339</tns1:MATERIAL>
    </tns1:BAPI_NAME>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Here is what is proposed by SAP (SE80 F8 "Test Web Service"):

<n0:BAPI_NAME xmlns:n0="urn:sap-com:document:sap:rfc:functions">
 <MATERIAL>000000000000636339</MATERIAL>
</n0:BAPI_NAME>

When looking to the protocol, one finds out, that the passed variables were not detected.

I checked the available options and tried the with SAP original doc :

OptionDefaultAfter WSDL parse (if different)Set toComplementary info
contentTypetext/xml;charset=utf-8
locationhttp://saphost.de:8025/sap/bc/srt/rfc/sap/zws_bapi_name/001/service0/binding0 URL to send http request to
skipHeaderLevel00No changes on request document
skipLevelOnReply00Also look for results when Tag <ENV:header> is not present
skipLevelWhenActionPresent00Removes xml tag '<tns1:BAPI_NAME>' in upper query. Implies 'skipLevelOnReply'.
suppressTargetNS001Do not output namespace prefix "tns1:" in front of call variables. In the upper example query, the xml tag '<tns1:MATERIAL>' is replaced by '<MATERIAL>'. This looks much more like the SAP sample.
targetNamespacetns1 urn:sap-com:document:sap:rfc:functions w http://schemas.xmlsoap.org/wsdl/ xs http://www.w3.org/2001/XMLSchema d http://schemas.xmlsoap.org/wsdl/soap/ tns2 http://schemas.xmlsoap.org/wsdl/soap12/ tns3 http://schemas.xmlsoap.org/wsdl/http/ tns4 http://schemas.xmlsoap.org/wsdl/mime/ tns5 http://schemas.xmlsoap.org/ws/2004/09/policy tns6 http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd The book example has none of those. I will remove them in a future try.
parseInAttr{}{}Hard coded to 1.
genOutAttr{}{}Utility option hard coded to 1
valueAttrCompatiblityMode11How attributes are represented in input/output dict
suppressNS{}{}List of namespace prefixes to suppress. Option "suppressTargetNS" sets "tns1" on this list.
useTypeNs{}{}Add type namespace in query xml. No change observed.
nsOnChangeOnly{}{}Add type namespace in query xml only if different to tns1. No changes observed.
noTargetNs001Does not add attribute "xmlns <Targetnamespace>" to request xml tag "SOAP-ENV:Envelope". In the example query, this is 'xmlns="urn:sap-com:document:sap:rfc:functions"'. This is also the case in the example in the book so I tried this option.
errorOnRedefine00Allow to add a service with the same name as an existing service.
allowOperOverloading11An operation may exist twice in the WSDL
UseNS{}WS utility option hard coded to 0.
ValueAttr{}WS utility option hard coded to {}.

Here is what was sent with options "suppressTargetNS" and "noTargetNs" and with worked with SAP:

<SOAP-ENV:Envelope
  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
  xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:tns1="urn:sap-com:document:sap:rfc:functions"
  xmlns:w="http://schemas.xmlsoap.org/wsdl/"
  xmlns:d="http://schemas.xmlsoap.org/wsdl/soap/"
  xmlns:tns2="http://schemas.xmlsoap.org/wsdl/soap12/"
  xmlns:tns3="http://schemas.xmlsoap.org/wsdl/http/"
  xmlns:tns4="http://schemas.xmlsoap.org/wsdl/mime/"
  xmlns:tns5="http://schemas.xmlsoap.org/ws/2004/09/policy"
  xmlns:tns6="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
  <SOAP-ENV:Body>
    <tns1:BAPI_NAME>
      <MATERIAL>
        000000000000636339
      </MATERIAL>
    </tns1:BAPI_NAME>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Here is the response of SAP:

<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
  <soap-env:Header />
  <soap-env:Body>
    <n0:BAPI_NAMEResponse xmlns:n0="urn:sap-com:document:sap:rfc:functions">
      <MATERIALPLANTDATA>
        <PUR_GROUP />
        <ISSUE_UNIT />
      </MATERIALPLANTDATA>
      <MATERIAL_GENERAL_DATA>
        <MATL_DESC>Sunshine in Boxes</MATL_DESC>
      </MATERIAL_GENERAL_DATA>
      <RETURN>
        <TYPE>S</TYPE>
        <CODE />
        <MESSAGE />
        <LOG_NO />
        <LOG_MSG_NO>000000</LOG_MSG_NO>
        <MESSAGE_V1 />
        <MESSAGE_V2 />
        <MESSAGE_V3 />
        <MESSAGE_V4 />
      </RETURN>
    </n0:BAPI_NAMEResponse>
  </soap-env:Body>
</soap-env:Envelope>

and the returned dict looks as follows:

{
MATERIALPLANTDATA {
  PUR_GROUP {}
  ISSUE_UNIT {}
}
MATERIAL_GENERAL_DATA {
  MATL_DESC "Sunshine in Boxes"
}
RETURN {
  TYPE S
  CODE {}
  MESSAGE {}
  LOG_NO {}
  LOG_MSG_NO 000000
  MESSAGE_V1 {}
  MESSAGE_V2 {}
  MESSAGE_V3 {}
  MESSAGE_V4 {}
}

Well dont, TCLWS !