[SalesForce] Issue with SOAP Header created via Apex

We are trying to make a SOAP callout to an API. The sample header provided by the API is as follows

<env:Header>
     <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
           <UsernameToken>
                <Username>abcdef</Username>
                <Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">*****</Password>
            </UsernameToken>
      </Security>
</env:Header>

After wsdltoapex, when I made the call out I get the following Header as a part of my request.

<env:Header>
     <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
           <UsernameToken>
                <Username>abcdef</Username>
                <Password>*****</Password>
            </UsernameToken>
      </Security>
</env:Header>

I could see that the Type attribute in the header generated by the apex class is missing.

This is the apex code in the class that constructs the Username and password tags in the XML

public class UsernameToken_element {

        public UsernameToken_element(String username, String password) {

             this.username = username;
             this.password = password;
        }

        public String username;
        public String password;
        private String[] username_type_info = new String[]{'Username','http://www.w3.org/2001/XMLSchema','string','1','1','false'};
        private String[] password_type_info = new String[]{'Password','http://www.w3.org/2001/XMLSchema','string','1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd','true','false'};
        private String[] field_order_type_info = new String[]{'username','password'};
}

In order to include the Type attribute in the Password tag where should I include it in the Apex code? I tried adding it as another private string and also tried to include it in the password_type_info. But it was not added in the password tag.

Best Answer

Looking at http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd I see

<xsd:complexType name="PasswordString">
  <xsd:annotation>
    <xsd:documentation>
      This type is used for password elements per Section 4.1.
    </xsd:documentation>
  </xsd:annotation>
  <xsd:simpleContent>
    <xsd:extension base="wsse:AttributedString">
      <xsd:attribute name="Type" type="xsd:anyURI"/>
    </xsd:extension>
  </xsd:simpleContent>
</xsd:complexType>

So they Type attribute can just be any URL.

You will likely need to use the rare and elusively documented _att_info string array to populate the Attribute. This would be a good time to pause and consider voting for Documentation for WebServiceCallout.

The open source version of WSDL2Apex includes a test case against this attribute.

From the source WSDL:

        <xs:complexType name="Angle">
            <xs:simpleContent>
                <xs:extension base="xs:double">
                    <xs:attribute name="uom" type="tns:AngularUnit" use="required"/>
                </xs:extension>
            </xs:simpleContent>
        </xs:complexType>

Corresponding test assertion.

public class wwwMapinfoComMidevServiceUnitsV1 {
    public class Angle {
        public String uom;
        private String[] uom_att_info = new String[]{'uom'};
        private String[] apex_schema_type_info = new String[]{'http://www.mapinfo.com/midev/service/units/v1','false','false'};
        private String[] field_order_type_info = new String[]{};
    }
}

So, you create a member on the class to store the actual attribute value and a memberName_att_info String array that contains the name of the attribute.

public class UsernameToken_element {

    public UsernameToken_element(String username, String password) {

        this.username = username;
        passwordToken = new password_element(password);
        system.debug('passwordTokennnn '+passwordToken);
    }

    public String username;
    public password_element passwordToken;        
    private String[] username_type_info = new String[]{'Username','http://www.w3.org/2001/XMLSchema','string','1','1','false'};
    private String[] passwordToken_type_info = new String[]{'Password','http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText','string','1','1','false'};
    private String[] apex_schema_type_info = new String[]{'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd','true','false'};
    private String[] field_order_type_info = new String[]{'username','passwordToken'};
}

public class password_element{
    public password_element( String password) {
        system.debug('passworddddd '+password);
        this.password = password;
        type = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText';             
    }


    public String password;
    public string type; // Set to 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText'
    public String[] type_att_info = new String[]{'Type'};        
    private String[] password_type_info = new String[]{'Password','http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText','string','1','1','false'};
    private String[] apex_schema_type_info = new String[]{'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd','true','false'};
    private String[] field_order_type_info = new String[]{'password'};
}

Give that a try, it might work. I say might as I made a note in my version of Wsdl2Apex that

"Simple Content Types with Attributes are not supported by WebServiceCallout.invoke"

Related Topic