Getting Started with Force.com Tooling API

The power of the Tooling API can be demonstrated using a basic Visualforce page that calls to the Tooling API’s REST endpoint from the Apex controller. Figure 11.4 shows the sample user interface. On the left side are the Apex classes available in the organization, accessible with an ordinary SOQL query on ApexClass. On the upper-right side is the body of the selected Apex class. Below it is a Save button, which deploys changes to the class body.

Image

Figure 11.4 Result of Save button click

The process for deploying Apex code or other types of Force.com logic is to create a MetadataContainer, add to it the wrapper object corresponding to the type of artifact to be deployed (in this case, ApexClassMember), create a ContainerAsyncRequest, and track the progress of the request using a specialized Tooling API query service.

Below the Save button are two fields that illustrate the internal state of the deployment: the ContainerId and RequestId. These are maintained both to check the status of the deployment (via the Refresh Status button), and to properly clean up (by deleting the MetadataContainer) when the user clicks the Start Over button.

To use the example, click Edit beside the class you’d like to edit. Make a change to the class body and click Save. You should see two successful JSON responses concatenated in the log output box, and the other buttons in the user interface should become enabled.

Figure 11.5 shows the results of clicking the Refresh Status button. According to the JSON response, the deployment is complete and without compiler errors. Click the Start Over button. You should see your changes to the selected Apex class reflected in the user interface and anywhere that Apex code is visible.

Image

Figure 11.5 Result of Refresh Status button click

The code in Listing 11.16 and Listing 11.17 provides an implementation of the controller and page for the Tooling API example. The controller makes extensive use of HTTP callouts and the built-in JSON parsing support.


Note

For the sample code to work, you must add a Remote Site setting to allow requests to the Tooling API endpoint. The endpoint is the root of your instance URL, for example, https://na15.salesforce.com.


Listing 11.16 Visualforce Controller for Tooling API Example


public class MyPageController11_16 {
  public String editBody { get; set; }
  public String editClassId { get; set; }
  public String containerId { get; set; }
  public String requestId { get; set; }
  public String log { get; set; }
  public List<ApexClass> getClasses() {
    return [ SELECT Id, Name, IsValid FROM ApexClass
      ORDER BY Name ];
  }
  public PageReference edit() {
    editBody = [ SELECT Body FROM ApexClass
      WHERE Id = :editClassId LIMIT 1 ][0].Body;
    return null;
  }
  public PageReference save() {
    log = '';
    // Create MetadataContainer
    HttpRequest req = newRequest('/sobjects/MetadataContainer',
      'POST'),
    Map<String, Object> args = new Map<String, Object>();
    args.put('Name', 'ClassContainer'),
    String result = sendRequest(req, args);
    containerId = null;
    try {
      containerId = getResultId(result);
    } catch (Exception e) {
      log += result;
      return null;
    }
    // Create ApexClassMember
    req = newRequest('/sobjects/ApexClassMember',
      'POST'),
    args = new Map<String, Object>();
    args.put('ContentEntityId', editClassId);
    args.put('Body', editBody);
    args.put('MetadataContainerId', containerId);
    log += sendRequest(req, args);
    // Create ContainerAsyncRequest
    req = newRequest('/sobjects/ContainerAsyncRequest', 'POST'),
    args = new Map<String, Object>();
    args.put('IsCheckOnly', 'false'),
    args.put('MetadataContainerId', containerId);
    result = sendRequest(req, args);
    log += result;
    requestId = getResultId(result);
    return null;
  }
  public PageReference reset() {
    cleanup(containerId);
    editClassId = '';
    requestId = '';
    containerId = '';
    log = '';
    editBody = '';
    return null;
  }
  public PageReference refresh() {
    String soql = 'SELECT Id, State, CompilerErrors, ErrorMsg FROM ' +
      'ContainerAsyncRequest where id= '' + requestId + ''';
    HttpRequest req = newRequest('/query/?q=' +
      EncodingUtil.urlEncode(soql, 'UTF-8'),
      'GET'),
    log = sendRequest(req, null);
    return null;
  }
  public static void cleanup(String containerId) {
    sendRequest(newRequest('/sobjects/MetadataContainer/' + containerId,
      'DELETE'), null);
  }
  private static HttpRequest newRequest(String toolingPath,
    String method) {
    HttpRequest req = new HttpRequest();
    req.setHeader('Authorization',
      'Bearer ' + UserInfo.getSessionID());
    req.setHeader('Content-Type', 'application/json'),
    req.setHeader('X-PrettyPrint' , '1'),
    req.setEndpoint(getInstanceUrl() +
      '/services/data/v28.0/tooling' + toolingPath);
    req.setMethod(method);
    return req;
  }
  private static String sendRequest(HttpRequest req,
    Map<String, Object> args) {
    Http h = new Http();
    if (args != null) {
      req.setBody(Json.serialize(args));
    }
    HttpResponse res = h.send(req);
    return res.getBody();
  }
  private static String getInstanceUrl() {
    String url = System.URL.getSalesforceBaseUrl()
      .toExternalForm();
    url = url.replace('visual.force', 'salesforce'),
    url = url.replace('c.', ''),
    return url;
  }
  private static Id getResultId(String body) {
    Map<String, Object> result = (Map<String, Object>)
      JSON.deserializeUntyped(body);
    return (Id)result.get('id'),
  }
}


Listing 11.17 Visualforce Page for Tooling API Example


<apex:page controller="MyPageController11_16">
  <apex:form id="form">
  <apex:pageBlock title="Force.com Tooling API Example">
  <apex:pageBlockSection columns="2">
  <apex:pageBlockTable value="{!classes}" var="c">
    <apex:column >
      <apex:commandLink value="Edit" action="{!edit}"
        rerender="editor">
        <apex:param name="editClassId"
          assignTo="{!editClassId}" value="{!c.Id}" />
      </apex:commandLink>
    </apex:column>
    <apex:column value="{!c.Name}" />
    <apex:column value="{!c.IsValid}" />
  </apex:pageBlockTable>
  <apex:outputPanel id="editor">
    <apex:inputTextArea id="editBody" rows="15" cols="90"
      value="{!editBody}" disabled="{!editClassId == NULL}" />
    <p/><apex:commandButton value="Save" action="{!save}"
      disabled="{!editClassId == NULL}" rerender="editor" />
    <p/>
    ContainerId: {!containerId},
    RequestId: {!requestId}<br />
    <apex:commandButton value="Refresh Status" action="{!refresh}"
      disabled="{!requestId == NULL}" rerender="editor" />
    <apex:commandButton value="Start Over" action="{!reset}"
      disabled="{!containerId == NULL}" />
    <p/>
    <textarea disabled="true" rows="10" cols="90">
      {!log}
    </textarea>
  </apex:outputPanel>
  </apex:pageBlockSection>
  </apex:pageBlock>
  </apex:form>
</apex:page>


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.216.143.65