Apex Metadata API Introduction
The Salesforce Metadata API allows you to perform many org configuration and setup features programatically. Code examples given by Salesforce are in Java as the API is a SOAP API. In respect to the Apex library provided here, it wraps via the providedMetadataService.cls class the Salesforce SOAP version of the API to make it easier to access for Apex developers. Key terms, structures and operations exposed are still the same however, so reading the Salesforce documentation is still important. You should also review the readme and example class
Handling Asynchronous Aspects
The first challenging aspect is the fact that Salesforce provides this API as an asynchronous API. Which requires some additional coding approaches to handle its responses. Effectively polling for the result of the API calls. To make it easier the Apex library includes some examples for doing this in Batch Apex and using Visualforce’s apex:actionPoller component.
Create Custom Field Example
Here is an example of creating a CustomField using the create operation. First creating an instance of the service which exposes all the operations and configures the authentication, by passing on the users current Session ID. For more information on calling SOAP from Apex, see here.
MetadataService.MetadataPort service = new MetadataService.MetadataPort(); service.SessionHeader = new MetadataService.SessionHeader_element(); service.SessionHeader.sessionId = UserInfo.getSessionId(); MetadataService.CustomField customField = new MetadataService.CustomField(); customField.fullName = 'Test__c.TestField__c'; customField.label = 'Test Field'; customField.type_x = 'Text'; customField.length = 42; MetadataService.AsyncResult[] results = service.create(new List<MetadataService.Metadata> { customField });
The code then needs to inspect the contents of AsyncResult and be prepared to pass it back to the API to poll for the results periodically. If you study the create documentation you will see a good summary of the steps. Basically calling one of the Metadata API operations, receiving the result and if needed repeatedly calling checkStatus.
You can call the checkStatus method from Apex, though you must have your code wait for Salesforce to process the request, either via Batch Apex context or via Visualforce and its AJAX support.
MetadataService.AsyncResult[] results = service.checkStatus(new List<String> { results[0].Id });
Calling checkStatus from Visualforce
If you have an interactive tool your building, you can use Visualforce and use the apex:actionPoller to store the AsyncResult in your controller and write a controller method to call the checkStatus, which the action poller repeatedly calls until the AsyncResult indicates the request is completed by Salesforce.
public with sharing class MetadataController { public MetadataService.AsyncResult result {get;set;} public PageReference createField() { // .. as per above ... result = createService().create(new List<MetadataService.Metadata> { customField })[0]; displayStatus(); return null; } public PageReference checkStatus() { // Check status of the request result = createService().checkStatus(new List<String> { result.Id })[0]; displayStatus(); return null; } private void displayStatus() { // Inspect the AsyncResult and display the result ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.Info, result.done ? 'Request completed' : 'Request in progress...')); if(result.state == 'Error') ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.Error, result.message)); if(result.done) result = null; } }
This controllers page then looks like this…
<apex:page controller="MetadataController"> <apex:form id="form"> <apex:sectionHeader title="Metadata Demo"> <apex:pageMessages> <apex:actionPoller action="{!checkStatus}" interval="5" rerender="form" rendered="{!NOT(ISNULL(Result))}"> <apex:outputPanel rendered="{!ISNULL(Result)}"> <apex:commandButton value="Create Field" action="{!createField}"> <apex:commandButton value="Delete Field" action="{!deleteField}"> </apex:outputPanel> </apex:form> </apex:page>
Image may be NSFW.
Clik here to view.Image may be NSFW.
Clik here to view.Image may be NSFW.
Clik here to view.
The demos for the retrieve and deploy operations also provide good examples of this here and here. Even though they don’t actually use the create operation of the Metadata API, polling for the AsyncResult is the same. Note that the two operations they use, retrieve and deploy are still useful for deploying code or retrieving configuration. But do have the added complexity of handling zip file format, something the library also provides some components for.
Calling checkStatus from Batch Apex
This part of the Readme describes a helper class MetadataCreateJob.cls to do this in Batch Apex which actually handles both calling the create and checkStatus methods for you. Sending the final results to an Apex handler class, in this case a default email handler is provided, but you write your own.
// Pass the Metadata items to the job for processing, indicating any dependencies MetadataCreateJob.run( new List<MetadataCreateJob.Item> { new MetadataCreateJob.Item(customField) }, new MetadataCreateJob.EmailNotificationMetadataAsyncCallback());
What org configuration can be access via the Salesforce Metadata API?
First review this topic from the Salesforce documentation. In respect to the CRUD (Create, Update and Delete) operations of the API you can only pass metadata / component types that extend the class Metadata (or MetadataService.Metadata in the Apex API). If you review the MetadataService.CustomField class you can see it does just this…
public class CustomField extends Metadata {
When you review the individual topics for the metadata component types in the Salesforce documentation, pay attention to those that state that they extend the Metadata type.
NOTE: As per the last section of the Readme for the library, it has manually made this modification as the Salesforce WSDL to Apex code generator that initially generated the wrap did not do this. Most of the popular Metadata types have already had this treatment in the Apex API.
Image may be NSFW.
Clik here to view.
Clik here to view.
