13

We created an 100% native Salesforce IDE as a Managed Package which is using ToolingAPI and MetadataAPI via APEX. This works fine. But there is one thing which really bothers people after the installation of the package: they are annoyed be the well know endpoint-exception:

System.CalloutException: IO Exception: Unauthorized endpoint, please check Setup->Security->Remote site settings

In my opinion it is very bad to let users run into that exception - even if you may assume that developers are the target-group and they will figure the simple steps to proceed. We want to do it better.

What we have done first, was to include the most common endpoints as Remote-Sites in the packaged - knowing that we'll never be able to cover all due to the myDomain feature. So we have done it for na0..naX, eu0..euX and csX. Now, I have developed a strong dislike to this kind of bulk-inclusion, because:

  • it is bad to include > 30 endpoints in a (frustrated) preemptive measure when only a single one is really required.
  • there is an issue in the installer, when you need to confirm the endpoints and the list is too long, the confirm-button falls out of the visible screen area...
  • for sure, security review will dislike that sort of misdemeanor - and I totally agree.

So what can we do? Writing an manual or display a nice list of post-install manual steps? C'mon, still not nice. One guy from Salesforce suggested a Heroku-Proxy: APEX calls Heroku (one static endpoint, passing required endpoint as parameter) and Heroku calls back MetadataAPI to configure the endpoint... hmmmm... overkill...? What else?

I just read this very cool answer from @sfdcfox here Dynamically set remote site Setting in Apex Fast-forward discarding the options Flash, Java, Silverlight and ThirdParty as having too many tradeoffs, I really love the JavaScript idea!

Now, my draft would be to create a Post-Install page which contains the JavaScript. First thing to ask for username, password and token (or better go with OAuth?). Then to access MetadataAPI via a callout by Javascript and dynamically add the single endpoint to the org. Bottom-line: the Heroku-Pattern without Heroku ;-)

And finally here is my question: does anyone of you have accessed MetadataAPI directly from JavaScript? Even if in a completely different use-case, a JS-callout-skeleton to do just "something" with the MetadataAPI would help me a lot to figure out the final solution on my own. Or are there any obvious show-stoppers which prevent JavaScript from accessing the MetadateAPI?

I think it could be worth to share a concept here, since it would be a repeatable pattern usable for many use cases.

5
  • 2
    A very interesting question, the main question i would have is if the metadata API is supported from the Visualforce domain, if so this ought to be possible, if not, a proxy of some kind will always be required to avoid hitting the cross domain security in the browser. BTW, i'm also finding that despite documenting the Remote Site requirement users of my Declarative Rollup Summary tool also find it hard to remember to it, so i'd love to automate this as well. And yes +1 for Mr Fox for his suggestions! Jul 25, 2014 at 14:04
  • 1
    Of course if Salesforce permitted Apex HTTP callouts to their own servers without requiring Remote Site settings that would solve the problem perfectly! ;-) success.salesforce.com/ideaView?id=08730000000l4TkAAI Upvote now! :-) Jul 25, 2014 at 14:08
  • 1
    Ooops that was my 'make Metadata API native' idea, if thats not possible, and for all the other API's we want to use, this idea would be nice as well! success.salesforce.com/ideaView?id=08730000000l7iEAAQ ;-) Jul 25, 2014 at 14:17
  • 1
    Blog showing how i've implemented sfdcfox's excellent answer below in a tool i share with the community, andyinthecloud.com/2014/07/29/… Jul 29, 2014 at 8:14
  • @AndrewFawcett great! I definitely owe Mr. Fox and you a beer for this one!! Shouldn't we write a pure-JS wrapper API for the most common metadata tasks? IdeaExchange sometimes feels a bit like Waiting for Godot, so we may have to help ourselves for the good stuff ;-)
    – Uwe Heim
    Jul 29, 2014 at 8:51

1 Answer 1

11

You can actually call the metadata API from JavaScript exceptionally easy. I threw this together in response to this question:

var binding = new XMLHttpRequest(), payload = '<?xml version="1.0" encoding="utf-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-Instance"> <soapenv:Header> <urn:SessionHeader xmlns:urn="http://soap.sforce.com/2006/04/metadata"> <urn:sessionId>{!$Api.Session_ID}</urn:sessionId> </urn:SessionHeader> </soapenv:Header> <soapenv:Body> <createMetadata xmlns="http://soap.sforce.com/2006/04/metadata"> <metadata xsi:type="ns2:RemoteSiteSetting" xmlns:ns2="http://soap.sforce.com/2006/04/metadata"> <fullName>This Server</fullName> <description>The API endpoint</description> <disableProtocolSecurity>false</disableProtocolSecurity> <isActive>true</isActive> <url>{0}</url> </metadata> </createMetadata> </soapenv:Body></soapenv:Envelope>'.replace('{0}',window.top.location.protocol+'//'+window.top.location.hostname+'/');
    binding.open('POST', window.top.location.protocol+'//'+window.top.location.hostname+'/services/Soap/m/31.0');
    binding.setRequestHeader('SOAPAction','""');
    binding.setRequestHeader('Content-Type', 'text/xml');
    binding.onreadystatechange = function() { if(this.readyState==4) handleResponse(); }
    binding.send(payload);

Everything appears to work (I was getting some odd Invalid Session ID when I tried to grab it from the cookie directly, so the merge field should fix that). Feedback welcome. Also, you'll want to add the "base" version as well (use getSalesforceBaseUrl() in Apex Code to discover the correct URL to use).

6
  • Thanks Mr. Fox, that looks great (as always)! I will give it a try. To compose the right SOAP for the addition of the RemoteSite we could possibly use @AndrewFawcett 's github.com/financialforcedev/apex-mdapi and try to grab the SOAP-body somehow. Andrew: do you know if we can use the new CRUD functions for RemoteSites or we need to go with async-deploy?
    – Uwe Heim
    Jul 25, 2014 at 14:53
  • 1
    Don't see any reason why not give it a go! :-) Jul 25, 2014 at 15:00
  • 1
    @UweHeim Apache wsdl2js could help here too. I only manually constructed the code as a demo.
    – sfdcfox
    Jul 25, 2014 at 15:11
  • 1
    Works a treat, just updated my Welcome page to auto sense and offer a button to create! Very cool! github.com/afawcett/declarative-lookup-rollup-summaries/blob/… Jul 26, 2014 at 11:17
  • Awesome Mr. Fox! I didn't see that you provided the full SOAP already last week. There are just few typos at "</RemoteSiteSetting> </convertLead>" but very easy to figure and @AndrewFawcett provided the entire solution on a red carpet. Thanks a lot!
    – Uwe Heim
    Jul 29, 2014 at 8:58

You must log in to answer this question.

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