4

I just tried to implement Tooling API using REST API and did some basic callouts:

  • create a Apex class
  • creating a apex trigger
  • editing apex class
  • query a tooling api object
  • delete a apex class

out of these 5 tries, i got succeed in three. By tooling api I was able to create a Apex class and delete a apex class and query a objects.

Rest of my tries:-

When I try to edit a apex class which i created before

it always comes with a error UNABLE TO ACCESS OBJECT FROM A CROSS REFERENCE ID . I tried it in several ways changing letter cases, changing HTTP METHOD but no method works. Setting correct configuration according to Salesforce Winter '14 tooling API guide but it shows same error UNABLE TO ACCESS OBJECT FROM A CROSS REFERENCE ID. Why this error comes? Is some special access is required with these callout?

My code is

HttpRequest req = new HttpRequest();
req.setEndpoint('https://ap1.salesforce.com/services/data/v28.0/tooling/sobjects/ApexClass/'+oid+'?_HttpMethod=PATCH');
req.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionID());
req.setHeader('Content-Type', 'application/json');
req.setBody('{"Name":"NewApexClass1","Body":"public class TheApexClass1 { // DO SOMETHING }"}');
req.setMethod('POST');

I am not sure what going wrong here.

After I tried to create ApexTrigger but I stuck with TableEnumOrId. If I give it a name e.g Lead it says invalid object or... and if I don't mention, it show MISSING FIELD. I have read about creating ApexTrigger by Tooling API everywhere on internet but there is no answer for it. Work arounds are only that use simple REST API to create a trigger. If anybody has some good reason, why this is error? please share some knowledge. If there are more issues regarding tooling API then please share.

Note: Already read all question regarding tooling api on this site , developerforce community and several other places.

1

As Adam points out, and the Tooling API docs, you need to use a MetadataContainer. From the Tooling API Developer's Guide (pg.12)

To edit, save, or compile Apex classes, use ApexClassMember

Here is the reason: Apex must be compiled to ensure that all syntax and references are valid. For example, if I patch an ApexClass's name field, another class that uses that field (MyClass.MyStaticMethod()) is on longer valid. Salesforce could compile for you behind the scenes, but this poses several problems.

  1. Salesforce would then be doing more (much more) than the intended field patch, reducing the visibility into what is actually going on.
  2. A compile may take a long time. It wouldn't be good for the patch to take 5 minutes to return.

That is where the ContainerAsyncRequest comes in. It is explicitly saying, "I want to compile and save these changes to my org, and it may take a little while."

I agree the guide could provide more information on this point, and the describe metadata is misleading.

Creation is a little different though. A class will (most often) not contain complex references when it is created and (more importantly) no other classes will be referencing it. Also, an ApexClassMember must have an existing ApexClass to reference, so we must create it though the actual ApexClass

In terms of creating a trigger, it is the same as creating a class except that you need the TableEnumOrId. The table enum is the name of the sObject you want to trigger on. For example:

A post to the following resource with the following data will create an Apex Trigger.

https://<instance>/services/data/v29.0/tooling/sobjects/ApexTrigger

Body: "trigger aa on Account (before insert) {}"
TableEnumOrId: "Account"

I'm not sure why you were getting the invalid object or... error. I was able to create a trigger on Lead by replacing Account in the TableEnumOrId field AND in the body.

EDIT:

I just tried again in Summer'13 and got the same error message. The reason why you are getting that error is because Lead (or Account) is not accessible in the Tooling API. In other words, when a trigger is created via the Tooling API it tries to reference the object that it is triggering on. When this happens, it throws that error because the object is no accessible in the Tooling API. This has been fixed in Winter'14.

  • (+1) Thanks @Thomas. I agree with using Metadatacontainer. Your answer is theoretical and has good details, In tooling API practical scenarios are different for ex: In my opinion ApexTrigger can't be created (using REST API) till today not sure about tomorrow. TableEnumOrId causes error everywhere. Also I think https://<instance>/services/data/v29.0/tooling/sobjects/ApexTrigger will show you error The requested resource does not exist. – Ashwani Sep 4 '13 at 6:06
0

Could you be omitting the '/sobjects/MetadataContainer' part of the equation? For updating classes you need to first load them up into a MetadataContainer and work with them from there.

From the Tooling API Developer's Guide (pg. 36):

Usage

Use a MetadataContainer as a package for your tool’s workspace. As a user works in the tool, update the ApexClassMember, ApexTriggerMember, ApexPageMember and ApexComponentMember objects in the MetadataContainer and use a ContainerAsyncRequest object to save and deploy changes to the current organization.

  • It means it is necessary to use metadata container to update a apex class? I think metadata container is used to work with set of components which may reference each other. And on page 3 of Tooling API it is written, Use the GET method to retrieve records or fields, the DELETE method to delete records, and the PATCH method to update records. – Ashwani Sep 2 '13 at 5:36

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.