7

I'm trying to update StaticResources from APEX. Currently I'm using async methods of apex-mdapi and it works good. On the other side I'm using apex-toolingapi to update ApexClasses which works even better. Therefore I would prefer to use ToolingAPI for StaticResource-Updates, too.

So why don't stick with MetadataAPI?

  • ToolingAPI is compared to MetadataAPI much faster (best-case MetadataAPI ~8sec vs. ToolingAPI ~4sec).
  • MetadataAPI needs a quite bulky handling with zipfiles. Solution is harder to maintain.

For classes, ToolingAPI works by putting a ApexClassMember into a ToolingAPI.MetadataContainer(). Works fine!

But for StaticResource, there is no "StaticResourceMember". Looking at the ToolingAPI documentation, there seems no way to do it with container. Now there is a:

public ToolingAPI.SaveResult updateSObject(SObject_x sobjectRecord)

Which is "Commented out per issue #12" because "Updates need to be "PATCH" methods, however, most clients balk at the PATCH method".

Tried to remove the comments from updateSObject and do it this way

    public static void StaticResourceUpdate(string name, string body){
        string      namespace = 'YourNamespace';
        string      soqlFilterNamespace = ' NamespacePrefix=null or NamespacePrefix=\''+ namespace +'\' ';
        integer     errors = 0;
        ToolingApi  tool = new ToolingApi();
        ToolingAPI.StaticResource item = new ToolingAPI.StaticResource();
        item.name = name;
        item.ContentType = 'text/javascript';
        item.body = EncodingUtil.base64Encode(Blob.valueof(body));
        ToolingAPI.SaveResult sr = tool.createSObject( item );
}

It did not work. Obviously my "client balked" - whatever it means.

Anyone experience in UPDATING StaticResources from within APEX using ToolingAPI?

2 Answers 2

9

One option is to use the Rest version of the ToolingAPI to create the StaticResource:

string name = 'testjs';
string contentType = 'text/javascript';
string body = 'alert("Hello World");';

    // The static resource is expected to be base64 encoded.
string base64EncodedBody = EncodingUtil.base64Encode(Blob.valueof(body));
System.debug(base64EncodedBody);

HttpRequest req = new HttpRequest();
req.setEndpoint( URL.getSalesforceBaseUrl().toExternalForm() + '/services/data/v29.0/tooling/sobjects/StaticResource');
req.setMethod('POST');
req.setHeader('Content-Type', 'application/json'); 
req.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionId());
// JSON formatted body
req.setBody(
    '{"Name":"'+name+'"'+
    ',"ContentType":"'+contentType+'"'+
    ',"Body":"'+base64EncodedBody+'"'+
    ',"CacheControl":"Public"}'
    );

Http http = new Http();
HttpResponse res = http.send(req);
System.debug(res);
System.debug(res.getBody());
System.debug(res.getHeaderKeys());

To update it you will need the StaticResource Id (starting with 081 key prefix) appended onto the end of the endpoint URL. Change the HTTP method to 'PATCH'.

Using this method with Workbench I as able to change the contents of the StaticResource by altering the base64 encoded body.

Workbench patching StaticResource Body

A note on changing the HTTP request method to PATCH. If you just change it directly against as req.setMethod('POST'); you will end up with the error:

System.CalloutException: Invalid HTTP method: PATCH

Instead keep the POST in the HttpRequest and use the `_HttpMethod=PATCH' query string parameter documented from Update a Record

    string name = 'testjs';
    string contentType = 'text/javascript';

HttpRequest req = new HttpRequest();

Http http = new Http();

req.setEndpoint( URL.getSalesforceBaseUrl().toExternalForm() + 
                '/services/data/v29.0/tooling/sobjects/StaticResource/08170000000GuZVAA0'+
                '?_HttpMethod=PATCH');
req.setMethod('POST');
//System.CalloutException: Invalid HTTP method: PATCH
req.setHeader('Content-Type', 'application/json'); 
req.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionId());
// JSON formatted body
req.setBody('{"Name":"'+name+'", "ContentType":"'+contentType+'", "Body":"YWxlcnQoIkhlbGxvIFdvcmxkIik0"}');

HttpResponse res = http.send(req);
System.debug(res);
11
  • this looks very good! I'll try it and come back with my results. Thanks a lot!
    – Uwe Heim
    Jun 30, 2014 at 4:15
  • I verified the solution from @DanielBallinger and it work great! just one followup question: did you try this PATCH also on ApexClass and ApexPage? If yes: did it work? If not: I'll try and give feedback.
    – Uwe Heim
    Jun 30, 2014 at 11:24
  • Does it work if place this code in a managed package?
    – Ayub
    Aug 7, 2017 at 12:10
  • @AyubAnsari It should be possible. The user would still need the required permissions to update the static resource. I.e. they can't update a static resource that is part of the managed package. Aug 7, 2017 at 20:41
  • 1
    Hmmm...thanks I came up with another workaround by using tooling api. Instead of adding the resource in package, I am creating it by apex code after installation. Thanks for your suggestions!
    – Ayub
    Aug 8, 2017 at 10:43
1

If you decide to use v50, you then need to modify the code

string name = 'testjs';
string contentType = 'text/javascript';
string body = 'alert("Hello World");';

    // The static resource is expected to be base64 encoded.
string base64EncodedBody = EncodingUtil.base64Encode(Blob.valueof(body));
System.debug(base64EncodedBody);

HttpRequest req = new HttpRequest();
req.setEndpoint( URL.getSalesforceBaseUrl().toExternalForm() + '/services/data/v50.0/tooling/sobjects/StaticResource');
req.setMethod('POST');
req.setHeader('Content-Type', 'application/json'); 
req.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionId());
// JSON formatted body
req.setBody(
    '{"Fullname":"'+name+'"'+
    ',"Metadata":{"contentType":"'+contentType+'"'+
    ',"content":"'+base64EncodedBody+'"'+
    ',"cacheControl":"Public"}}'
    );

Http http = new Http();
HttpResponse res = http.send(req);
System.debug(res);
System.debug(res.getBody());
System.debug(res.getHeaderKeys());

I used Daniel answer as the base and changed Name to Fullname, the other parameters embedded into Metadata key and changed Body to content and make other attributes lowercased.

You must log in to answer this question.

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