5

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
    What you really want is GET ../services/data/v27.0/tooling/query/?q=SELECT+SymbolTable+FROM+ApexClass – Thomas Mar 4 '14 at 18:56
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.

| improve this answer | |
  • 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?! – Robert Sösemann Feb 15 '13 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. – Robert Sösemann Feb 15 '13 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 Feb 15 '13 at 23:49
  • grigriforce: I couldn't find an example on how to do that with the REST API – Robert Sösemann Feb 16 '13 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. – Greg Grinberg Feb 19 '13 at 19:36
0

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;
            }
        }
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.