Showing posts with label WSDL. Show all posts
Showing posts with label WSDL. Show all posts

Tuesday, June 13, 2017

Importing the Salesforce Summer 17 (v40.0) Tooling and Metadata APIs to .NET

The Salesforce Summer '17 release includes the return of an old friend in both the Tooling and Metadata APIs. After refreshing to the latest WSDLs and making sure the project builds it starts producing errors like the following with calling the API:

error CS0029: Cannot implicitly convert type 'XYZ.SalesforceConnector.SalesforceTooling.NavigationMenuItem' to 'XYZ.SalesforceConnector.SalesforceTooling.NavigationMenuItem[]'

I say it's an old friend, because I've seen this before with the Partner API when it transitioned to Winter '15 (v32.0) for the QuickActionLayoutItem. It's also cropped up with Winter '13 (v29.0). Thankfully the same fix that was applied in those cases will also work here.

The problem is the navigationLinkSet property that gets generated with a multidimensional array return type. The type for the corresponding XmlArrayItemAttribute is incorrect.

Before Fix

/// 
[System.Xml.Serialization.XmlArrayItemAttribute("navigationMenuItem", typeof(NavigationMenuItem), IsNullable=false)]
public NavigationMenuItem[][] navigationLinkSet {
    get {
        return this.navigationLinkSetField;
    }
    set {
        this.navigationLinkSetField = value;
    }
}

After Fix

/// 
[System.Xml.Serialization.XmlArrayItemAttribute("navigationMenuItem", typeof(NavigationMenuItem[]), IsNullable=false)]
public NavigationMenuItem[][] navigationLinkSet {
    get {
        return this.navigationLinkSetField;
    }
    set {
        this.navigationLinkSetField = value;
    }
}

The same fix needs to be applied to both the Tooling API and the Metadata API.

Thursday, March 5, 2015

.NET Integration with Salesforce Tooling API Spring 15 (v33.0)

The Spring 15 Salesforce release brings some changes to the Tooling API. From the release notes:

Feature: Metadata namespace
Description: In previous versions of the SOAP Tooling API, metadata type elements were defined in the same namespace as the sObjects of the API, tooling.soap.sforce.com. In order to uniquely name these types, the suffix “Metadata” was appended to the metadata types. In this version of the API, a new namespace is introduced, metadata.tooling.soap.sforce.com, and the suffix is no longer used. The old names will continue to work with clients against older API endpoints.

