using the tooling api to generate apex soap web service clients

29
Using the Tooling API to Generate Apex SOAP Web Service Clients Daniel Ballinger Senior Developer @FishOfPrey

Upload: daniel-ballinger

Post on 13-Jul-2015

1.833 views

Category:

Technology


4 download

TRANSCRIPT

Using the Tooling API to Generate Apex SOAP Web Service ClientsDaniel Ballinger

Senior Developer

@FishOfPrey

Place

Customer or

Partner logo in

white area of

slide, centered

Daniel BallingerSenior Developer

@FishOfPrey

Have you ever seen…

Supported WSDL Features

http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_callouts_wsdl2apex.htm#supported_wsdls_topic-title

Feature Limits

Apex and

Deployment

Apex Generation Failed

The total size of apex code in this application after removing comments exceeds the maximum character size of

3000000

Apex Generation Failed

Class name 'toolingSoapSforceCom' already in use. Please delete this class or specify a new class name

Apex Generation Failed

Unsupported schema type: {http://www.w3.org/2001/XMLSchema}anyType

Apex Generation Failed

Unsupported WSDL. Found more than one part for message XAVRequestMessage

No file chosen

Error: Failed to parse wsdl: Found more than one wsdl:binding. WSDL with multiple binding not supported

Choose File

No file chosen

Error: Failed to parse wsdl: Unknown element: import

Choose File

In This Session

•The FuseIT SFDC Explorer tool

•Existing Salesforce WSDL functionality

•Demo custom WSDL to Apex tooling

•How it was made

• Windows only for the full tool

• Basic web version of WSDL tooling

The FuseIT SFDC Explorer Tool – FusesFriend

• Features

– Session management

– Schema Explorer + Tooling API

– SOQL queries

– Running Test Cases

– Debug Log Monitoring

– Data Export Retrieval

– Anonymous Apex

– Code Coverage

– WSDL import to Apex

http://www.fuseit.com/explorer

Unsupported

feature

WSDL

> 1 MB

<xsd:import>

<xsd:include>

Existing Class

Name

Manually

Edit WSDL

Superfluous

bindings

Existing WSDL2Apex Experience

Download single WSDL

Generate from WSDL

Upload single WSDL

Namespaces to Classes

Generate Apex Code

Call Generated

Code

Shaving the [WSDL] Yak

“Yak shaving is what you are doing when you're doing

some stupid, fiddly little task that bears no obvious

relationship to what you're supposed to be working

on, but yet a chain of twelve causal relations links

what you're doing to the original meta-task.”

Scott Hanselman

Photo by Dennis Jarvis CC BY-SA 3.0

Use to

introduce a

demo, video,

Q&A, etc. Demo SFDC Explorer Basic

The IT Crowd – Series 1 Episode 2: Fire!

Code Coverage Testing for Generated Code

•Need to implement WebServiceMock Interface

• Invoke each web service method

•Handle Request and Response types

•Supporting Types (Headers)

Use to

introduce a

demo, video,

Q&A, etc. Demo SFDC Explorer Web Service Mock

Class name 'Metadata' already in use.

Please edit WSDL to remove repeated

names

The Metadata API – Handling larger WSDLs

• CustomObject et al. extend Metadata and

need elements from it to work correctly.<xs:extension base="Metadata">

• 330 KB WSDL

Metadata 590,915

Remaining

Apex Character Limit

• 590,915 characters / 6740 lineswithout comments

• Approximately 20% of 3,000,000 character Apex limit

Method filtering functionality

•Reduces amount of generated code

•Reduces testing requirements for dead code

•Potentially skips unsupported features

•Optionally remove supporting Apex classes

Use to

introduce a

demo, video,

Q&A, etc. DemoSFDC Explorer Method filtering

How it works

Deploy to Salesforce

• Tooling API to deploy all associated Apex classes at once

Transform to Apex

• T4 (Text Template Transformation Toolkit)Templates to convert the object model into Apex

Build Apex Model

• C# Object Model of Apex Classes, Members and Methods

Parse WSDL

• Import all WSDL data and extract required elements

.NET Representation of Apex Class model

ApexClass

ApexMemberApexClassCollection

StringArrayApexMember

ApexMethod

ApexMethodParameter

ApexMethodHttp

ReturnType

ReturnPrimitive ReturnClassTypeReturnVoid

Type Infos

Inner Classes

Namespace Collection

* Members1 InnerApexClasses * Methods

Parameters

*

ReturnType

1

ApexClasses

0..*

Outer class for each Namespace

Created for the Port/Service

Each Request/Response

Tooling API deployment

Existing ID

• Create a MetadataContainer with a

unique name

• Create a collection of

ApexClassMember referencing the

MetadataContainerId

• Create a ContainerAsyncRequest

– Option to Validate Only (IsCheckOnly)

• Keep retrieving until the State is no

longer Queued.

• Delete MetadataContainer

New – no existing ID

• Call create and collect the SaveResults

• Or create an empty stub class to get an

ID and use the MetadataContainer

Tooling API deployment

Existing ID

• Create a MetadataContainer with a

unique name

• Create a collection of

ApexClassMember referencing the

MetadataContainerId

• Create a ContainerAsyncRequest

– Option to Validate Only (IsCheckOnly)

• Keep retrieving until the State is no

longer Queued.

• Delete MetadataContainer

New – no existing ID

• Call create and collect the SaveResults

• Or create an empty stub class to get an

ID and use the MetadataContainerList<ApexClass> apexClassesToInsert = new List<ApexClass>();

// Add ApexClass records with T4 generated BodyApexClass classToInsert = new ApexClass() { Name = "HelloDF" }; classToInsert.Body = "public class " + classToInsert.Name + " { public class Add{ }}"; apexClasses.Add(classToInsert);

SaveResult[] saveResults = toolingService.create(apexClassesToInsert.ToArray());if (saveResults != null) {

foreach (SaveResult sr in saveResults) {if (!sr.success) {

throw new ToolingCreateException(apexClassesToInsert.ToArray(), saveResults);}

}}

Tooling API deployment

Existing ID

• Create a MetadataContainer with a

unique name

• Create a collection of

ApexClassMember referencing the

MetadataContainerId

• Create a ContainerAsyncRequest

– Option to Validate Only (IsCheckOnly)

• Keep retrieving until the State is no

longer Queued.

• Delete MetadataContainer

New – no existing ID

• Call create and collect the SaveResults

• Or create an empty stub class to get an

ID and use the MetadataContainerMetadataContainer container = new MetadataContainer();

// max length 32 characters!container.Name = "UAC " + DateTime.Now.Ticks;

SaveResult[] containerResults = toolingService.create(new sObject[] { container });

if (!containerResults[0].success) { throw new ToolingCreateException(container, containerResults[0]);

}

Id metadataContainerId = new Id(containerResults[0].id);

Tooling API deployment

Existing ID

• Create a MetadataContainer with a

unique name

• Create a collection of

ApexClassMember referencing the

MetadataContainerId

• Create a ContainerAsyncRequest

– Option to Validate Only (IsCheckOnly)

• Keep retrieving until the State is no

longer Queued.

• Delete MetadataContainer

New – no existing ID

• Call create and collect the SaveResults

• Or create an empty stub class to get an

ID and use the MetadataContainer

var toUpdate = new List<ApexClassMember>(); foreach(ApexClass ac in classesToUpsert) {

var acm = new ApexClassMember();acm.ContentEntityId = ac.Id; // 01p…acm.Body = ac.Body; // T4 Template outputacm.FullName = ac.Name; // class nameacm.MetadataContainerId =

metadataContainerId.CaseSafeID;toUpdate.Add(acm);

}

sObject[] toCreate = toUpdate.ToArray(); SaveResult[] cResult =

toolingService.create(apexClassMembersToCreate);foreach (SaveResult sr in cResult) {

if (!sr.success) { throw new ToolingCreateException(toCreate,

cResult);}

}

Tooling API deployment

Existing ID

• Create a MetadataContainer with a

unique name

• Create a collection of

ApexClassMember referencing the

MetadataContainerId

• Create a ContainerAsyncRequest

– Option to Validate Only (IsCheckOnly)

• Keep retrieving until the State is no

longer Queued.

• Delete MetadataContainer

var cr = new ContainerAsyncRequest(); cr.MetadataContainerId =

metadataContainerId.CaseSafeID;cr.IsCheckOnly = false;

SaveResult[] cars = this.create(new sObject[]{cr});if (! cars[0].success) {

throw new ToolingCreateException(cr,cars[0]); }

ContainerAsyncRequest retrieve =WaitForClassToUpdate(containerAsyncResults);

if (retrieve.State == "Failed") {throw ApexUpdateException.FromDeployDetails

(retrieve.DeployDetails);}

DeleteResult[] dr = this.delete(new string[]{ metadataContainerId.CaseSafeID });

WSDL Variation

http://www.superbwallpapers.com/photography/snowflakes-10358/

If you have a unique WSDL:

• http://www.fuseit.com/wsdlhelp

• Please provide the WSDL details

Use HTTP Requests for

WebServiceCallout.invokelimits

Summary

•Manually dealing with WSDL import errors is time consuming

and error prone

•Extended SOAP support for several common WSDL issues:

– Increased support – <xsd:import> <xsd:include> <xsd:extension>

– Generate basic code coverage

– Method filtering to reduce Apex

Q&A+ What Next

• Download: the free FuseIT SFDC Explorer Tool (or get the web version)

http://www.fuseit.com/explorer

• Idea: Run anonymous apex as if it were a test case

http://goo.gl/Aci1ys

• Salesforce: The now open source WSDL2Apex generator

http://goo.gl/tUcnVj

• Session: Building Callouts Without WSDL2Apex and Apex Parsers

Tuesday, 4:00 PM - 4:30 PM | Moscone Center West | Mobile Theater

www.fuseit.com

@FishOfPrey

www.fishofprey.com

@GirishaArora

Girisha Arora

Bonus Slides

Handling Extensions

• <xsd:extension base=“”/>

• Copy fields from base type to the

sub class.

• Use extends in the future

• Metadata API

• AccountOwnerSharingRule extends

• OwnerSharingRule extends

• BaseSharingRule extends

• Metadata

HTTP Requests

• When WebServiceCallout.invoke() fails

• A template for supporting more complex callouts

• Access full fault messages

SOAPFault Information for Apex

https://success.salesforce.com/ideaView?id=08730000000BqG9AAK

Apex Class Drifting with WSDL changes