Find your content:

Search form

You are here

Has anyone, ever, successfully invoked the Metadata API from within Apex?

 
Share

While researching a semi-related question posted here earlier, I dug up some interesting and conflicting information regarding whether it's even possible to use the Metadata API from within Apex. I used to be of the belief that this was strictly forbidden, as others seem to agree with. In a related post a Force.com MVP states:

"You can't callout to any Salesforce web services from within Salesforce. You can only callout to external web services."

But looking around I found some hopeful posts along with a more recent post at the Force.com Cookbook which suggests that it should be possible. I've discovered plenty of people who claim that it is possible, though so far I can find absolutely nothing confirming that anyone has actually done this successfully.

For example, from this post:

Yes. You can use Ajax Toolkit to call Metadata API calls, but this possible only in VisualForce pages not Apex Class.
http://www.salesforce.com/us/developer/docs/ajax/index.htm

So if you (or anyone you know) has done this and can share the details of the process, or at least the steps to achieve a login and then a simple create() from within Apex, please share your results!


Attribution to: Adam

Possible Suggestion/Solution #1

I can confirm that it is indeed possible as h9nry has pointed out. I also had some issues getting the WSDL2Apex tool to generate anything usable, but was finally successful!

I really wanted to use the generated Apex code rather than construct and parse the XML. The main key is getting it to output the xsi:type attribute to indicate the type of component your dealing with. Once you sort this you can do things like this!

MetadataService.MetadataPort service = createService();     
MetadataService.CustomObject customObject = new MetadataService.CustomObject();
customObject.fullName = 'Test__c';
customObject.label = 'Test';
customObject.pluralLabel = 'Tests';
customObject.nameField = new MetadataService.CustomField();
customObject.nameField.type_x = 'Text';
customObject.nameField.label = 'Test Record';
customObject.deploymentStatus = 'Deployed';
customObject.sharingModel = 'ReadWrite';
MetadataService.AsyncResult[] results = 
    service.create(new List<MetadataService.Metadata> { customObject });

I've uploaded the modified Apex Metadata class to Github at https://github.com/financialforcedev/apex-mdapi. Along with a more detailed description of whats possible and what is not. As has previously been pointed out most of the methods return an async result, meaning you have to implement some kind of polling solution. I would love to work with someone to contribute a VF component perhaps for this?

I've also been wondering about the Metadata REST API, as its sitll in pilot. Having spoken to Salesforce it does seem that the GA release of this is sadly some way out. So if your keen to do this, and there are some really useful use cases to want to do so! Hopefully this will help in the meantime!

https://github.com/financialforcedev/apex-mdapi


Attribution to: Andrew Fawcett

Possible Suggestion/Solution #2

it's definitely possible to use CRUD based Metadata API calls in APEX. salesforce to salesforce and even loopback inside the same instance, we tried both. Adam already mentioned that a major part of the functionality is based on zipped metadata packages which cannot be created or unzipped using apex. however you can still create custom fields or objects using the create call which can be pretty useful. The ugly part of this is WSDL2APEX as it does not support schema type extensions but the Metadata WSDL has a super type Metadata that all components inherit from. We tried to modifiy the metadata WSDL and code WSDL2APEX produced but we ended up sending our own SOAP messages. Here's some simple code I just tested that illustrates how APEX can be leveraged to create a new custom object, just paste it into the developer console to test it:

HTTP h = new HTTP();
HTTPRequest req = new HTTPRequest();
req.setMethod('POST');
req.setHeader('Content-Type', 'text/xml');
req.setHeader('SOAPAction', 'create');

String b = '<?xml version="1.0" encoding="UTF-8"?>';
b += '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">';
b += '<soapenv:Header>';
b += '<ns1:SessionHeader soapenv:mustUnderstand="0" xmlns:ns1="http://soap.sforce.com/2006/04/metadata">';
b += '<ns1:sessionId>' + UserInfo.getSessionId() + '</ns1:sessionId>';
b += '</ns1:SessionHeader>';
b += '</soapenv:Header>';
b += '<soapenv:Body>';
b += '<create xmlns="http://soap.sforce.com/2006/04/metadata">';
b += '<metadata xsi:type="ns2:CustomObject" xmlns:ns2="http://soap.sforce.com/2006/04/metadata">';
b += '<fullName>sample__c</fullName>';
b += '<deploymentStatus>Deployed</deploymentStatus>';
b += '<description>created by the Metadata API</description>';
b += '<enableActivities>true</enableActivities>';
b += '<label>sample Object</label>';
b += '<nameField>';
b += '<displayFormat>AN-{0000}</displayFormat>';
b += '<label>sample__c Name</label>';
b += '<type>AutoNumber</type>';
b += '</nameField>';
b += '<pluralLabel>sample Objects</pluralLabel>';
b += '<sharingModel>ReadWrite</sharingModel>';
b += '</metadata>';
b += '</create>';
b += '</soapenv:Body>';
b += '</soapenv:Envelope>';

req.setBody(b);
req.setCompressed(false);
req.setEndpoint('https://na12-api.salesforce.com/services/Soap/m/25.0');
HTTPResponse resp = h.send(req);
System.debug(resp.getBody());

you probably have to adjust the endpoint to point to your server. Using some Visualforce and a actionPoller component you can develop a nice object builder and even check if the asynchronous object creation was succussful.


Attribution to: h9nry
This content is remixed from stackoverflow or stackexchange. Please visit https://salesforce.stackexchange.com/questions/1082

My Block Status

My Block Content