Andy in the Cloud

From BBC Basic to Force.com and beyond…

Querying Custom Object and Field IDs via Tooling API

72 Comments

There are quite a few blogs describing a means to build Salesforce URL’s to standard pages for creating records, that can be applied to Custom Buttons in order to pre-populate field values when a user clicks a button. For example replacing the New button on a Related list.

IMPORTANT NOTE: The URL format Salesforce uses for the standard UI is not a supported API and thus subject to change. Though it has become quite common practice in order to improve the usability of the standard UI.

Making things a little more Robust

Other than URL format assumption, there is an area of further fragility shared amongst the solutions I’ve seen to-date. Which is how to obtain and manage the custom field Id’s needed on the URL’s (since they don’t take field API names). Either via discovering these manually or dynamically via using REGEX parsing of the HTML source code of the Salesforce standard pages. Although there is a Salesforce IdeaExchange submission to have Salesforce expose these Id’s via Apex Describe, it apparently has not been addressed… or so it would seem that is….

Tooling API support for Custom Object and Custom Fields

Probably the least obvious aspect of the Tooling API is its ability to actually query (via REST or SOAP) the CustomObject and CustomField objects (which are not accessible via Apex SOQL). As we know every object in Salesforce has an ID, this got me wondering if in this case its the same ID’s needed by the approaches used above. And thus if so, would serve as a more supported and less risky means to obtain the custom field Id’s. I have also wanted to start building out a Apex wrapper for the Tooling API for a while now. The following Apex code shows the library in action…

// Constructs the Tooling API wrapper (default constructor uses user session Id)
ToolingAPI toolingAPI = new ToolingAPI();

// Query CustomObject object by DeveloperName (note no __c suffix required)
List<ToolingAPI.CustomObject> customObjects = (List<ToolingAPI.CustomObject>)
     toolingAPI.query('Select Id, DeveloperName, NamespacePrefix From CustomObject Where DeveloperName = \'Test\'').records;

// Query CustomField object by TableEnumOrId (use CustomObject Id not name for Custom Objects)
ToolingAPI.CustomObject customObject = customObjects[0];
Id customObjectId = customObject.Id;
List<ToolingAPI.CustomField> customFields = (List<ToolingAPI.CustomField>)
     toolingAPI.query('Select Id, DeveloperName, NamespacePrefix, TableEnumOrId From CustomField Where TableEnumOrId = \'' + customObjectId + '\'').records;

// Dump field names (reapply the __c suffix) and their Id's
System.debug(customObject.DeveloperName + '__c : ' + customObject.Id);
for(ToolingAPI.CustomField customField : customFields)
     System.debug(
          customObject.DeveloperName + '__c.' +
          customField.DeveloperName + '__c : ' +
          customField.Id);

This results in the following list of fields and Id’s against my test custom object…

00:27:38.845 (845882069)|USER_DEBUG|[45]|DEBUG|Test__c : 01IG00000021cXoMAI
00:27:38.846 (846249350)|USER_DEBUG|[47]|DEBUG|Test__c.A_Number__c : 00NG0000009Y0I9MAK
00:27:38.846 (846305290)|USER_DEBUG|[47]|DEBUG|Test__c.Colours__c : 00NG0000009prwyMAA
00:27:38.846 (846328856)|USER_DEBUG|[47]|DEBUG|Test__c.Date__c : 00NG0000009BrnxMAC
00:27:38.846 (846513094)|USER_DEBUG|[47]|DEBUG|Test__c.Message__c : 00NG0000009Y0IOMA0
00:27:38.846 (846535746)|USER_DEBUG|[47]|DEBUG|Test__c.MultiPick__c : 00NG000000AcULrMAN
00:27:38.846 (846558753)|USER_DEBUG|[47]|DEBUG|Test__c.MyCheckbox__c : 00NG0000009Y5C8MAK
00:27:38.846 (846741056)|USER_DEBUG|[47]|DEBUG|Test__c.RichText__c : 00NG0000009XaRJMA0
00:27:38.846 (846816183)|USER_DEBUG|[47]|DEBUG|Test__c.Text__c : 00NG0000009prxwMAA

Summary

This blog serves as a means to demonstrate an initial starting point for a Apex wrapper around the Tooling API (the GitHub repo has the full code with tests to deploy direct via the usual link). Be sure to setup the appropriate Remote Site setting for the Tooling API end point (easiest way is to run the code and note the URL given in the exception). Finally a means for for the various authors of the solutions above to update their code. Enjoy!