You can see the change diffing the v32.0 WSDL with the newer v33.0 WSDL. The metadata ApexClass element from ApexClass is now also called ApexClass and resides in the namespace urn:metadata.tooling.soap.sforce.com (with the alias mns.

Having two complexTypes with the same name but different namespaces in the same WSDL creates a bit of a mess with Web References (WSDL.exe) and Service References (SvcUtil.exe).

With a web reference to the WSDL the ApexClass from the Metadata namespace gets the matching name and the ApexClass from the urn:tooling.soap.sforce.com namespace becomes ApexClass1. Not only does this break all the previous code that referenced ApexClass within the Tooling API but it is damn ugly.

    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.34234")]
    [System.SerializableAttribute()]
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.ComponentModel.DesignerCategoryAttribute("code")]
    [System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:metadata.tooling.soap.sforce.com")]
    public partial class ApexClass : Metadata {
    // ...
    }

    // ...
    /// 
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.34234")]
    [System.SerializableAttribute()]
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.ComponentModel.DesignerCategoryAttribute("code")]
    [System.Xml.Serialization.XmlTypeAttribute(TypeName="ApexClass", Namespace="urn:tooling.soap.sforce.com")]
    public partial class ApexClass1 : sObject {
        // ...
        private ApexClass metadataField; 
        // ...
        /// 
        [System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
        public ApexClass Metadata {
            get {
                return this.metadataField;
            }
            set {
                this.metadataField = value;
            }
        }
        // ...
    }

To be fair, this is an issue with the .NET generation of the classes in different namespaces as much as anything. The same WSDL in the FuseIT version of Wsdl2Apex creates separate Apex classes for each namespace to keep the class definitions distinct.

One brute force way to fix this is to rename the classes after the codegen is complete. Find the ApexClass that inherits from Metadata and rename it to ApexClassMetadata. Then rename ApexClass1 to ApexClass. Rinse and repeat for any other types that derive from Metadata (WorkflowRule1, WorkflowFieldUpdate1, ValidationRule1, Profile1, ...).

It might be possible to use the /namespace: option on SvcUtil.exe to move each WSDL namespace into a separate C# namespace. It doesn't appear that Wsdl.exe has the same option.

Monday, November 3, 2014

Dreamforce 2014 Presentation - Improved Apex support for SOAP based web services

At Dreamforce this year I gave a breakout presentation on the WSDL2Apex component of the FuseIT SFDC Explorer. The core idea is to increase support for calling SOAP based web services by generating the required Apex classes from the WSDL.

Breakout Session Blurb

Join us as we review the capabilities of the existing WSDL-to-Apex code generation feature, and explain how we built a tool to provide expanded features using the Tooling API. The resulting tool has greater support for more WSDL features, generates test cases and the associated mocks to maximize code coverage, and optionally includes HttpRequest versions of the callouts.


Using the Tooling API to Generate Apex SOAP Web Service Clients

Offsite Session Video on Vidyard.



The demo's went well. I was caught out a bit with the resolution. I went in expecting 1280x720 and it got bumped up to 1920x1080. Hopefully the core parts scaled well enough to be seen. Note to self, have a magnifier tool handy for future demo's.

It was difficult to see the audience in the breakout room with 3 spot lights pointed at the stage - the cellphone camera isn't really showing how blinding it was. I could hear people were out there, but couldn't really see them. Some of the breakout rooms had this set up and some didn't, I'm not sure why.

These were minor things really, and I'm glad I was given the opportunity to present at Dreamforce. I got some great questions and feedback from the session. We are getting more followup queries at work now a couple of weeks after Dreamforce. With so much going on during the conference it seems to take awhile for people to filter back to work and follow up on sessions. Also, with so much going on, it was tempting to skip the sessions that were being recorded and attend other activities, such as labs and meeting with other attendees.

Thursday, October 23, 2014

Importing the Salesforce Winter 15 Partner API to .NET

After updating the Partner API to v32.0 from v31.0 I started getting the following SGEN compilation errors:

  1. System.InvalidOperationException: Unable to generate a temporary class (result=1).
  2. error CS0029: Cannot implicitly convert type 'XYZ.SalesforcePartner.ListViewRecordColumn' to 'XYZ.SalesforcePartner.ListViewRecordColumn[]'

The new ListViewRecord and ListViewRecordColumn complexTypes from the v32.0 wsdl:

            <complexType name="ListViewRecord">
                <sequence>
                    <element name="columns"                  type="tns:ListViewRecordColumn" maxOccurs="unbounded"/>
                </sequence>
            </complexType>

            <complexType name="ListViewRecordColumn">
                <sequence>
                    <element name="fieldNameOrPath"          type="xsd:string"/>
                    <element name="value"                    type="xsd:string" nillable="true"/>
                </sequence>
            </complexType>

The problem appears in the generated Reference.cs with the ExecuteListViewResult.records multidimensional array return type.

        /// 
        [System.Xml.Serialization.XmlArrayItemAttribute("columns", typeof(ListViewRecordColumn), IsNullable=false)]
        public ListViewRecordColumn[][] records {
            get {
                return this.recordsField;
            }
            set {
                this.recordsField = value;
            }
        }

The XmlArrayItemAttribute typeof(ListViewRecordColumn) should be typeof(ListViewRecordColumn[]). After changing this manually the web reference compiled again.

        /// 
        [System.Xml.Serialization.XmlArrayItemAttribute("columns", typeof(ListViewRecordColumn[]), IsNullable=false)]
        public ListViewRecordColumn[][] records {
            get {
                return this.recordsField;
            }
            set {
                this.recordsField = value;
            }
        }

See also:

Wednesday, October 8, 2014

How to contribute to the Salesforce WSDL2Apex project

WSDL2Apex is a Salesforce tool for converting a web services WSDL file into Apex classes that can make the required SOAP callouts and then process the responses. Internally the callouts are made using the Apex class WebServiceInvoke. In September 2014 it was released as an open source project.

I'll cover setting up a development environment to build it and then contribute those changes back in this post. It is loosely based on Peter Knolle's Extending the Force.com IDE article.

  • Clone the project repository from GitHub. The Fork button on the web page made me a clone easily enough at https://github.com/FishOfPrey/WSDL2Apex
  • If you don't have it already, get the Java Development Kit 7. For me this installed as %Program Files%\Java\jdk1.7.0_67.
  • Add the Java 7 JDK bin to the PATH.
    ...\WSDL2Apex\WSDL2Apex-master\target>SET PATH=%PATH%;C:\Program Files (x86)\Java\jdk1.7.0_67\bin
  • Get Apache Maven 3.2.2 to manage the builds and test runs.
  • Setup the Maven PATH requirements as per the Installation instructions.
    > SET JAVA_HOME=C:\Program Files (x86)\Java\jdk1.7.0_67

    Note the absence of surrounding double quotes. With quotes I was getting the message "Files was unexpected at this time."
    > SET PATH=%PATH%;D:\Development\Maven\apache-maven-3.2.3\bin
  • Package it up, including running the test cases:
    >mvn install package
  • Run WSDL2Apex from the command line:
    ...\WSDL2Apex\WSDL2Apex-master\target>java -jar WSDL2Apex-1.0.jar "C:\PathTo\WSDLs\WithLimitElement.xml" outputDir yes

You will need to submit a Contributor's License Agreement before Salesforce can accept your pull requests. The form is available on the Contributing Code page.

It is licensed under the Eclipse Public License (EPL) v1.0. Eclipse Public License (EPL) Frequently Asked Questions is a useful resource.

See also:

Monday, March 24, 2014

An alternative to Salesforces Wsdl2Apex for calling SOAP web services in Apex

Salesforce provides a tool called Wsdl2Apex that allows you to generate Apex classes from a WSDL. These Apex classes act as a proxy for invoking the web service methods.

While functional, Wsdl2Apex has a number of limitations. Including:

  1. The generated Apex classes require code coverage, which needs to be created manually.
  2. You need to import the entire WSDL. In many cases you may only require a subset of the web methods. Reducing the number of methods cuts down the lines of Apex (a limited resource) that are generated and subsequently the number of lines requiring code coverage.
  3. Support for complex types that extend a base type. <xsd:extension base="foo:Bar">.
  4. Support for importing another WSDL. <xsd:import>
  5. Support for attributes. <xsd:attribute>
  6. The ordering of the generated methods appears to be arbitrary (maybe hash based ordering internally?). At the very least, a small change in the input WSDL can produce Apex that doesn't diff very well. This can be a pain with source control and tracking the history of changes.
  7. Conflicts between named WSDL elements and reserved keywords in Apex. E.g. long
  8. WSDLs that contain multiple wsdl:binding elements

I've been working with an intern student here at FuseIT to create a replacement tool as part of the FuseIT SFDC Explorer under the new WSDL tab in the 1.0.0.47 release. The current release is aimed at having reasonable feature parity with the existing Salesforce Wsdl2Apex implmentation.

Current functionality:

  • Import a WSDL from URL or local file.
  • User definable class names for each WSDL namespace
  • Detection of existing Apex Classes that match those being generated
  • The ability to select just the Apex methods that should be generated (including a description of the input and output parameters)
  • Publish the Apex classes directly into a Salesforce Org via the Tooling API.

Work in progress and possible future extensions:

  • Generate test Apex methods and mocks that give 100% code coverage for the generated callout code.
  • Generate HttpRequest web service calls as an alternative/backup to the WebServiceCallout.invoke calls.
  • Generate a WebServiceMock class with expanded request/response objects and doInvoke implementation.
  • Generate a wrapper Apex class that will revert to the mock implementation with running in a test case context. This could also expose the end point and timeout settings as properties.

See also:

Thursday, August 30, 2012

Sorting WSDL type elements using XSLT before using Salesforce wsdl2apex

After using the Salesforce wsdl2apex to generate apex I'll often need to make some tweaks by hand to allow for automated testing.

When regenerating for WSDL updates I've found the order of the elements can change dramatically. With large WSDLs this can make migrating the changes by hand a time consuming exercise.

For example, below is the SourceGear DiffMerge overview from two versions of a 5000 line WSDL. (Turned on it's side)

WSDL diff

It isn't clear to me why the order of the XML elements is changing so much between versions, but it does make tracking the actual differences more difficult.

One possible solution I've found is to run the WSDL through an XSLT to sort the various elements by name.

I've appropriated the following from Glen Mazza - Using XSLT to organize SOAP contracts (WSDLs and XSDs)

One easy way to apply it is to open the WSDL in Visual Studio as XML and then use XML > Start XSLT Debugging to apply it.

After processing the line counts went from 3475 and 5005 to 3472 and 5002 respectively. Now I can clearly see where new elements have been added in the diff:

WSDL diff after sorting
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE stylesheet [
<!ENTITY space "<xsl:text> </xsl:text>">
<!ENTITY ivSp "<xsl:text>    </xsl:text>">
<!ENTITY tab "<xsl:text>	</xsl:text>">
<!ENTITY cr "<xsl:text>
</xsl:text>">
]>

<xsl:stylesheet version="1.0"
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   xmlns:xs="http://www.w3.org/2001/XMLSchema"
   xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
   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">

   <xsl:template match="wsdl:definitions">
       &cr;
       <!-- By itself, xsl:copy outputs only the element referred to.
            It needs additional coding as below to copy the
            element's attributes and descendant elements.
       -->
       <xsl:copy>
          <!-- output all attributes of the top-level element -->
          <xsl:for-each select="@*">
              <xsl:copy/>
          </xsl:for-each>&cr;

          <xsl:for-each select="./wsdl:types">
              &ivSp;<wsdl:types>&cr;
                  <xsl:apply-templates select="./xs:schema"/>
              &ivSp;</wsdl:types>&cr;
          </xsl:for-each>

          <xsl:for-each select="./wsdl:message">
              <xsl:sort select="@name"/>
              <xsl:apply-templates select="."/>
          </xsl:for-each>

          <xsl:for-each select="./wsdl:portType">
              <xsl:apply-templates select="."/>
          </xsl:for-each>

          <xsl:for-each select="./wsdl:binding">
              <xsl:sort select="@name"/>
              <xsl:apply-templates select="."/>
          </xsl:for-each>

          <xsl:for-each select="./wsp:Policy">
              <!--if wish to sort: xsl:sort select="@wsu:Id"/-->
              &ivSp;<xsl:copy-of select="."/>&cr;
          </xsl:for-each>

          <xsl:for-each select="./wsdl:service">
              &ivSp;<xsl:copy-of select="."/>&cr;
          </xsl:for-each>

       </xsl:copy>

   </xsl:template>

   <!--
      Each immediate child of the top-level element is indented four spaces
      from the top-level item.  May need to convert original WSDL from tabs
      to spaces first.
   -->
   <xsl:template match="wsdl:message">
       &ivSp;<xsl:copy-of select="."/>&cr;
   </xsl:template>

   <xsl:template match="wsdl:portType | wsdl:binding">
    &ivSp;<xsl:copy>
          <!-- output all (@*) attributes of the top-level element -->
          <xsl:for-each select="@*">
              <xsl:copy/>
          </xsl:for-each>&cr;
     
          <xsl:for-each select="./*[not(self::wsdl:operation)]">
              &ivSp;&ivSp;<xsl:copy-of select="."/>&cr;
          </xsl:for-each>

          <xsl:for-each select="./wsdl:operation">
              <xsl:sort select="@name"/>
              <xsl:apply-templates select="."/>
          </xsl:for-each>&ivSp;

        </xsl:copy>&cr;
   </xsl:template>

   <xsl:template match="wsdl:operation">
       &ivSp;&ivSp;<xsl:copy-of select="."/>&cr;
   </xsl:template>

   <xsl:template match="xs:schema">

       &ivSp;&ivSp;<xsl:copy> 
          <!-- output all (@*) attributes of the top-level xs:schema element -->
          <xsl:for-each select="@*"> 
              <xsl:copy/>
          </xsl:for-each>&cr;
          
          <xsl:for-each select="./xs:import">
              <xsl:apply-templates select="."/>
          </xsl:for-each>
      
          <xsl:for-each select="./xs:simpleType">
              <!-- sort the simpleTypes by their name attribute first -->
              <xsl:sort select="@name"/> 
              <xsl:apply-templates select="."/>
          </xsl:for-each>
      
          <xsl:for-each select="./xs:complexType">
              <xsl:sort select="@name"/>
              <xsl:apply-templates select="."/>
          </xsl:for-each>

          <xsl:for-each select="./xs:element">
              <xsl:sort select="@name"/>
              <xsl:apply-templates select="."/>
          </xsl:for-each>
       &ivSp;&ivSp;</xsl:copy>&cr;

   </xsl:template>

   <xsl:template match="xs:import | xs:simpleType | xs:complexType | xs:element">
       <!-- Unlike xsl:copy above, xsl:copy-of automatically copies the 
            element's attributes and its descendant elements. -->
       &ivSp;&ivSp;&ivSp;<xsl:copy-of select="."/>&cr;
   </xsl:template>

</xsl:stylesheet>

Friday, March 4, 2011

Consuming an ASP.NET Web Service from Salesforce Apex Callouts

Salesforces wsdl2apex can be a bit basic in what is supports (see Supported WSDL Features).

Steps for consuming an ASP.NET web service from Apex.

  1. Add the Web service host to the Remote Sites.
    Adminitraction Setup > Security Controls > Remote Site Settings
    Skipping this step will result in a "System.CalloutException: IO Exception: Unauthorized endpoint, please check Setup->Security->Remote site settings. endpoint ="...
  2. Export the WSDL from the ASP.NET web service
  3. Optionally, you might like to sort the WSDL. The idea is to ensure wsdl2apex produces the same elements in the same order for future code comparisons.
  4. Import the WSDL into Salesforce
    Develop > Apex Classes > Generate from WSDL*
  5. Define a Apex class name
  6. Test the new Apex class using Execute Anonymous in Eclipse. With a ASP.NET generated web service I found the class I defined in the previous step had a nested class corresponding to the binding name from the uploaded WSDL. The nested class had a method that corresponded to the web method (the operation in the WSDL).
  7. Optionally, search the generated class for calls to WebServiceCallout.invoke(...). This will currently cause test cases to abort silently (except for a log message) and you may not notice. Sprinkle the code with a liberal amount of Test.isRunningTest().

*Common WSDL parsing errors:

  1. If the Parse WSDL button produces Error: Failed to parse wsdl: Found more than one wsdl:binding. WSDL with multiple binding not supported Deleting the second <wsdl:binding> element what contains soap12 elements and the corresponding port definition under <wsdl:wervice>.
  2. wsdl:import
    Error: Failed to parse wsdl: Unknown element: import
    Locate the import elements in the current WSDL. E.g. . Pull that WSDL down and merge its contents into the parent WSDL.
  3. xsd:import
    Failed to parse wsdl: Found schema import from location http://example.com/WebService.svc?xsd=xsd0. External schema import not supported
    These will typically appear under <wsdl:types>. Copy the contents of the import under <wsdl:types> and remove the <xsd:schema>. See also: Force.com Discussion Forum: Referencing external schemas in a WSDL
  4. Error: Failed to parse wsdl: Failed to parse WSDL: Unable to find binding {http://tempuri.org/}BasicHttpBinding_IWebService. Found BasicHttpBinding_IWebService instead.
    The <wsdl:definitions> had the attribute xmlns:i0="http://tempuri.org/". i0: was being used as the namespace for the binding. Switching it to tns: resolved the issue.
  5. Error: Failed to parse wsdl: schema:targetNamespace can not be null
    In this case I needed to add a targetNamespace attribute and value to the element. E.g. <xsd:schema> becomes <xsd:schema targetNamespace="http://www.example.com/api/Imports">
  6. Apex Generation Failed:
    Unable to find schema for element; {http://schemas.microsoft.com/2003/10/Serialization/Arrays}ArrayOfstring
    and
    Unable to find schema for element; {http://schemas.microsoft.com/2003/10/Serialization/Arrays}ArrayOflong
    In this case I'd initially made a mistake when pulling in the xsd for these complex types. I corrected it by putting the complex type definitions within a <xsd:schema targetNamespace="http://schemas.microsoft.com/2003/10/Serialization/Arrays">element in wsdl:type section.
  7. Error: wwwExampleComApi
    Error: Identifier name is reserved: long at 191:23

    This error was caused by the ArrayOflong type generating the following Apex:
    public class ArrayOflong {
        public Long[] long;
        private String[] long_type_info = new String[]{'long','http://www.w3.org/2001/XMLSchema','long','0','-1','false'};
        private String[] apex_schema_type_info = new String[]{'http://www.Example.com/api','false','false'};
        private String[] field_order_type_info = new String[]{'long'};
    }
    
    Possible fix:
    public class ArrayOflong {
        public Long[] long_x; // Note the _x suffix to avoid the conflict
        // The first parameter here is what will appear in the SOAP message as the element name
        private String[] long_type_info = new String[]{'long','http://www.w3.org/2001/XMLSchema','long','0','-1','false'};
        private String[] apex_schema_type_info = new String[]{'http://www.Example.com/api','false','false'};
        private String[] field_order_type_info = new String[]{'long_x'};
    }
    
  8. Error: Failed to parse wsdl: Unsupported Schema element found http://www.w3.org/2001/XMLSchema:attribute. At: 1866:57
    The WSDL had three "attribute" elements:
    • <xs:attribute name="FactoryType" type="xs:QName"/>
    • <xs:attribute name="Id" type="xs:ID"/>
    • <xs:attribute name="Ref" type="xs:IDREF"/>
    Commenting these out of the WSDL allowed into to be used with wsdl2apex. I'm not sure what adverse effects resulted from removing them.
  9. The use of <xsd:extension base="foo"> in a complex type won't result in the base class fields appearing in generated Apex class.
    Try copying the fields from Apex class that was being extended into the sub class.

See also: