Understanding Salesforce Tooling API

Introduction

The Salesforce Tooling API is a powerful tool that allows developers to retrieve metadata such as Apex classes, Apex triggers, custom objects, custom fields, and more. Instead of manually gathering this information from the entire Salesforce (SF) organization, the Tooling API SOQL can be leveraged to obtain the list of Custom Objects or Custom fields.

Fetching Custom Objects and Custom Fields

To retrieve Custom Objects created in the org, including Managed package objects, the following query can be used:

EX: SELECT Id, DeveloperName FROM CustomObject

In this query, “CustomObject” is referred to as a “Tooling API Object”. The ID represents the custom object’s ID, and DeveloperName represents the custom object’s name.

Similarly, to retrieve all custom fields in the organization, the following Tooling API query can be used:

EX: SELECT Id, DeveloperName FROM CustomField

Detailed Usage Example

Let’s illustrate the detailed usage of Tooling API objects. We have created a Visualforce page (code snippet shown later in this article) to display custom fields and their IDs from an object.

Step 1: Execute the Custom Visualforce Page

Step 1

Step 2: Select the Object Types as “Standard Objects”

Step 2

Then, all the Standard Objects in the organization will appear in the List of Objects picklist.

See also  Simplifying Snowflake API Integration

Objects List

Step 3: Select a Standard Object like Account

Selecting a Standard Object will display the custom fields in the Account object within a Page block table, showing the ID and API name of each custom field.

Account Object Fields

The process involves using the Controller and Schema Object to retrieve all Standard objects, Custom Objects, Custom Settings, and Custom Metadata. An HTTP callout, coupled with the Tooling API query, is used to retrieve the custom fields from both standard and custom objects.

Fetching Custom Object Fields

To retrieve Custom Fields from a Custom Object, the Custom Object ID should be used in the WHERE condition. For example, to fetch custom fields in the Account object, the endpoint URL is:

BaseURL+'/services/data/v38.0/tooling/query/?q=Select+Id,DeveloperName+from+CustomField+Where+TableEnumORId='+ a0B5B0000018IhT+ "'"

Fetching Standard Object Fields

To retrieve Custom Fields from a Standard Object, the Standard Object API name should be used in the WHERE condition. For example, to fetch custom fields in the Account object, the endpoint URL is:

BaseURL+'/services/data/v38.0/tooling/query/?q=Select+Id,DeveloperName+from+CustomField+Where+TableEnumORId='+ Account+ "'"

It’s important to add the Base URL of the organization to the Remote Site Settings as the Endpoint URL of the HTTP Request. Using a Wrapper class in the controller, we can display the Custom Field ID and Name of the Account object.

Custom Field List

Implementing the Visualforce Page

Step 1: Create a Visualforce Page

<apex:page controller="ToolingAPIObjectsController_AC">
    <apex:form>
        <apex:pageBlock title="Tooling API">
            <apex:pageMessages />
            <apex:pageblockSection>
                <apex:pageBlockSectionItem>
                    <apex:outputLabel value="Object Types" />
                    <apex:outputPanel>
                        <apex:selectList value="{!toolingObjectName}" multiselect="false" size="1">
                            <apex:selectOption itemValue="" itemLabel="- Select -"></apex:selectOption>
                            <apex:selectOption itemValue="StandardObjects" itemLabel="Standard Objects ({!standardObjectsList.size})" />
                            <apex:selectOption itemValue="CustomObjects" itemLabel="Custom Objects ({!customObjectsList.size})" />
                            <apex:selectOption itemValue="CustomSettings" itemLabel="Custom Settings ({!customSettingsList.size})" />
                            <apex:selectOption itemValue="CustomMetaData" itemLabel="Custom MetaData ({!customMetaDataList.size})" />
                            <apex:actionSupport event="onchange" action="{!reset}" reRender="Picklistvalue, CustomFieldList" />
                        </apex:selectList>
                    </apex:outputPanel>
                </apex:pageBlockSectionItem>
            </apex:pageblockSection>
            <apex:pageBlockSection id="Picklistvalue">
                <apex:pageBlockSectionItem>
                    <apex:outputLabel value="List of Objects" />
                    <apex:outputPanel>
                        <apex:selectList size="1" value="{!selectedValue}">
                            <apex:selectOption itemValue="" itemLabel="- Select -"></apex:selectOption>
                            <apex:selectOptions value="{!listOfValues }"></apex:selectOptions>
                            <apex:actionSupport event="onchange" action="{!getListOfObjects}" status="processing" reRender="CustomFieldList,processing" />
                        </apex:selectList>
                    </apex:outputPanel>
                </apex:pageBlockSectionItem>
            </apex:pageBlockSection>
        </apex:pageBlock>
        <apex:actionStatus id="processing">
            <apex:facet name="start">
                <apex:outputPanel>Processing... <img src="/img/loading.gif" /></apex:outputPanel>
            </apex:facet>
        </apex:actionStatus>
        <apex:outputPanel id="CustomFieldList">
            <apex:pageMessages />
            <apex:pageblock title="Custom Field List" rendered="{!showBlock}">
                <apex:outputLabel value="Total number of Custom Fields{!customFieldNameList.size}" />
                <apex:pageBlockTable value="{!wrapper}" var="wrap">
                    <apex:column headerValue="Custom Field Id">
                        <apex:outputLink target="_blank" value="/{!wrap.customFieldId}">{!wrap.customFieldId}</apex:outputLink>
                    </apex:column>
                    <apex:column headerValue="Custom Field API Name" value="{!wrap.customFieldName}" />
                </apex:pageBlockTable>
            </apex:pageblock>
        </apex:outputPanel>
    </apex:form>
</apex:page>

Step 2: Create a Controller

public with sharing class ToolingAPIObjectsController_AC {
    public Boolean showBlock { get; set; }
    public List<MyWrapper> wrapper { get; set; }
    public String selectedValue { get; set; }
    public String toolingObjectName { get; set; }
    public List<SelectOption> listOfValues = new List<SelectOption>();
    public String cusObjId { get; set; }
    public List<String> customObjectsList { get; set; }
    public List<String> customSettingsList { get; set; }
    public List<String> customMetaDataList { get; set; }
    public List<String> standardObjectsList { get; set; }
    public List<String> customFieldNameList { get; set; }

    public ToolingAPIObjectsController_AC(){
        customObjectsList = new List<String>();
        standardObjectsList = new List<String>();
        customSettingsList = new List<String>();
        customMetaDataList = new List<String>();

        for(Schema.SObjectType objTyp : Schema.getGlobalDescribe().Values()){
            if(objTyp.getDescribe().isCustom() && !objTyp.getDescribe().getName().contains('__mdt')){
                customObjectsList.add(objTyp.getDescribe().getLabel());
            }
            if(objTyp.getDescribe().isCustomSetting()){
                customSettingsList.add(objTyp.getDescribe().getLabel());
            }
            if(objTyp.getDescribe().getName().contains('__mdt')){
                customMetaDataList.add(objTyp.getDescribe().getLabel());
            }
            if(!objTyp.getDescribe().getName().contains('__')){
                standardObjectsList.add(objTyp.getDescribe().getName());
            }
        }
    }

    public List<SelectOption> getlistOfValues(){
        listOfValues.clear();

        if(toolingObjectName == 'CustomObjects'){
            for(String customObj : customObjectsList){
                listOfValues.add(new SelectOption(customObj, customObj));
            }
        }
        else if(toolingObjectName == 'StandardObjects'){
            for(String standardObj : standardObjectsList){
                listOfValues.add(new SelectOption(standardObj, standardObj));
            }
        }
        else if(toolingObjectName == 'CustomSettings'){
            for(String cusset : customSettingsList){
                listOfValues.add(new SelectOption(cusset, cusset));
            }
        }
        else if(toolingObjectName == 'CustomMetaData'){
            for(String cusmetdata : customMetaDataList){
                listOfValues.add(new SelectOption(cusmetdata, cusmetdata));
            }
        }

        return listOfValues;
    }

    public void getListOfObjects(){
        if(selectedValue != null){
            String endpoint, baseURL = URL.getSalesforceBaseUrl().toExternalForm();
            showBlock = true;

            if(selectedValue.contains(' ')){
                selectedValue = selectedValue.Replace(' ', '_');
            }

            if(toolingObjectName == 'CustomObjects' && selectedValue != NULL){
                endpoint = baseURL+'/services/data/v38.0/tooling/query/?q=SELECT+Id+FROM+CustomObject+WHERE+DeveloperName=' + selectedValue+   '';
                getListOfCustomField(endpoint);

                if(cusObjId != NULL){
                    endpoint = baseURL+'/services/data/v38.0/tooling/query/?q=SELECT+Id,DeveloperName+FROM+CustomField+WHERE+TableEnumORId='' + cusObjId+ ''';
                    getListOfCustomField(endpoint);
                }
            }
            else{
                endpoint = baseURL+'/services/data/v38.0/tooling/query/?q=SELECT+Id,DeveloperName+FROM+CustomField+WHERE+TableEnumORId='' + selectedValue+ ''';
                getListOfCustomField(endpoint);
            }
        }
    }

    public class MyWrapper {
        public String customFieldName { get; set; }
        public String customFieldId { get; set; }

        public MyWrapper(String name, String id) {
            customFieldId = id;
            customFieldName = name;
        }
    }

    public void reset(){
        showBlock = false;
        selectedValue = '';
        if(listOfValues != null){
            listOfValues.clear();
        }
    }

    public void getListOfCustomField(String endpoint){
        List<String> customFieldIdList;
        String response;
        HttpRequest req = new HttpRequest();
        req.setHeader('Authorization', 'Bearer '+ UserInfo.getSessionID());
        req.setHeader('Content-Type', 'application/json');
        req.setEndpoint(endpoint);
        req.setMethod('GET');

        Http h = new Http();
        HttpResponse res = h.send(req);
        response = res.getBody();

        if(response.contains('"entityTypeName":"CustomObject"')){
            JSONParser parser = JSON.createParser(response);
            customFieldNameList = new List<String>();
            customFieldIdList = new List<String>();

            while(parser.nextToken() != null){
                if((parser.getCurrentToken() == JSONToken.FIELD_NAME) && ((parser.getText() == 'Id'))){
                    parser.nextToken();
                    cusObjId = parser.getText();
                }
            }
        }
        else{
            JSONParser parser = JSON.createParser(response);
            customFieldNameList = new List<String>();
            customFieldIdList = new List<String>();

            while(parser.nextToken() != null){
                if((parser.getCurrentToken() == JSONToken.FIELD_NAME) && ((parser.getText() == 'DeveloperName'))){
                    parser.nextToken();
                    customFieldNameList.add(parser.getText());
                }
                if((parser.getCurrentToken() == JSONToken.FIELD_NAME) && (parser.getText() == 'Id')){
                    parser.nextToken();
                    customFieldIdList.add(parser.getText());
                }
            }

            wrapper = new List<MyWrapper>();

            for(Integer i = 0; i < customFieldNameList.size(); i++){
                wrapper.add(new MyWrapper(customFieldNameList[i], customFieldIdList[i]));
            }

            if(customFieldNameList.size() == 0){
                ApexPages.Message myMessage = new ApexPages.Message(ApexPages.Severity.Info, 'Info: There is no Custom Field for this Object.');
                ApexPages.addMessage(myMessage);
                showBlock = false;
            }
        }
    }
}

How to Use Tooling API Query in the Developer Console

You can also use the Tooling API query in the Developer Console by selecting the “Use Tooling API” checkbox in the Query Editor.

See also  Hazardous Area Classification: A Comparison of API vs. NFPA

Reference Link