72 thoughts on “Querying Custom Object and Field IDs via Tooling API

  1. Very nice. For managed packages, it would be cool to run this code from the InstallHandler and dump the field ids in a list custom setting for convenient/fast access. Can you see any problems with that approach?

  2. So long as the appropriate Remote Site setting was added before hand this is possible. I’m going to try to convince Salesforce to white list its own servers one day, since it seems an odd requirement to ask the user to permit calls to the Salesforce servers.

  3. Thanks.

    I agree with you about Salesforce white listing its own servers. I have an app that uses OATH and supports Facebook, Google or Salesforce itself as the possible authorization sources. But for the Salesforce one, getting an email address involves a 302 being returned with a location that looks like it could be just about any Salesforce instance, meaning a Remote Site might need adding for all 17 NA instances…

  4. Pingback: Hack to find field ids – allows a default UI “New” page to be pre-populated | Force 201

  5. When I implemented this solution I found that the Tooling API does not return an exact match to the html input Id. Here are two examples:

    Tooling API ID = 00NK00000013TUFMA2
    HTML input ID = 00NK00000013TUF

    Tooling API ID = 00NU0000001Cxs4MAC
    HTML input ID = 00NU0000001Cxs4

    If you truncate the last three characters of the Tooling API ID the code works and auto-populates the form fields.

  6. Hi Andrew,

    Thanks for your hard work – You may be aware of this already but the source on GitHub at the moment is installing with errors.

    Failures:
    classes/ToolingAPIDemo.cls(36,4):Method does not exist or incorrect signature: [ToolingAPI].queryCustomObject(String)
    classes/ToolingAPITest.cls(81,3):Invalid type: ToolingAPI.CustomObjectQueryResult

    I know its a work in progress but thought Id let you know.

    Best,

    Don

    • Ah yes, sorry about that, do you want me to dig out the working version at the time the blog was posted and put it in a Gist? I probably should do this anyway tbh.

  7. That would be awesome!

    Best,

    Don

  8. I just grabbed a prior commit from Jan 4th – I just loaded into sf but haven’t tested but I should be good – thanks!

    • Cool, good to know, i’ve made a note to sort this out for future blog readers, thanks for keeping me honest! Btw, what is it your doing out of interest? (its cool if you don’t want to share btw).

    • I’ve updated the repo to fix the demo class and test class, feel free to try again if you want the latest.

      • Hey Andy,

        I think the code example is still broken. I plan to use this class to prepopulate a native Salesforce page via URL dynamically.

      • The repo is valid, let me check the blog sample code though, sorry about this.

      • Ok yeah, apologies forgot to update the code sample in the blog, it now matches the latest in the repository. Have fun!

  9. Pingback: Finding Visualforce field ids | Force 201

  10. Pingback: Going Native with the Apex UML Tool and Tooling API! | Andy in the Cloud

  11. Hi Andy,

    Thanks for the info about the Tooling API. I checked out your code example for custom object/fields and it works great for a sample custom object in my Org. However, I have some custom fields in the Lead standard object and from what I found from the Salesforce Tooling API doc, the API does not seem to support standard objets – which is a bummer.

    The workaround is that I am using the listMetaData method from your excellent Metadata API apex wrapper, and I am able to get the Ids of custom fields in the Lead object. The only issue with this method is that it gives the Ids of all the fields in the Org, thus is probably not the most efficient way to get to find the Id of a specific custom field. Please let me know if there is a better way to find the Ids.

    Thanks again for the API classes. I find them very useful.
    Madhav

  12. Hi Andrew Fawcett,
    I displayed all custom objects in visualforce page using wrapper class with delete and edit command links.when i click delete command link how to delete a custom object from database?( i try to delete custom object using key prefix but visualforce error invalid id )

    please help me…………

    • Checking the Salesforce documentation, it does not appear that delete is supported for CustomObject’s, nor for CustomField sorry.

  13. Hi Andrew Fawcett,
    can you just send me that link ?

  14. Hi Andrew Fawcett,
    Using tooling api i try to delete custom objects and custom field from visualforce page.Is it possible to delete custom objects and custom fields directly from visualforce page?

    please help me…

  15. Hi Andrew Fawcett,
    is there any otherway to delete custom objects or fileds from the vf page, please help me on this?

    pleadse help me……….

    • In theory you could use the Metadata API’s “deploy” method by providing a zip file that contains a destructivePackage.xml containing the custom object or field you want to delete. This is only in theory i have never tried it for VF. You could start by adapting the Deploy demo controller in the Apex Metadata API library. Sorry I cannot be of much more help at this time it is a busy time. If you are struggling create an issue on GitHub under the Apex Metadada API repository and i will see if i can build an example in the future. Thanks, hope this helps!

  16. Hi Andrew Fawcett,

    I created fields using visualforce page(through metadata api).How to add created fields from pagelayout to fieldset through coding(Dynamically)and how to iterate fieldset in an visualforce page dynamically (through coding).

    please help me………..

  17. Hi Andrew Fawcett,

    I deleted one field from visualforce page using metadata api,but I displayed list of fields using wrapper class and related command link Del.I try to delete one field but all fields will be deleted from database and following error will be came

    System.LimitException: rajesh:Too many callouts: 11
    Error is in expression ‘{!doDelete}’ in page rajesh:createfields

    Class.rajesh.MetadataService.MetadataPort.deleteMetadata: line 8470, column 1
    Class.rajesh.MetadataController11.doDelete: line 15, column 1

    line 15: MetadataService.AsyncResult[] results = service.deleteMetadata(new List { customField });

    How to solve above Error.

    please help me…………

    • This is a governor limit of the platform. You can work better within it by passing multiple fields on line 15 and don’t call deleteMetadada in a loop.

  18. Hi Andrew Fawcett,

    you know i created fields using metadata api but created fileds Field-Level Security for Profile place visible and editable checkboxes not checked(any profile not checked i am a administrator).Every time i will goto Setup->Manage->users>administrator->custom field level security->related object name and click view and checked to metadata created fields visible and editable.How to check this visible and editable Using metadata api dynamically .

    please help me………..

  19. sorry here Read-only insted of editable

  20. Hi Andrew Fawcett,
    please send me related link i don’t no how to Create a Permission Set using Apex DML and assign to the user.

    please help me………

  21. Hi Andrew Fawcett,

    Using visualforce page everytime i will create new object. Is it possible to add permission set everytime created object dynamically using this permission set?………..

    please help me…………..

  22. Hi Andrew Fawcett,
    Actually i try that link but there is no result.Using the above link i con’t able to do anything.I search in google also there is no information related to fields visible and Read-only permissions related to administrator profile.There is any related link is available.Please send me

    please help me……………….

  23. Hi Andrew Fawcett,

    I solved above problem using metadata api

    thanks

    • That’s great well done! I am curious what kind of tool are you building, what end user problem does it solve?

  24. I tried to use that in a managed package controller and as non-admin user I get a
    UP2GO_ITE.ToolingAPI.ToolingAPIException: INVALID_TYPE : sObject type ‘CustomEntityDefinition’ is not supported.

    Class.UP2GO_ITE.ToolingAPI.submitRestCall: line 1841, column 1
    Class.UP2GO_ITE.ToolingAPI.submitRestCall: line 1782, column 1
    Class.UP2GO_ITE.ToolingAPI.submitRestCall: line 1774, column 1
    Class.UP2GO_ITE.ToolingAPI.query: line 105, column 1
    Class.UP2GO_ITE.TravelCtrlExt.getExpenseObjectId: line 125, column 1
    Class.UP2GO_ITE.TravelCtrlExt.getRelatedListId: line 109, column 1

    I checked the RemoteSiteSettings. Everything is ok?!

  25. Hi Andrew Fawcett,
    I created checkbox using metadata api.My requirement is checkbox creation time i gave some values with comma separated like A,B,C,D.How to display created checkbox field as look like following in an visualforce page

    FieldName
    Checkbox A
    Checkbox B
    Check box C
    Checkbox D using metadata api

    Please Help me………………..

  26. This is blog post about Tooling API, not Metadata API, unless you have a question or comment related to the blog post content.

    i think it is best you either post your questions on the GitHub repositories related to the libaraires Apex Metadata API (https://github.com/financialforcedev/apex-mdapi/issues?state=open) or on Salesforce StackExchange (http://salesforce.stackexchange.com/).

    Finally I think this question is more general and you could get help from a broader selection of people on http://salesforce.stackexchange.com/. I really want to help you, but i find your questions hard to understand and to broad. The StackExchange approach will help you to be more specific and ask more general quesitons. I also hang out on that site as well! 🙂

  27. Hi Andy,

    Thanks for all the hard work with these APIs. They’re awesome. However, I’m have a problem querying for WorkflowRules by LastModifiedDate. Here is the line in my code that’s giving me problems:

    List workflowRules = (List)toolingAPI.query(‘SELECT TableEnumOrId, LastModifiedDate FROM WorkflowRule Where LastModifiedDate > ‘ + json.serialize(dt).replace(‘”‘, ”) ).records;

    I’ve tried a few different approaches to this, but each have given me errors. I’m assuming that I’m just missing the easy answer here.

    What is the correct way to achieve this?

    Thanks, Bob

  28. Hello Andy,

    Have a question about meta data for standard object… Can we use tooling API to get the ID of a field on standard object? The custom Object does not seem to give me details of Standard Object.

  29. Pingback: How do I prepopulate fields on a Standard layout? | CL-UAT

  30. Pingback: Workaround for URL Hack to prepopulate fields on standard page layout | DL-UAT

  31. Andrew,

    I’m not sure how you do it, but pretty much every time I have searched Google for something Salesforce related I end up on your site and finding the answer.

  32. Awesome Stuff!!!
    The Code as given does not work; at least not for me;
    2 issues – CustomObject & CustomField are defined in ToolingAPIWSDL and not ToolingAPI as mentioned in the code;
    Typecasting does not save and compile properly

    I even tried using the overloaded function ToolingAPI.query passing the class of the records; this compiles fine but during runtime it fails with a null point exception


    12:01:11.971 (971445424)|METHOD_ENTRY|[84]|01pR0000000CQTS|ToolingAPI.castQueryResultToSubclass(ToolingAPIWSDL.QueryResult, String)
    12:01:11.971 (971467885)|SYSTEM_METHOD_ENTRY|[166]|String.isEmpty(String)
    12:01:11.971 (971482283)|SYSTEM_METHOD_EXIT|[166]|String.isEmpty(String)
    12:01:11.971 (971495574)|METHOD_ENTRY|[167]|01pR0000000CQTS|ToolingAPI.castSobjectArrToSubclass(List, String)
    12:01:11.971 (971514875)|SYSTEM_METHOD_ENTRY|[173]|System.Type.forName(String)
    12:01:11.972 (972434482)|SYSTEM_METHOD_EXIT|[173]|System.Type.forName(String)
    12:01:11.972 (972446798)|SYSTEM_METHOD_ENTRY|[173]|System.Type.newInstance()
    12:01:11.972 (972680329)|SYSTEM_METHOD_EXIT|[173]|System.Type.newInstance()
    12:01:11.972 (972790904)|METHOD_EXIT|[167]|01pR0000000CQTS|ToolingAPI.castSobjectArrToSubclass(List, String)
    12:01:11.972 (972800092)|METHOD_EXIT|[84]|01pR0000000CQTS|ToolingAPI.castQueryResultToSubclass(ToolingAPIWSDL.QueryResult, String)
    12:01:11.972 (972806709)|METHOD_EXIT|[28]|01pR0000000CQTS|ToolingAPI.query(String, String)
    12:01:11.972 (972825933)|CONSTRUCTOR_EXIT|[1]|01pR0000000CQTI|(String)
    12:01:11.972 (972837404)|SYSTEM_MODE_EXIT|false
    12:01:11.972 (972925525)|FATAL_ERROR|System.NullPointerException: Attempt to de-reference a null object

    Class.ToolingAPI.castSobjectArrToSubclass: line 174, column 1
    Class.ToolingAPI.castQueryResultToSubclass: line 167, column 1
    Class.ToolingAPI.query: line 84, column 1

    changing to sObject_x instead of CustomObject or CustomField works for me as I need the Developer Name and the Id’s…

    Does anyone have a clue on the errors and typecasting

    • Are you using the rest branch code from the repo? If so please raise an issue on the GitHub repo and I will try to look this weekend, though I am quite busy with Dreamforce prep. There is another who works on this I can also alert him to help

  33. Is there a test class for the latest code in the master? I got everything working but are having a hard time writing the test class.

  34. As described in this Issue of the Apex wrapper, such code can currently not be called by non-admin users. This definitly reduces it’s usefulness. Isn’t there an alternative using the Metadata API and its Apex wrapper?

  35. Got the Restful version working great. Having trouble with permissions. All is good when user profile = sys admin. Any other user profiles get a http 400 error. Any ideas on correct permission settings?

  36. Hi Andy I cant seem to get this to work. If you have a spare minute Id appreciate it

  37. Pingback: URL hacking. Automatically fill field values on chilid page layouts | Bartosz Michał Borowiec

  38. Hi. Trying to install the REST version and I get “Oops, something went wrong…” when clicking on Login to Salesforce …

    • Ah the deploy too does not support branches. Not bet anyway. For now you will have to download the code and deploy manually via ant or an IDE

  39. Hi. I am using the code provided in the article for constructing the Tooling API wrapper in an Apex class and I get Error: Compile Error: Invalid type: ToolingAPI.

    Could you advice what I’m missing? Do I need to enable anything in Salesforce to use ToolingAPI? Thank you

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s