Apex Metadata API Introduction
The Salesforce Metadata API allows you to perform many org configuration and setup features programatically. Code examples given by Salesforce are in Java as the API is a SOAP API. In respect to the Apex library provided here, it wraps via the providedMetadataService.cls class the Salesforce SOAP version of the API to make it easier to access for Apex developers. Key terms, structures and operations exposed are still the same however, so reading the Salesforce documentation is still important. You should also review the readme and example class
Handling Asynchronous Aspects
The first challenging aspect is the fact that Salesforce provides this API as an asynchronous API. Which requires some additional coding approaches to handle its responses. Effectively polling for the result of the API calls. To make it easier the Apex library includes some examples for doing this in Batch Apex and using Visualforce’s apex:actionPoller component.
IMPORTANT UPDATE: Since Summer’14 (API 31.), Salesforce have removed the Async API’s in the Metadata API, see here for more details. If you are developing against v30.0 or below this information will still be useful to you. Indeed it is also still relevant to the deploy and retrieve operations.
Create Custom Field Example
Here is an example of creating a CustomField using the create operation. First creating an instance of the service which exposes all the operations and configures the authentication, by passing on the users current Session ID. For more information on calling SOAP from Apex, see here.
MetadataService.MetadataPort service = new MetadataService.MetadataPort(); service.SessionHeader = new MetadataService.SessionHeader_element(); service.SessionHeader.sessionId = UserInfo.getSessionId(); MetadataService.CustomField customField = new MetadataService.CustomField(); customField.fullName = 'Test__c.TestField__c'; customField.label = 'Test Field'; customField.type_x = 'Text'; customField.length = 42; MetadataService.AsyncResult[] results = service.create(new List<MetadataService.Metadata> { customField });
The code then needs to inspect the contents of AsyncResult and be prepared to pass it back to the API to poll for the results periodically. If you study the create documentation you will see a good summary of the steps. Basically calling one of the Metadata API operations, receiving the result and if needed repeatedly calling checkStatus.
You can call the checkStatus method from Apex, though you must have your code wait for Salesforce to process the request, either via Batch Apex context or via Visualforce and its AJAX support.
MetadataService.AsyncResult[] results = service.checkStatus(new List<String> { results[0].Id });
Calling checkStatus from Visualforce
If you have an interactive tool your building, you can use Visualforce and use the apex:actionPoller to store the AsyncResult in your controller and write a controller method to call the checkStatus, which the action poller repeatedly calls until the AsyncResult indicates the request is completed by Salesforce.
public with sharing class MetadataController { public MetadataService.AsyncResult result {get;set;} public PageReference createField() { // .. as per above ... result = createService().create(new List<MetadataService.Metadata> { customField })[0]; displayStatus(); return null; } public PageReference checkStatus() { // Check status of the request result = createService().checkStatus(new List<String> { result.Id })[0]; displayStatus(); return null; } private void displayStatus() { // Inspect the AsyncResult and display the result ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.Info, result.done ? 'Request completed' : 'Request in progress...')); if(result.state == 'Error') ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.Error, result.message)); if(result.done) result = null; } }
This controllers page then looks like this…
<apex:page controller="MetadataController"> <apex:form id="form"> <apex:sectionHeader title="Metadata Demo"> <apex:pageMessages> <apex:actionPoller action="{!checkStatus}" interval="5" rerender="form" rendered="{!NOT(ISNULL(Result))}"> <apex:outputPanel rendered="{!ISNULL(Result)}"> <apex:commandButton value="Create Field" action="{!createField}"> <apex:commandButton value="Delete Field" action="{!deleteField}"> </apex:outputPanel> </apex:form> </apex:page>
The demos for the retrieve and deploy operations also provide good examples of this here and here. Even though they don’t actually use the create operation of the Metadata API, polling for the AsyncResult is the same. Note that the two operations they use, retrieve and deploy are still useful for deploying code or retrieving configuration. But do have the added complexity of handling zip file format, something the library also provides some components for.
Calling checkStatus from Batch Apex
This part of the Readme describes a helper class MetadataCreateJob.cls to do this in Batch Apex which actually handles both calling the create and checkStatus methods for you. Sending the final results to an Apex handler class, in this case a default email handler is provided, but you write your own.
// Pass the Metadata items to the job for processing, indicating any dependencies MetadataCreateJob.run( new List<MetadataCreateJob.Item> { new MetadataCreateJob.Item(customField) }, new MetadataCreateJob.EmailNotificationMetadataAsyncCallback());
What org configuration can be access via the Salesforce Metadata API?
First review this topic from the Salesforce documentation. In respect to the CRUD (Create, Update and Delete) operations of the API you can only pass metadata / component types that extend the class Metadata (or MetadataService.Metadata in the Apex API). If you review the MetadataService.CustomField class you can see it does just this…
public class CustomField extends Metadata {
When you review the individual topics for the metadata component types in the Salesforce documentation, pay attention to those that state that they extend the Metadata type.
NOTE: As per the last section of the Readme for the library, it has manually made this modification as the Salesforce WSDL to Apex code generator that initially generated the wrap did not do this. Most of the popular Metadata types have already had this treatment in the Apex API.
October 28, 2013 at 5:56 pm
nice post. Can you please also write an intro for Tooling API? thx
October 28, 2013 at 6:29 pm
Sure, you mean from Apex?
October 31, 2013 at 5:45 pm
I’m playing around with your retrieve demo and was wondering how you retrieve more than one type in a single call. For example, I’m trying to get profiles and custom object types. I tried modifying the retrieveMedatadataItem() method and have something like this:
MetadataService.PackageTypeMembers packageType = new MetadataService.PackageTypeMembers();
packageType.name = ‘Profile’;
packageType.members = new String[] {‘Admin’};
retrieveRequest.unpackaged.types.add(packageType);
MetadataService.PackageTypeMembers packageType2 = new MetadataService.PackageTypeMembers();
packageType.name = ‘CustomObject’;
packageType.members = new String[] { ‘Account’,’Contact’ };
retrieveRequest.unpackaged.types.add(packageType2);
AsyncResult = service.retrieve(retrieveRequest);
But this only brings back CustomObject.
What am I doing wrong?
November 1, 2013 at 10:55 am
There is a bug in the above code, you are not setting up the packageType2 variable.
November 1, 2013 at 2:02 pm
Thanks Andrew. I realized my mistake too after my post. Ahh, the pitfalls of copy and paste!
November 1, 2013 at 5:20 pm
Ah no worries. Note that the Profile info is only complete as far as the other components you retrieve in the request. So for example, you will only get VF page permissions if you also include those in the package manifest. Though the permission set objects do not give all profile info, you may want to look at using SOQL to query those (under the covers the platform creates a permission set per profile).
November 1, 2013 at 10:53 am
Reblogged this on Sutoprise Avenue, A SutoCom Source.
December 5, 2013 at 8:42 am
Hi Andrew, I am absolutely grateful for this wonderful post. I was trying to tweak the generate code for couple of days but even after trying was missing some points, your code finally helped me to get through it. Unfortunately SFDC documentation is nil and SFDC must provide much more support for WSDL2APEX for this very useful feature. Thanks again.
December 5, 2013 at 9:03 am
Excellent, so pleased it helped out, have fun!
December 6, 2013 at 7:46 pm
Your very welcome, glad to have saved you some time. Good luck!
Pingback: Updating Layouts in Apex | Andy in the Cloud
January 1, 2014 at 8:36 am
I have setup the demo in my sandbox, but for some components in the metadataretrieve page, i get the following errors –
1. ApexClass – Collection size 2,298 exceeds maximum size of 1,000.
2. CustomField – IO Exception: Read timed out
Error is in expression ‘{!listMetadataItems}’ in page metadataretrieve
I have an idea how to handle the first error, what do i do about the second error?
January 5, 2014 at 2:29 am
RE: 1, Relates to a Visualforce limit, when displaying lists of items you cannot have more than 1000, see here for more info, http://www.salesforce.com/us/developer/docs/pages/Content/pages_controller_readonly_context.htm
RE: 2, Since the library is essentially a wrapper around a web service, the usual timeouts apply, it is likely for large orgs the listMedata operation takes longer, at least on initial attempts until internal caching kicks in. Default is 10 seconds, though it can be increased, see here for more info, http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_callouts_timeouts.htm
January 12, 2014 at 9:33 pm
Hi Andrew, thanks for posting and for the good work you have done here. I just completed some work to effectively create universal picklists based on this library. This saves my client a lot of headaches in updating multiple picklists all over the place and of course, reduces errors caused by mismatches between these lists.
January 12, 2014 at 10:12 pm
Thanks so much for feedback what you have been up to, really inspiring! Nice one!
February 27, 2014 at 2:14 pm
Hi Andrew, firstly, thanks very much for the excellent post. It was of great help. I am trying to access the metadata in one sandbox from another sandbox and it gives me error that the session Id is invalid. I believe this is because I first need to login to the target sandbox before I execute any query on it, but I am new to Apex and not sure how to do it from the Metadata API context. Below is my createservice() method, and I think the login part with the username and password needs to be included within this but not sure how. Can you please correct this and help here?
public static MetadataService.MetadataPort createService() {
MetadataService.MetadataPort service = new MetadataService.MetadataPort();
service.SessionHeader = new MetadataService.SessionHeader_element();
service.SessionHeader.sessionId = UserInfo.getSessionId();
return service;
}
Also I ‘m not sure if I need to send an AuthToken during the login process and if Yes, how? Will be of great help if you can shed some light on this.
Many Thanks!
Best Regards,
Deni
February 28, 2014 at 11:06 pm
Hi,
Yes you do need to login to the other org to get the correct Session ID. Take a look at this answer on StackExchange for an example of how to use the Salesforce API Web Service to perform a login from Apex.http://salesforce.stackexchange.com/questions/22704/sessionid-problem-in-schedule-batch-class/22736#22736
You can also go via the oAuth web flow (user enters user name password and redirects back to your page) to obtain an AuthToken (which will last longer if you want to make multiple metadata calls over a longer period of time and aviods you having to store the user/password). Once you have the oAuth token you can pass this in the sessionId field as well. I don’t have an example of this to hand unfortunately, though have been meaning to find one.
The above user name and password option should work fine, just be careful what you do with the user and password used for security reasons. Maybe not so much of an issue if its your own sandboxes i guess.
Hope this helps!
Thanks,
Andy
March 10, 2014 at 7:38 am
Thanks very much Andy! That really helped. Now my code is able to login to the target Org. I am trying to retrieve the details of the workflow alerts such as the name, description, sender, recipients, and email template. But I am able to retrieve only the alert fullname. For emailalert.description, emailalert.senderAddress, emailalert.template i get an error saying that the variables do NOT exist. But as per the Metadata API documentation these variables are available under workflowalert. Below is my code, can you please take a look and let me know if you see what I am missing here?
MetadataService.MetadataPort service = MetadataService.createService();
List queries = new List();
MetadataService.ListMetadataQuery queryEmailAlert = new MetadataService.ListMetadataQuery();
queryEmailAlert.type_x = ‘WorkflowAlert’;
queries.add(queryEmailAlert);
MetadataService.FileProperties[] properties = service.ListMetaData(queries,30);
for(MetadataService.FileProperties emailalert:properties){
system.debug(‘Email Alert Name:::’+emailalert.fullname);
system.debug(‘Created By:::’+emailalert.CreatedByName);
system.debug(‘Description:::’+emailalert.description);
system.debug(‘From:::’+emailalert.senderAddress);
system.debug(‘To:::’);
system.debug(‘CC:::’);
system.debug(‘Template:::’+emailalert.template);
}
Many Thanks in advance,
Deni
March 10, 2014 at 10:05 am
No problem, but could i request you move this to the GitHub repository, its a bit easier to see code and discuss there, https://github.com/financialforcedev/apex-mdapi/issues?state=open
March 10, 2014 at 1:20 pm
Sure Andrew, I have posted the issue in GitHub here – https://github.com/financialforcedev/apex-mdapi/issues/21
Can you please take a look and share your thoughts? Many Thanks!
March 11, 2014 at 2:20 pm
Hi Andrew,
I have updated the post in github with the latest status and findings. Could I please request you to take a look and share your thoughts since this is very urgent on my side to meet one my deliveries. Many Thanks!
https://github.com/financialforcedev/apex-mdapi/issues/21
March 11, 2014 at 10:12 pm
No problem, i’ve found the solution for you, unfortunately at this time it appears GitHub is having some issues. I have tried updating the issue by responding via email, you will need the attached class files (custom MetadataService.cls based on the one i used in the Spring’14 metadata api blog) and the following sample code, but i did get it to work! 🙂
// Read Workflow Alert
MetadataService.WorkflowAlert wfa =
(MetadataService.WorkflowAlert) service.readMetadata(‘WorkflowAlert’,
new String[] { ‘Account.Test’ }).getRecords()[0];
System.debug(‘Description ‘ + wfa.description);
System.debug(‘Sender Address ‘ + wfa.senderAddress);
March 20, 2014 at 5:58 am
Hi Andrew,
Is there any limitation on number of Objects/Fields that can be created in one call ?
March 20, 2014 at 1:56 pm
Yes the Salesforce Metadata API documentation states that only 10 items per ‘create’ call can be passed. In Apex you can only make 10 outbounded calls per request, so maximum in theory of 100. You could also deploy an entire object via the ‘deploy’ call, see the deploy examples, though these do need a client side peace of code.
March 21, 2014 at 5:48 am
Hi Andrew, while doing a webservice callout, is it possible to print the response XML to take a complete look at what is received? If yes can you please explain how?
Thanks
March 21, 2014 at 10:22 am
Yes, if you enable HTTP callout in the Debug log levels this will be in your Salesforce debug logs. http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_debugging_debug_log.htm
March 25, 2014 at 7:07 am
Thanks very much Andrew! I need help on designing a requirement. It mayn’t me related to this thread but don’t know how to reach you elsewhere, so posting it below, apologies if it’s wrongly placed here. Can you please take a look and share your thoughts?
Requirement:
Display an Emails related list under Contacts that displays all the emails sent to the Contact from Salesforce (from Email alerts, or Triggers, etc).
Basically whenever an automated email is sent from Salesforce, the details of the email(From, To, Date, Subject, Body, Attachments) should be saved along with the contact as a related list for future reference.
I understand whenever the email is sent from trigger or other classes through OutboundEmailHandler then it can be done within the code. But I’m not sure how to capture the automated emails that are sent from workflow alerts. Will appreciate any help on this.
Many thanks!
April 8, 2014 at 9:22 pm
Hi Andy,
Love the work that you have done here! I am curious to find out how things are coming along now that Spring 14 is out. Are you planning a v30.0 update to this wrapper?
April 8, 2014 at 9:23 pm
Yes I am indeed! Cannot wait, all my dev orgs are in EU instances and I need to wait till the 18th! Soon though!
April 9, 2014 at 11:17 am
Hi Andrew,
the class MetadataPort within the MetadataService.cls does not contain the method readMetaData(), so i can not write this in my apex code ‘service.readMetadata. Can you help me please.
April 9, 2014 at 11:33 am
This is because i have not released the Spring’14 version of it yet, you can get a preview version of it here in the meantime, though it is incomplete. Once the Spring’14 rollout completes on all instances (specifically my EU ones!) i will release it before the end of the month formally with full support for all metadata types (only some are supported in the preview version). https://gist.github.com/afawcett/8108494
April 9, 2014 at 1:12 pm
Great !! thank you so much
April 17, 2014 at 6:04 am
Awesome!!! Thanks a lot buddy for this awesome post. I spent hours reading salesforce metadata API documentation but I could not find anything. But after going through your apex library in Github I was able to create new object an fields in couple of hours.
But now I am stuck in another issue. As per my requirement I have to add components to Home Page Layout through Apex, and my client has suggested to use Metadata API. I found two wrapper classes “HomePageLayout” and “HomePageComponent”,, but I am not able to add components to HomePageLayout . I am stuck in this big time. Could you help me please
Thanks in Advance
Roshan
April 17, 2014 at 10:55 am
Sure happy to try and help, do you want to raise an issue on the GitHub site and we can exchange code samples, error messages etc. more easily. https://github.com/financialforcedev/apex-mdapi/issues?state=open
April 22, 2014 at 9:44 am
Hi Andrew,
I’m using the MetadataService to get the Flows (I use the listMetadata method) and for each element i want to get the flow object (I use the readMetadata method).
this is my code :
MetadataService.Flow flow = (MetadataService.Flow) service.readMetadata(‘Flow’, new String[] { ‘fullNameOfTheFlow’ }).getRecords()[0];
And this the error:
Incompatible types since an instance of MetadataService.Metadata is never an instance of MetadataService.Flow
Can you please tell me how to get the Flow object from the fullName ?
April 22, 2014 at 9:46 am
This is not yet supported on the wrapper, there are some challanges, I hope to release support within the next week or so. Can you open an issue on the GitHub repo in the meantime please.
Pingback: Apex Metadata API and Spring’14 : Keys to the Kingdom! | Andy in the Cloud
May 3, 2014 at 6:34 am
@Andrew very helpfull post, i have a question here .. i am trying to update a class by using metadata api.. but as soon i re zip the three components like: .cls, meta-xml and package.xml and convert it to base64 string and assign it to content the update_x methord gives me an error like: INVALID_TYPE: This type of object is not available for this organization faultcode=sf any idea why its giving me this ?? i appreciate for any help as i am banging my head with this for a week.
May 3, 2014 at 7:58 am
You have to use the deploy method, take a look at the deploy demo described on the readme file
May 29, 2014 at 6:55 am
Hi Andrew.Must thank you for this wonderful post. I was playing around within the apex-mdapi and stucked with a problem . Not able to find the bug in the MetadataDeploy page when clicking on Deploy button , gives me null pointer . Also the Metadata brwoser page , not able to retieve the tree.
Note that i’m new to this and want to learn. Please help. Thanks in advance.
May 29, 2014 at 10:52 am
That is odd, can you raise an Issue on the GitHub site and I’ll try and take a look asap for you!
May 30, 2014 at 3:29 am
Thanks Andrew, got a solution for MetadataDeploy page. But , can you please guide me as to how to retrieve the JSON in MetadataBrowser page . When i click on Retrieve button on that page , it says no nodes selected ..It displays me the empty list.
I cannot say this is an issue but somewhere i’m making a mistake. Please help ASAP.
June 3, 2014 at 11:55 pm
This is because you have not registered the Metadata API end point URL with your orgs Remote Site settings.
June 2, 2014 at 6:09 am
Hi Andrew ! I’m stuck on with an issue . Your help will be greatly appreciated . In the Metadatabrowser page , when i click on Retrieve button , it does not show me the list and prompts me a “Selected Nodes” message . I know that its the JSON , but it shows me the empty list .
Any pointers on this ?
Please help me. Stuck from last 3 days.
June 2, 2014 at 12:06 pm
Have you added a Remote Site setting to your org for the Metadata API? If not, try running the Metadata Retrieve Demo and if it displays an error follow the instructions shown to add a Remote Site setting and then try again.
June 16, 2014 at 1:25 pm
I’ve raised a GitHub issue to improve the error handling on the MetadataBrowser page so that it will display the approprite error message if the Remote Site has not been setup. https://github.com/financialforcedev/apex-mdapi/issues/31
June 14, 2014 at 4:31 am
@ Andrew
After Changing the “Port Type” name attribute, I created a WSDL to APEX class(“MetadataServiceImported” class) out of that. However, when I tried to see the contents of the generated apex class the following message displayed. Same is happening with the “Metadataservice” class also. Any idea regarding this ?
Error:
An internal server error has occurred
An error has occurred while processing your request. The salesforce.com support team has been notified of the problem. If you believe you have additional information that may be of help in reproducing or correcting the error, please contact Salesforce Support. Please indicate the URL of the page you were requesting, any error id shown on this page as well as any other related information. We apologize for the inconvenience.
Thank you again for your patience and assistance. And thanks for using salesforce.com!
Error ID: 915341540-14795 (-1437854107)
June 14, 2014 at 11:44 am
Is there any reason why your creating your own? And not using the one in the repository I already created? Try that one and see if you get the same error.
June 17, 2014 at 5:24 am
Hi Andrew Fawcett,
I try to create a field using metadata api i got following error how to solve this error.
System.CalloutException: IO Exception: Read timed out
Error is in expression ‘{!createField}’ in component in page createfields
Class.rajesh.MetadataService.MetadataPort.readMetadata: line 8513, column 1
Class.rajesh.MetadataController11.UpdateFieldLevelSecurity: line 844, column 1
Class.rajesh.MetadataController11.createField: line 298, column 1
please help me……………………
June 17, 2014 at 7:32 am
This is Salesforce error and time out we cannot change as apex developers. Can you try sending less information at a time?
June 18, 2014 at 11:05 am
Hi Andrew Fawcett,
I displayed custom objects and related custom fields(Here object will come dynamically through URL)with delete command link and edit command link using schema methods and wrapper class .My requirement is When i click delete command link this command link related custom object and custom field how to delete from database?this is my last requirement related to project
please help me…………
July 5, 2014 at 3:44 am
Hi Sir,
How to create User Using metadata api?
July 5, 2014 at 8:29 am
You don’t need the metadata API for this, you can use DML in apex or partner API in other languages to inset a record into the User object.
July 5, 2014 at 9:38 am
Hi Andrew Fawcett,
Using schema methods and wrapper class I displayed all custom objects with in the pageblocktable one column.another column name is created dates(column name).How to retrieve all custom objects related created dates(column name) with in the pageblock table column(means created Dates column)?
Note:i posted lot of blogs no reply
help me………
July 5, 2014 at 9:42 am
I try to respond to all your questions on GitHub, I gave you some sample code the other day for adding fields to layouts did you see it?
Please raise this question on GitHub and I will try to help also. Please also keep in mind I provide support mostly in my personal time.
Please raise this on GitHub I have some ideas I can share with you there.
July 16, 2014 at 6:05 am
Hi Andrew Fawcett,
How to create Multi select field using Metadata api?
July 16, 2014 at 9:03 am
Have you tried reviewing the Metadata API guide and the sample Apex code, there is one for a Picklist field already, maybe you can try to adapt? https://github.com/financialforcedev/apex-mdapi/blob/master/apex-mdapi/src/classes/MetadataServiceExamples.cls#L252. If you are still struggling raise a GitHub issue and I’ll try to help there.
July 18, 2014 at 5:01 am
Hi Andrew Fawcett,
Using metadata api I created Multipicklist from visualforce page.I want to How to display Dynamically comming multipicklist values as checkboxes.
July 18, 2014 at 7:20 am
Try this, https://developer.salesforce.com/blogs/developer-relations/2008/12/using-the-metadata-api-to-retrieve-picklist-values.html
July 18, 2014 at 9:05 am
Hi Andrew Fawcett,
Every time i will create Multselectipicklist Fields and other fields Using metadata api From visualforce page.I displayed all fields dynamically (without using fieldset)in an visualforce page, on that time i want how to display all multiselectpicklists as checkboxes Dynmically?
Note:Not single multiselectpicklist field I want to Every time comming multiselectpicklist field as checkboxes
I displayed all comming multipicklists as checkboxes but problem is Created multiselect picklists also visible and multiselectpicklist related vales as checkboxes also visible .
My code i pasted on Github issue .
——————————————–
please help me…………
July 18, 2014 at 9:51 am
Thanks for raising on the GitHub Metadata API repository, please continue to do this.
However…
– You don’t need to also post such specific questions here in future.
– Please do not post requests for help and questions that very specific this on my blog in future.
– Please use GitHub or StackExchange for such issues, it is much much easier to talk and share code. 🙂
Thank you.
July 31, 2014 at 10:26 am
Hi Andrew Fawcett,
Using metadata api how to update created field datatype dynamically?
July 31, 2014 at 10:47 am
I don’t understand your question sorry. Have you taken a look at the examples in MetadataServiceExamples.cls in the GitHub repository?
August 7, 2014 at 9:57 am
Hi Andrew Fawcett,
Using visualforce page how to schedule a batch Dynamically.Like below link’
https://www.cloudclickware.com/jobbook/
please help me…….
Pingback: Apex Metadata API Streamlined and Simplified for Summer’14 | Andy in the Cloud
September 9, 2014 at 6:18 pm
Hi Andrew Fawcett,
Is there any way we can create Apex class using Metadata API?
We have a requirement of converting code from a different language to apex. Our last hurdle is that we are not able to save the result as an Apex Class. We used the API to create Pages which has the create() call enabled but with Apex Class no luck.
Please kindly suggest some method.
Best Regards,
Sumedha
September 10, 2014 at 6:51 am
For ApexClass and ApexTrigger metadata types, the CRUD methods are not supported by Salesforce. You have to use the deploy operation, take a look at the Deploy Demo in the GitHub repository. Its not ideal. There is another API called the Tooling API that will also do this, without having to use a zip component on the client! Take a look at this repository and the demo class file within it for an example, https://github.com/afawcett/apex-toolingapi
September 12, 2014 at 9:42 pm
Did anyone try creating FieldSets? I can add fields in FieldSet as availableFields but not as displayedFields ? Please see simple code below. Developer console throws an error for fieldSet.displayedFields.add(myAvailableField);
Error Message: Metadata API Exception Attempt to de-reference a null object
//FieldSet Field
fieldSet.availableFields = new List();
MetadataService.FieldSetItem myAvailableField = new MetadataService.FieldSetItem();
//Field 1
myAvailableField.field = ‘UnitPrice’;
fieldSet.availableFields.add(myAvailableField);
//Field 2
myAvailableField = new MetadataService.FieldSetItem();
myAvailableField.field = ‘Product2.LastModifiedDate’;
fieldSet.displayedFields.add(myAvailableField);
September 13, 2014 at 1:03 pm
I cannot check this on detail at present, but wanted to check you had seen the examples in MetadataServiceExamples.cls? I think there is an example of this in that?
October 21, 2014 at 11:08 am
why is there a compile error “Compile Error: Invalid type: MetadataService.AsyncResult ” while saving the following code
public with sharing class MetadataController {
public MetadataService.AsyncResult result {get;set;}
.
.
.
.
.
.
}
in apex class.
October 22, 2014 at 8:05 pm
I don’t see why, have you used the MetadataService class from the repo? Can you maybe post a more complex code sample via a GitHub Issue if so.
October 23, 2014 at 5:47 am
how should i use the MetadataService class from the repo. can you guide step by step.
My requirement is to fetch all the validation rules for a custom object using a apex class.
October 24, 2014 at 5:18 pm
You can click the Deploy to Salesforce button to deploy the code to your org, or Clone the repo to your local machine, or download the MetadataService.cls and MetadataServiceTest.cls individually and copy paste into your developer org.
November 4, 2014 at 3:36 pm
Hi Andrew,
I am working with metadata service class and try to get list views. I need to setup a remote site to get it working otherwise there is an error “unauthorized endpoint…… ” .
Is there any way to set endpoint dynamically as I may need to package my solution and install in another org. This is “https://c.ap1.visual.force.com” in my org but could be different in other orgs.
Is there any way to make it dynamic ?
Thanks in advance!
November 5, 2014 at 12:40 pm
Sure check this blog, https://andyinthecloud.com/2014/07/29/post-install-apex-metadata-api-configuration-solved/
November 7, 2014 at 2:27 pm
Thanks for this Andrew!
Another issue I have anyone might help. I am retrieving list view filters , I can get list view filters for opportunity closing this month in one org (My Dev org) but when try the same code in another org (this org have namespace enabled for managed package . Does this makes any difference ?) it is returning null (no filters at all ), tried with different list views but can not succeed.
Do you have any Idea about what might be the issue? I am stuck in this
Thanks
November 7, 2014 at 3:45 pm
Here is my class code,
public with sharing class GetListViews{
private static MetadataService.MetadataPort createService()
{
MetadataService.MetadataPort service = new MetadataService.MetadataPort();
service.SessionHeader = new MetadataService.SessionHeader_element();
service.SessionHeader.sessionId = UserInfo.getSessionId();
return service;
}
public void readListView()
{
MetadataService.MetadataPort service = createService();
MetadataService.ListView listView =
(MetadataService.ListView) service.readMetadata(‘ListView’,
new String[] { ‘Opportunity.ClosingThisMonth’ }).getRecords()[0];
if(listView.filters!=null)
for(MetadataService.ListViewFilter filter : listView.filters)
{
System.debug(‘Filter ‘ + filter.field + ‘ ‘ + filter.operation + ‘ ‘ + filter.value);
}
}
}
I am stuck in this issue . Any kind of help is appreciated .
Thanks in advance!
November 8, 2014 at 5:46 pm
Hi Andrew,
Thanks for the great post. I am able to create fields but I am unable to update Field Level Security Permissions for these fields against any profile. When I execute the updateFieldLevelSecurity() method I get a CalloutException. When I checked the fullname on the MetadataService.Profile is not getting set.
Am I missing something? Please Help.
Thanks in Advance!!
November 10, 2014 at 9:17 am
Thanks your welcome! Can you please raise an issue in GitHub and include some more code samples and the exact error your getting and i’ll take a look for you. https://github.com/financialforcedev/apex-mdapi/issues
November 14, 2014 at 5:41 am
Hi Andrew ,
Just confirmed that orgs with namespace (for managed packages) are not returning list view filters . Tested with one of my Dev org and this returns list view filters. Then added a namespace (setup -> create -> package -> edit -> add namespace) , after that list view filters are returning null.
So, does this means that metadata api stops working with this kind of orgs? Or is there some other way to call metadata for such orgs ? or is there some kind of issue/ bug ?
Please let me know. This is kind of urgent thing, if you can help with just telling me if this is possible or not.
Thanks !
November 14, 2014 at 6:13 am
Got a solution my self . Pasting so could help someone in the same problem 🙂
adding namespace prefix with two underscores solves my problem.
List view name : Opportunity.NAMESPACE__ClosingThisMonth
Example :
public void readListView()
{
MetadataService.MetadataPort service = createService();
/* Instead of this ..
MetadataService.ListView listView =
(MetadataService.ListView) service.readMetadata(‘ListView’,
new String[] { ‘Opportunity.ClosingThisMonth’ }).getRecords()[0];*/
//I use this
MetadataService.ListView listView =
(MetadataService.ListView) service.readMetadata(‘ListView’,
new String[] { ‘Opportunity.NAMESPACE__ClosingThisMonth’ }).getRecords()[0];
if(listView.filters!=null)
for(MetadataService.ListViewFilter filter : listView.filters)
{
System.debug(‘Filter ‘ + filter.field + ‘ ‘ + filter.operation + ‘ ‘ + filter.value);
}
}
}
Best Regards!
November 14, 2014 at 4:52 pm
Awesome, namespaces can make naming things in the metadata api very tricky, I often use the Developer Workbench to list them to get the correct names. Sorry i didn’t get to this sooner to suggest this. Though as they say, there is only one good way to learn! 🙂 Glad you sorted it and enjoy the API!
Pingback: Apex Metadata API Q&A | Andy in the Cloud
January 6, 2015 at 8:58 am
Andy,
Do you have a example to ‘retrieve’ the package from one environment and deploy the same in some other environment using the metadata api?
January 6, 2015 at 9:22 pm
Sorry i don’t. Add it as an issue on the GitHub repo and i’ll see if i can get one created in due course.
February 12, 2015 at 7:06 am
Hi Sir,
Is it possible salesforce integration with xero?
February 12, 2015 at 8:05 am
Zero has Api’s and Apex can make web call outs, so I would say yes. Though I have no experience in this. Try using Google and Salesforce StackExchange perhaps.
February 18, 2015 at 3:30 pm
Hi Andrew
I’m trying to create an ApexClass using the tooling api by forking the following project:
https://github.com/afawcett/apex-toolingapi/issues
When I have the ApexClass list, I’m casting it into sObject_x list and then adding it as a parameter in create() webservice call. It always gives me “INVALID_TYPE” exception. Please could you help?
Thanks in advance.
February 18, 2015 at 3:35 pm
To provide more info:
Intially, I tried something like:
List sObjects = new List();
sObjects = testClassList;
ToolingAPIWSDL.SaveResult[] saveResult = toolingAPI.create(sObjects);
And, then I tried the following, but both of them gave the same exception – Invalid_Type.
List sObjects = new List();
for(ToolingAPIWSDL.ApexClass newTestClass : testClassList) {
ToolingAPIWSDL.sObject_x newSObject = new ToolingAPIWSDL.sObject_x();
String[] tempFieldstoNull = new String[]{};
newSObject.fieldsToNull = tempFieldstoNull; //null
newSObject.Id = null;
newSObject.ApexClass = newTestClass;
sObjects.add(newSObject);
}
ToolingAPIWSDL.SaveResult[] saveResult = toolingAPI.create(sObjects);
February 24, 2015 at 9:17 pm
Take a look at the rest branch in the repo this should work
Pingback: Metadata API and SOAP using Google REST Console and Anonymous Apex | jdope
March 13, 2015 at 4:42 am
Hi Andrew,
Thank you very much for this useful post. I managed to create new custom labels with translations, but was unable to read them back using readMetadata method. Will it be possible to read and update an existing custom label and its translation using MetadataService?
Many Thanks
Imalka
March 13, 2015 at 7:48 am
Your very welcome thank you! This should be possible, can you raise a request for an example on the github issue list and I will take a look
March 13, 2015 at 10:31 am
Thank you very much for your prompt reply. I will raise an issue on github.
Really appreciating your help.
Many Thanks,
Imalka
March 13, 2015 at 10:30 am
Thank you very much for your prompt reply. I will raise an issue on github.
Really appreciating your help.
Many Thanks,
Imalka
March 21, 2015 at 10:05 pm
Andy,
This looks fantastic. It opens up a whole lot of functionality. Thank you. I’m glad it is a passion of yours.
I have recently taken over a Salesforce installation that has had far too many (inexperienced) hands on it. There are different naming conventions for fields and many fields that are just left abandoned. What I’d really like to do is change the API name of some of the fields so that they actually relate to their use and function.
I’d like to use your Metadata classes to create a method that changes the API name of a field. I imagine that it would need to create a field under the new name but same field type, copy all the data from the old into the new, replace all references to the old with the new and then remove the old field.
I then thought ‘I can’t be the only one who has this bug bear’. Are you aware of anyone who has such a piece of code?
Thanks, Greg.
March 22, 2015 at 9:44 am
Thanks Greg, really appreciate the words. As regards your use case, it is indeed a frustrating one, and costs time as developers have to map what they see on the screen to less obvious API names. So yes aware of the problem, though not of anyone as yet who has attempt to overcome it with this API.
You can use the Metadata API to automate a lot of what can be done under the Setup menu for sure. However it will not bypass any of the integrity constraints Salesforce enforce. So if you cannot rename or delete a field in the UI, due to references, the Metadata API will also reflect the same. As regards developing a tool to help do this, i’d say its certainly got its challanges, but i’m not a none-starter. Some initial challange will be in finding the dependencies to unhook before the field can be renamed/deleted. There is no API for this, though maybe you can parse the error message returned when attempting to do so?
Thanks for your support and good luck if you attempt this mission! 🙂