8

While still having this question open I found out about the new Tooling API and tried to access an ApexClass members via the Rest Explorer in the Workbench and could get it to run.

How would I be able to access this SymbolTable JSON object? When I tried this on an v27.0 org I got no error but also no results:

GET ../services/data/v27.0/tooling/query/?q=SELECT+SymbolTable+FROM+ApexClassMember

Is this not the right way to call the Tooling Api. The Force.com Tooling API Developer's Guide didn't help at all.

1
  • 1
    What you really want is GET ../services/data/v27.0/tooling/query/?q=SELECT+SymbolTable+FROM+ApexClass
    – Thomas
    Commented Mar 4, 2014 at 18:56

2 Answers 2

5

ApexClassMembers aren't the same thing as ApexClasses. If you want to work with an ApexClassMember you will need to create it through the tooling api first.

//First, the MetadataContainer
JSONObject metadataContainerRequest = new JSONObject();
metadataContainerRequest.put("Name", "SaveClass" + id); // Any unique name
JSONObject metadataContainerResponse = ToolingApi.post("sobjects/MetadataContainer", metadataContainerRequest);
System.out.println("MetadataContainer id: "+ metadataContainerResponse.get("id"));

// Then create an ApexClassMember.
JSONObject apexClassMemberRequest = new JSONObject();
apexClassMemberRequest.put("MetadataContainerId",metadataContainerResponse.get("id"));
apexClassMemberRequest.put("ContentEntityId", id); //This is the salesforce ID of any existing ApexClass. 
apexClassMemberRequest.put("Body", body); //if not changing the body can probably be excluded
JSONObject apexClassMemberResponse = ToolingApi.post("sobjects/ApexClassMember", apexClassMemberRequest);
System.out.println("ApexClassMember id: " + apexClassMemberResponse.get("id"));

//get the ApexClassMember you just created. Should include the SymbolTable.
JSONObject apexClassMemberResponse = ToolingApi.get("sobjects/ApexClassMember/" + apexClassMemberResponse.get("id"));

source: http://blogs.developerforce.com/developer-relations/2013/01/new-in-spring-13-the-tooling-api.html

I assume this is for performance reasons because salesforce probably doesn't want to store and re-create the entire symboltable every time you save a class.

You could of course do this all through workbench as well but that is a bunch of JSON generation and it might be easier to just spin up something quickly in your language of choice.

7
  • How should I use the Tooling Api for reflection of exiting classes when I only can query classes I have created before with the API?! Commented Feb 15, 2013 at 22:36
  • BTW: I am not getting back anything as I don't know which ID to provide in the URL. It seems to not be the Id of an ApexClass. Commented Feb 15, 2013 at 22:38
  • 3
    Right now you have to get the class body, then you can save it with isCheckOnly set to true. This will run a syntax check and populate the symbol table.
    – metadaddy
    Commented Feb 15, 2013 at 23:49
  • grigriforce: I couldn't find an example on how to do that with the REST API Commented Feb 16, 2013 at 9:52
  • 1
    @RobertS I'm working on a tooling api demo but it won't be ready for a while. A bit too much for an SFSE answer ;). btw, if this (or other) answers have answered your question you should mark them as accepted. Commented Feb 19, 2013 at 19:36
2

Another way to access the Tooling API via Apex is by simply establishing an HTTP connection and sending your query to /services/data/v48.0/tooling/query/?q=Select....

To see a full example of this (as well as deserializing the JSON response to access the data) see Audit Salesforce Org with Tooling API.

Here's a general gist of how it will work using an example that audits Salesforce Objects:

        // Establish HTTP connection
        HttpRequest req = new HttpRequest();
        req.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionID());
        req.setHeader('Content-Type', 'application/json');
        Http h = new Http();        
        
        // Gather our instance name to use for calls to the Tooling API
        String instanceName = System.Url.getSalesforceBaseUrl().toExternalForm();
              
        // Query 2 - Get Id, DeveloperName, Description, CreatedBy.Name, CreatedDate, LastModifiedBy.Name, LastModifiedDate from custom objects
        req.setEndpoint(instanceName + '/services/data/v48.0/tooling/query/?q=Select+Id,DeveloperName,Description,CreatedBy.Name,CreatedDate,LastModifiedBy.Name,LastModifiedDate+From+CustomObject');
        req.setMethod('GET');
        HttpResponse res1 = h.send(req);
        String responseBody = res1.getBody(); 
        
        // Deserialize the response into an object
        Map<String, Object> payload1 = (Map<String, Object>)JSON.deserializeUntyped(responseBody);
        
        for (Object data : (List<Object>)payload1.get('records')){
            Map<String, Object> record = (Map<String, Object>)data;
            
            String id = (String)record.get('Id');
            String formalizedId = id.substring(0,id.length() - 3);
            
            ObjectData oData = resultsMap.get(formalizedId);
            
            if(oData != null){
                String lastModifiedName, createdName;
                
                // Custom objects in a managed package do not include these details, so only attempt to add them if the createdByObject is not null
                Object createdByObject = (Object)record.get('CreatedBy');
                if(createdByObject != null){
                    Map<String, Object> createdByMap = (Map<String, Object>)createdByObject;
                    createdName = (String)createdByMap.get('Name');
                } 
                
                Object lastModifiedByObject = (Object)record.get('LastModifiedBy');
                if(lastModifiedByObject != null){
                    Map<String, Object> LastModifiedByMap = (Map<String, Object>)LastModifiedByObject;
                    lastModifiedName = (String)LastModifiedByMap.get('Name');
                }
                
                oData.description = (String)record.get('Description');
                oData.createdDate = formatDateTime((String)record.get('CreatedDate'));
                oData.lastModifiedDate = formatDateTime((String)record.get('LastModifiedDate'));
                oData.createdBy = createdName;
                oData.lastModifiedBy = lastModifiedName;
            }
        }

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .