3

Background

I'm trying to build upon Daniel Peter's approach in his HyperBatch library to build a HyperQuery tool that will allow me to get better insight into some large tables we have without needing to export the whole table to do external analysis.

Problem

Unfortunately, I stalled out at a pretty early stage, as I'm having trouble getting any Javascript code to identify which classes extend an interface. That's because every single class is returning a null SymbolTable.

Analysis

  • I simplified the AJAX as far as I could and still I get SymbolTable = null for every single ApexClass.
  • The below MVR works just fine in other sandbox environments, so it seems like the problem is specific to this sandbox.
  • The exact same query in the developer workbench returns a populated SymbolTable, so the problem does not appear to be my class(es) or profile.
  • The SymbolTable is null for every ApexClass record, even after I recompiled all classes.
  • If I run the query via Execute Anonymous, the SymbolTable is populated. I can pass that same session id into my AJAX call instead of what I get from the GETSESSIONID merge variable and the behavior remains unchanged.

Question

Why might my SymbolTable values be null only when queried via Javascript?

Repro

Javascript

Page/Script

<apex:page showHeader="false" sidebar="false">
    <script>
        "use strict";
        var query = "SELECT Name, SymbolTable FROM ApexClass WHERE NamespacePrefix = null",
            url = "/services/data/v39.0/tooling/query/?q=" + encodeURIComponent(query),
            request = new XMLHttpRequest();

        request.open("GET", url, /*async*/ true);
        request.setRequestHeader("Authorization", "Bearer {!GETSESSIONID()}");
        request.onreadystatechange = function () {
            if (request.readyState === 0 || request.readyState === 4) {
                var i, records = JSON.parse(request.responseText).records;

                console.debug(records); // results show up
                for (i = 0; i < records.length; i++) {
                    if (records[i].SymbolTable) {
                        console.debug(records[i]); // nada
                    }
                }
            }
        }
        request.send();
    </script>
</apex:page>

Console Log

Console Log

Workbench

Endpoint

/services/data/v39.0/tooling/query/?q=SELECT+SymbolTable+FROM+ApexClass+WHERE+Name='HyperQuery'

Result

Workbench

Execute Anonymous

Script

String query = 'SELECT+Name,SymbolTable+FROM+ApexClass+WHERE+Name=\'HyperQuery\'';
String root = URL.getSalesforceBaseUrl().toExternalForm();
String endpoint = root + '/services/data/v39.0/tooling/query/?q=' + query;

HttpRequest request = new HttpRequest();
request.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionId());
request.setEndpoint(endpoint);
request.setMethod('GET');

Map<String, Object> response = (Map<String, Object>)
    JSON.deserializeUntyped(
        new Http().send(request).getBody()
    );
List<Object> records = (List<Object>)response.get('records');
Map<String, Object> result = (Map<String, Object>)records[0];
system.assertNotEquals(null, result.get('SymbolTable'));
system.debug(result.get('SymbolTable'));

Debug Excerpt

{constructors=({annotations=(), location={column=5, line=6}, modifiers=(private) ...

  • 1
    I hate to break this to you... but it works in my developer org. Whatever is going on is not JavaScript's fault, or even how you're calling the API. Something else is afoot, and will require more sleuthing. The only way I was able to simulate a null SymbolTable in my org was to create two classes, make one dependent upon the other, then delete the class that the other depended on. – sfdcfox Apr 5 '17 at 17:00
  • 1
    A few guesses. 1) The Session ID you got in Visualforce might not be sufficient to access the SymbolTable - Ref. 2) The ApexClass needed to be recompiled to populated the SymbolTable - Ref. Unlikely if it works from Workbench. 3) Something in the specific SymbolTable is running afoul of a serialization process. What does the raw request.responseText look like? – Daniel Ballinger Apr 5 '17 at 21:47
  • 1
    "If there is not a cached version of SymbolTable, it will be compiled in the background and the query might take longer than expected. The SymbolTable returned from ApexClass does not contain references; to retrieve a SymbolTable with references, use ApexClassMember." - Ref – Daniel Ballinger Apr 5 '17 at 21:58
  • @AdrianLarson So it appears to be specific to one sandbox and accessing the SymbolTable via Javascript. Really makes it sound like something that is coming back in the response is dropping the SymbolTable. Maybe a problem Header or Cookie on the request? I'd be looking closely at the request and response in the Network tab of the Chrome Developer tools. – Daniel Ballinger Apr 5 '17 at 23:01
  • @DanielBallinger Also an interesting hypothesis, but I'm sending exactly the same headers in both environments as well. I'll take a closer look using Chrome Developer tools though. – Adrian Larson Apr 5 '17 at 23:36

Your Answer

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

Browse other questions tagged or ask your own question.