Date post: | 13-Jul-2015 |
Category: |
Technology |
Upload: | daniel-ballinger |
View: | 1,835 times |
Download: | 4 times |
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
Code Coverage Testing for Generated Code
•Need to implement WebServiceMock Interface
• Invoke each web service method
•Handle Request and Response types
•Supporting Types (Headers)
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
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
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