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.
Pingback: Failing to update picklist on task object via metadata API | DL-UAT
April 25, 2015 at 6:56 am
Hello Guys,
I try to access read the PageLayout using the latest Metadata API i.e 33, I can read all other metadata excepting Layouts.
Has any face the same problem.
Thanks!
April 25, 2015 at 9:51 am
Can you raise an issue on the GitHub repo and I will take a look. Thanks
June 9, 2015 at 6:53 am
Andy, that’s indeed brilliant. Awesome stuff. However, I do have one question. Actually, the requirement is to sync two Objects in Salesforce, not only the data but the Metadata too. Let’s say there are two Objects, A and B. If a field is added or deleted in A, the same should reflect in B. Could you please suggest in detail how can I use your metadata api to achieve such functionality???
June 9, 2015 at 9:04 pm
Thanks! There are a lot of examples for this in the metadataserviceexamples class you can use as a basis, then also look at my sobjectdataloader repo for the data side. I am afraid I just don’t have time to go into detail in this with you, sorry. Happy if you want to ask some hi level questions after having a look at the above.
September 9, 2015 at 11:35 am
Hi Andy,
This is very great and helpful API and I’ve a query about retrieving layout metadata. Is there a way I can retrieve all the fields on a specific layout provided the recordType?
–
Bilal
September 9, 2015 at 6:14 pm
Fields are not filtered by record type, rather a layout is assigned by recorded type
September 13, 2015 at 11:10 am
Right, I meant retrieving layout metadata as I can get fields from it.
So my question is, can I retrieve metadata :
– layout provided recordtype
– Page layout assignments
—
Bilal
September 14, 2015 at 12:10 am
This should be available on the permission set or profile
October 4, 2015 at 10:04 am
Hi Andy,
I was stuck on a point I have a code which is working fine for retrieve the folder info of an org.
MetadataService.MetadataPort service = new MetadataService.MetadataPort();
List queries = new List();
service.SessionHeader = new MetadataService.SessionHeader_element();
service.SessionHeader.sessionId = UserInfo.getSessionId();
MetadataService.ListMetadataQuery queryDocumentFolder = new MetadataService.ListMetadataQuery();
queryDocumentFolder.type_x = ‘DocumentFolder’;
queries.add(queryDocumentFolder);
system.debug(‘========queries=====’+queries);
MetadataService.FileProperties[] fileProperties = service.listMetadata(queries, 30);
system.debug(‘========fileProperties=====’+fileProperties);
List folderNames = new List();
for(MetadataService.FileProperties fileProperty : fileProperties)
folderNames.add(fileProperty.fileName.replace(‘documents/’, ”));
system.debug(‘========queries=====’+queries);
Now I want to make it generic so in the line “queryDocumentFolder.type_x = ‘DocumentFolder’;” instead of DocumentFolder if I pass “ApexClass” it should return all the Apex class of that org same for other component.
Currently when I am trying to do this I am facing the exception can you please help me to come out of it?
Thanks in Advance.
Abhishek
October 4, 2015 at 10:53 am
Sorry hard to help if you have to given me the exception your getting. However you may also like to know you can use Soql to query apex classes, just query the ApexClass object. Hope this help, if you want to proceed with Metadata API you will have to give me some more insight as to the exception message you have?
October 5, 2015 at 7:15 pm
The “create” method does not exist in the MetadataService.cls. Could you please update your example, or provide another example on how to use the Metadata API to create objects such as Workflow Rules using async methods?
October 7, 2015 at 4:35 pm
Salesforce have changed this, take a look at MetadaraServiceExamples.cls for latest
December 9, 2015 at 8:52 pm
Thanks for posting this discussion. I would like to know How to get field Description from object metadata getDescription() ?? Thank you.
December 9, 2015 at 9:09 pm
You can do this natively without going via the Metadada API, look for Apex Describe in the Apex Developer Guide.
December 28, 2015 at 1:12 pm
Hi guys
I need to pull lastModifiedDate for RecordType.
Is this not supported by MetadataService?
Workbench shows this information for every record type, also official Metadata API listing all the record types shows this.
Any suggestions or do I need to make official Metadata API call and pull huge amout of RTs?
December 28, 2015 at 4:24 pm
It should be a direct wrapper. Can you raise an issue on the GitHub site with a screenshot of what your seeing elsewhere in the docs and I will try to find it for you. Thanks.
December 29, 2015 at 6:49 am
I may have not described the case at hand clearly enough 🙂
So, I’m pulling Record Types using readMetadata(‘RecordType’, ), but there is no info on lastModifiedDate.
LastModifiedDate is included in listMetada(, ), which I guess is used by workbench and is indeed available in MetadataService, but this pulls huge amount of data in my org (over 600 RTs).
I was wondering if there is a way to pull lastModifiedDate for RTs without querying all of them.
Probably not, as my understanding is that lastModifiedDate is a property of a file holding metadata, not of specific metadata as such.
Background requirement is to get picklist values for different record types, but in a web service for mobile devices, so time is of the essence. My approach for this is to create some “cache” with the values (sObject), that would be refreshed only if anything changed, thus I need lastModifiedDate for included RTs 🙂
So my final question is: is there a way to pull lastModifiedDate for RTs without querying all of them?
December 29, 2015 at 11:08 am
Not via metadada API, have you looked at tooling API?
December 29, 2015 at 1:32 pm
Nope, not yet. I will look into it, but I’m resistant to including yet another API 🙂
January 14, 2016 at 10:37 am
Hi Andrew,
Thank you for such a fantastic class.
I just wanted to know like if there is a way to reduce the size of the MetadataService class as it itself consumes around 500000 characters and we all know Salesforce is picky about the character limit.
Any suggestion would be helpful.
Warm Regards
January 14, 2016 at 2:33 pm
This has been raised as an idea in the GitHub issues list for sure. And is a good one. Checkout some thoughts on there. Other than simply copy and pasting over what you need until it all compiles, I don’t yet have a tool or automated approach to cutting it down. Sorry.
February 4, 2016 at 5:22 am
Hi Andrew,
I tried to invoke the Metadata api from the Salesforce site but it is throwing an error like “Web service callout failed: WebService returned a SOAP Fault: UNKNOWN_EXCEPTION: Site under construction faultcode=UNKNOWN_EXCEPTION faultactor=” in the debug logs, Can you please help me to fix this
February 4, 2016 at 3:45 pm
Check the code that initialises the end point in the metadataservice class, it might need adjusting for this execution context. Also if your running as guest this will not work as that user does not have API or admin access. For that matter I suspect your general issue is site users won’t either. What you may have to consider is a more advanced approach of using a precaptured oauth token from an admin user in the org and having the code pass that as the session id. However you need to be very very careful with this kind of elevation of privileges. What is it your doing with the metadata API, there are some things now that can be done via soql….
February 10, 2016 at 11:33 am
hii…..
How to create tab using metadata API.
February 10, 2016 at 8:29 pm
Take a look at the examples class in the repo, if there is not one, you can probably figure it out from createObject example and by reviewing advice in my FAQ blog, linked from the readme file.
February 10, 2016 at 5:36 pm
Hi Andrew,
I am recently getting readtimeout exception while fetching metadata for profiles. It occurs sometime not always, Could you please help me to fix this?.
February 10, 2016 at 8:27 pm
There is a timeout property on the header where you set the session Id, try increasing this
April 5, 2016 at 1:49 pm
Couldn’t find that property, although i added a line service.timeout_x = 90000; but didn’t work out, am i missing somehting
April 5, 2016 at 5:42 pm
I think that’s the one, might be best to post your current issue on the GitHub repository as an issue, me and or others in FF or the community can view it more easily
April 5, 2016 at 1:53 pm
ahh never mind, it worked like a charm, you are a rockstar as always ^_^
April 5, 2016 at 5:42 pm
Ah wonderful!
February 12, 2016 at 6:21 pm
I have updated timeout property. Thank you very much.
April 6, 2016 at 5:46 am
Hi all i am trying to automate post sandbox refresh activities and to achieve this i need to update email alerts, custom labels, outbound messages as at present we need to do manually one by one as these all are metadata so we can’t do by normal apex. so can you share any sugestions how it can be acheived. as in above examples i have seen creating fields and other stuffs.
April 6, 2016 at 7:34 am
Have you reviewed the sample code in MetadadaServiceExamples.cls?
April 19, 2016 at 5:33 pm
Did you already try to create connected app through metadataapi?
April 19, 2016 at 7:28 pm
Yes there is a sample for this in metadataserviceexamples.cls
April 20, 2016 at 9:36 am
Hello Andrew, I just found an example of readConnectedapp , not a createConnectedApp, could you please provide us with a sample code for this?
Thanks
April 21, 2016 at 11:35 am
Yes sorry, i thought there was a create. I’m busy at the moment, but if you read this blog (https://andyinthecloud.com/2014/11/22/apex-metadata-api-qa/, see point 2) and reference the XML example shown in the docs here (https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_connectedapp.htm) you should be able to figure out how to create your own create example by following my blog instructions. If your struggling i can take a look when i have more time. Thanks.
April 19, 2016 at 7:28 pm
Did you know btw you don’t need to replicate a connected app in every org
April 20, 2016 at 8:55 am
Hello ,
How can i acheive this please? I need to ensure a multi org connection that’s why i’d like to create a connected app automatically .
Many thanks for your help
April 20, 2016 at 8:58 am
Hello Andrew ,
I tried with this MetadataService.AsyncResult[] results =
service.create(new List { customField }); but gives me this errorMethod does not exist or incorrect signature: [MetadataService.MetadataPort].create(List). It seems that the create method doesn’t exist in MetadataService class that i just uploaded in my org. Big Thanks
May 11, 2016 at 9:29 pm
Hi All,
This was a great discussion.
I’m looking to retrieve all the metadata components (Objects, Fields, Pagelayouts, Validation Rules, Workflow Rules, Approval Processes) based on Last Modified Date by using Query or any kind of script.
If anyone have any workaround for this.
Thanks,
Anil
May 14, 2016 at 1:50 am
You might want to look at the Tooling API where you can do soql over some of these metadata type objects, then come back to metadata API and build a package xml file driven by the results and passed to the retrieve API. Or you can stick with tooling API and retrieve what you need, though it only currently has a subset of Metadada types.
May 30, 2016 at 12:40 pm
Hi Andrew Fawcett,
how can we enable account teams and chatter and lightning design system through apex code can you help me please.
May 30, 2016 at 4:55 pm
Yes, this should be possible, look at this sample, https://github.com/financialforcedev/apex-mdapi/blob/master/apex-mdapi/src/classes/MetadataServiceExamples.cls#L497, basically adapt it use the AccountSettings metadata component https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_accountsettings.htm?search_text=AccountSettings and the for the Chatter settings, there are various ones scattered throughout the API, search for ‘Chatter’ in the code here, https://raw.githubusercontent.com/financialforcedev/apex-mdapi/master/apex-mdapi/src/classes/MetadataService.cls. Note you may have to make more than one MD API call.
May 31, 2016 at 10:28 am
Thank You for Sharing Andrew Fawcett.
But here i have to enable those things in other org so where can i provide user name and password,url?
and also i created on package.xml file how can i deploy to org without using any tool like(eclipse,ant,migration,change set), is there any way to deploy through apex code or any back end process. here you provided MetadataServiceExamples class but am unable to find where you are passing username and password,here you are using MetadataDeployController how can you use ZipData prperty,getPackageXml method i tried to use same code in my org but it is not working.
if you know anything can you provide that information.
May 31, 2016 at 5:48 pm
The samples work only in the org they are installed in. They use UserInfo.getSessionId. If you want to do the same from one org to anther, you have to obtain an oAuth token (recommended) or login as another user via the Logon API and use the resulting session is (not recommend as you would have to store the target orgs user and password).
February 28, 2017 at 4:01 am
Hi Andrew,
I tried replacing UserInfo.getSessionId from access token on another Org. But I’m getting multiple exceptions. But I’m getting INVALID_SESSION_ID exception. Then I tried updating the code as follows.
MetadataService.MetadataPort service = new MetadataService.MetadataPort(); service.SessionHeader = new MetadataService.SessionHeader_element();
//service.SessionHeader.sessionId = UserInfo.getSessionId();
service.SessionHeader.sessionId = clientAccessToken;
//I tried setting following as the endpoint_x. But was getting separate error
//service.endpoint_x = ‘https://login.salesforce.com’;
But then I was getting some other exception of “System.CalloutException: Web service callout failed: Unexpected element. Parser was expecting element ‘http://schemas.xmlsoap.org/soap/envelope/:Envelope’ but found ‘http://www.w3.org/1999/xhtml:html'”
Appriciate if you can help to figure this out. API versions of my source and target orgs are different as well. Source has 38 while target has 39. Not sure whether this might be the reason.
Thanks a lot for your support in advanced
February 28, 2017 at 8:00 am
If looks like your trying to use the client access token? Just checking here, you are using an oAuth token from the user/password or web flow from oAuth? You cannot use the static client or secret id given when you setup the connected app. If you are using it. It looks like there is some unmarshalling issue. Run your code from dev console and review the debug log. You will be able to see the actual xml response returned this way. Hope this helps.
February 28, 2017 at 10:08 am
Many Thanks for the response! I’m using username/password to get access token. The issue i had was with remote site settings. It was pointed to the current instance. So i have changed it to target instance and it worked fine. Moving forward I need to use your solution in my source org as well as the target org. So what’s your suggestion on that?
March 4, 2017 at 2:06 pm
For the source org you can use the running users session id so long as they have access to the API. If not you can have the admin go through the flow and store the oAuth token to be used by the code running on behalf of other users. Obviously use this approach with care
June 29, 2016 at 9:21 pm
Andrew,
Need some help. I am trying to get details on profiles in my org using the API.
I am getting a time out on getting any profile back from the API using this code
(MetadataService.Profile) service.readMetadata(‘Profile’,new String[] { profn }).getRecords()[0]
This call always times out, even if I turn up the timeout_x to 90000 so that it exceeds 120 seconds max on call outs and then errors out.
Note in the above call out the variable profn is equal to a Profle Name that I got back from a call to service.listMetadata(quer, vers) where the type=’Profile’.
July 7, 2016 at 9:05 pm
Yes these can be big, you may have to use the retrieve API, see demo in the repo. Or if it’s just reading your can use direct soql on the permission set objects. These contain details of permissions for profiles as well.
July 7, 2016 at 9:28 pm
Ah SOQL, I can use that. Do you know where the fields are defined that I can use on SOQL Select from premission set objects in the database. The names of the object(s) too?
July 8, 2016 at 9:26 pm
Here you go. https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_objects_permissionset.htm
July 5, 2016 at 5:25 pm
Hi Andrew,
Thanks for sharing such a great API.
I am trying to deploy selected metadata to my destination org.
My question is how can i create a zip out of the selected components to pass it as a parameter to deploy() call.
Please suggest and thanks in advance.
July 7, 2016 at 9:07 pm
Take a look at the retrieve API demo controller in the repo
August 4, 2016 at 9:53 am
Hi, I am new to Sales Force so this might be a stupid question, but how can I import the .cls file into my org so that all those classes are available?
August 4, 2016 at 12:24 pm
No worries. You can use many tools, if your familiar with command line, Google search for Salesforce Migration Toolkit
August 4, 2016 at 12:25 pm
You can click the Deploy to Salesforce button on the readme file. This is not a Salesforce tool though. Others are Force.com IDE and also Developer Console
August 5, 2016 at 2:29 pm
How would I import using the Developer Console? Again, many thanks, I am a bit new to this.
August 5, 2016 at 2:41 pm
It depends if you only want a few classes, the key ones are metadataservice.cls and metadataservicetest.cls, then copy and paste is best. If it’s the whole repo it’s the Deploy to Salesforce button, or if you prefer to download the repo try the Salesforce Migration Toolkit I mentioned.
August 5, 2016 at 3:59 pm
Got it, that clarified everything! Thank you very much for your help and this awesome wrapper!
August 5, 2016 at 2:08 pm
Hi, this may be a dumb question, but I am quite new to Salesforce. Could you please tell me how to use the .cls file? Do I have to import it into my SF org somehow? How do I make the classes contained in it available to my apex code in my org?
August 5, 2016 at 2:08 pm
Oh sorry…my browser was acting up and I did not see you answer… I will read..sorry!
August 10, 2016 at 2:10 pm
Hi i am new in Salesforce i am try to delete the apex class in visual force page .. i am getting error, Compile Error: DML not allowed on ApexClass at line . please give any idea to delete the apex class in production using VF page
August 12, 2016 at 9:39 am
Sorry deleting apex code programmatically in apex directly via do is not supported by the platform.
You could do this via the Metadada API, this is a WebService api so a http callout is needed. Also you need to use the deploy API operation. If you checkout my flow factory or declarative rollup summary tool in GitHub you can see examples. Also the apex Metadata API wrapper GitHub repo has a deploy example controller you can see.
August 15, 2016 at 11:24 pm
Hi Andrew,
I want to update description of bulk custom fields (500 fields) at a time across multiple objects. Do we have any approach to do this?. Could you please provide me sample code for this.
I know how to fetch the metadata details of custom fields by using Tooling API. From that, I could not able to do an update operation.
Please check and let us know best approach to do this.
Thanks,
Anil
August 16, 2016 at 2:45 pm
My best bet would be to use the Metadada API deploy operation, you can see an example in the deploy controller class and also by studying the deploy api documentation. You would need to include in the package sent to the deploy method .object files with only fields in and description, should should be enough, you can test this via a the ant tool perhaps before wasting time on coding. See the Salesforce migration toolkit for ant usage. Sorry I don’t have a ready made example for you.
August 26, 2016 at 11:20 am
can we get all test classes and test methods from one particular project using tooling api then how ?
August 26, 2016 at 3:55 pm
There is no concept of a project in the tooling API only the idea of workspace you set up by making other API calls. I have not kept up with tooling API calls, but it is possible there is an API to get this information since many ide’s provide this ui to select them. If not you will have to parse the apex code classes yourself for this. I just spent a few moments looking at the tooling API docs and didn’t see anything. You might find an answer by looking at code from IDE like MavensMate perhaps, i believe it’s code is open source? Good luck!
October 20, 2016 at 8:29 pm
Andrew,
Really appreciate that you have taken the time to build this and are continuing to support it. I’m looking at using your wrapper in an isv package to dynamically add Salesforce’s url.
Do you happen to know if this would pass the security review process?
Thanks,
Brian
October 21, 2016 at 5:11 pm
If you use the component to prompt the admin to approve the connection with the Metadada API they appear to be accepting it. I have seen in the chatter group for the security review team them reference my post outlining this approach to setting up the remote site which allows the comms to work. I assume this is what you mean by “add Salesforce’s url” ?
October 31, 2016 at 9:54 pm
Hi Andrew Fawcett,
Are we able to modify the metadata for a custom permission set, for example I am able to create a permission set but I would also like to enable a few check boxes under the System Permission. Like ViewEncryptedData. I would like to do this all with apex code but I couldn’t find anything, I noticed that the metadata has access to it.
ConfigSettings
false
EnabledChatter
false
Permission Set that allows the user to Manage Encryption Keys & View Encrypted Data
Manage Encryption
Salesforce
true
ManageEncryptionKeys
true
ViewEncryptedData
November 3, 2016 at 12:59 am
This is not exposed as far as i can tell.
December 1, 2016 at 12:23 pm
Hi Andrew.
I am battling to get a test mock class implemented for service.readMetadata on a page layout. The actual class is working fine, it is just the mock test that keeps failing with a System.NullPointerException: Attempt to de-reference a null object at the point where I do the readMetadata call. Any help would be greatly appreciated.
Thanks
Rudolf.
December 6, 2016 at 2:12 am
Have a look at this from some code that I wrote, if should give you the general idea, if not let me know via a GitHub issue your test code and I will take a look. https://github.com/afawcett/declarative-lookup-rollup-summaries/blob/master/rolluptool/src/classes/RollupControllerTest.cls
December 17, 2016 at 5:58 pm
how to get list of sharing setting and sharing rules in salesforce using metadata api?
December 22, 2016 at 12:19 am
Have you tried the listMetadata API?
December 22, 2016 at 10:56 am
no ..i am not much aware about that ..How can I use it ,Can you give any idea?
December 23, 2016 at 4:59 pm
Sure, take a look at the sample code in MetadataServiceExamples.cls and this help topic https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_listmetadata.htm
February 22, 2017 at 2:30 am
Hi, below code is giving me an exception, any idea? i have the remote site settings and i am on Spring17 release.
Exception:
System.CalloutException: Web service callout failed: WebService returned a SOAP Fault: Must specify a {http://www.w3.org/2001/XMLSchema-instance}type attribute value for the {http://soap.sforce.com/2006/04/metadata}metadata element faultcode=soapenv:Client faultactor=
Code:
system.debug(‘printing ‘+URL.getSalesforceBaseUrl());
MetadataService.MetadataPort service = new MetadataService.MetadataPort();
service.SessionHeader = new MetadataService.SessionHeader_element();
service.SessionHeader.sessionId = UserInfo.getSessionId();
MetadataService.DuplicateRule customObject = new MetadataService.DuplicateRule();
customObject.actionOnInsert=’allow’;
customObject.actionOnUpdate=’allow’;
customObject.isActive=true;
customObject.masterLabel=’Main Label’;
customObject.sortOrder=3;
customObject.securityOption=’EnforceSharingRules’;
string [] insertOperations = new string[]{‘Report’};
string [] updateOperations = new string[]{‘Report’};
MetadataService.SaveResult[] sr = service.createMetadata(new MetadataService.Metadata[]{customObject});
February 22, 2017 at 4:38 am
This could be a bug witb the wrapper code not supporting the more complex sharing metadata types. Can you post an issue on the GitHub repo please
February 27, 2017 at 4:57 am
Hi Andrew,
This is impressive. I’m trying to access metadata (Apex classes,VF pages,components,WF rules etc…) on Org B from Org A. I have the access token (retrieved via oAuth) to Org B. Can you use the same methodology to access them?
February 28, 2017 at 7:56 am
Yes use the oAuth token in place of sessionid.
March 3, 2017 at 7:39 am
MetadataService.MetadataPort isn’t found in the MetadataService WSDL. I am actually using C# to set field permissions on a salesforce object.
March 4, 2017 at 2:04 pm
It is renamed in the Apex wrapper. See instructions at the bottom of the readme
March 25, 2017 at 4:42 pm
Hi Andrew Fawcett,
I am struggling to write an apex code to add new values to a standard picklist field, Can you please share some sample
March 27, 2017 at 3:43 am
Have you seen the code in the MetadataServiceExamples.cls file?
March 25, 2017 at 4:43 pm
Hi People,
Can anyone share apex code sample to add new values to a standard picklist field as a metadata service
April 11, 2017 at 2:05 pm
Could you please tell me if it is possible to install a managed package from app exchange also utilizing the metadata api?
April 13, 2017 at 3:25 pm
Yes, see the examples in the repo
May 15, 2017 at 6:20 pm
Hi Andrew,
Thank you for sharing info on the Metadata api calls from Apex. I am trying to build a SandboxPostCopy script so that it can automate some of the post refresh activities. One of the setting I am trying to update is the remote site settings endpoints.
After looking into some salesforce docs and online resources, I see that Metadata API is the only way to update RemoteSiteSettings. To access metadata api, I will need to configure the instance url as an endpoint which again requires accessing metadata api. Is there any other way to update the remote site settings through apex from the post copy script. Thanks in advance!
May 17, 2017 at 8:58 pm
This is a catch for sure. Do you find your sandbox is always spinning up on the same instance? If so add this to your production org remote sites. If not, you can deploy my domain to get a fixed URL.
June 21, 2017 at 4:41 pm
Hi Andrew,
I have come up with some custom Apex coding to Read Report Folder and displaying users list within custom Report Folder using your MetadataService class and would like to post it as a blog in my website http://www.ashishkeshari.com. Is it fine with you and I will refer to your blog as source for MetadataService class and keep the header declaration as intact – is that fine with you ?
July 8, 2017 at 12:45 am
Totally fine! Great job!
Pingback: Custom Reports Folder Sharing List using Metadata API – It's getting cloudy here…
Pingback: Salesforce MetaData API Usage – Create Custom Field | Sharon Maliakkal
July 21, 2017 at 2:35 pm
Hi Andrew, Iam able to fetch data from Metadata API by using both listmetadata and readmetadata method by passing the fullname of listmetadata to Read Metadata inorder to retrieve other labels.Now i created two webservice mocks for listmetadata and readmetadata .when i try to test the main class with the both the webservice classes,only one (either listmeta or readmeta)is able to cover and eventually test class is failing with a stacktrace error,
Any help is appreciable ,Thanks in advance
July 22, 2017 at 1:25 am
Take a look at the tests in my dlrs GitHub project for the rollup controller test this should get you going 👍🏻
July 26, 2017 at 2:33 pm
I am able to achieve the functionality now,Thanks a lot for the help Andy !
August 8, 2017 at 12:08 pm
Hi Andy, Would you Explain Why readmetadata method in Metadataport is not invoked in MetaData Service Test Class ?
August 9, 2017 at 4:58 am
It’s a complex API and mocking those responses didn’t seem valuable. Most of the code is system generated by salesforce as well. I appreciate this means not 100% coverage. If it’s an issue feel free to raise a GitHub request and we can look at it in the future. 👍🏻
August 11, 2017 at 7:30 pm
Hi Andy, I’m using your Metadata API to get a list of RemoteSites but the results do not include the actual URL only the names of the metadata fields and the ID of the Remote Site. Because this object is not exposed I cannot retrieve the fields from this ID using a SOQL query. For example, I have the Remote Site name as Google below but I cannot get the Remote Site URL which is “http://www.google.com.” Any suggestions?
These are the results
fileName=remoteSiteSettings/GoogleHome.remoteSite, fileName_type_info=(fileName, http://soap.sforce.com/2006/04/metadata, null, 1, 1, false), fullName=GoogleHome,
August 11, 2017 at 8:24 pm
I’ve added these lines into the public class FileProperties but no joy…
public String url;
private String[] url_type_info = new String[]{‘url’,’http://soap.sforce.com/2006/04/metadata’,null,’1′,’1′,’false’};
August 12, 2017 at 10:45 pm
The listMetadata response only lists some details of the Metadata types. To get all details you need to use readMetadata method
August 12, 2017 at 10:43 pm
You need to use the readMetadata method once you know the full Metadata name of the remote site, which the listMetadata API returns. You can see examples of both in the MetadaraServiceExamples.cls class. Btw have you considered using Named Credentials? Assuming your wanting to avoid hard coding a URL is your motivation that is?
August 22, 2017 at 7:47 pm
I see several different readMetadata methods (readMetadata_element , readMetadataResponse_element) which one do I use and now that I have the Remote Site name how do I invoke it?
August 24, 2017 at 1:00 am
There is only one readMetadata method, check out the examples in the metadataserviceexamples class
August 22, 2017 at 7:50 pm
Would this work?
MetadataService.RemoteSiteSetting rss = (MetadataService.RemoteSiteSetting) service.readMetadata(‘Google’, new String[] { ‘URL’ }).getRecords()[0];
August 25, 2017 at 11:32 am
Andrew, I’m not sure I understand your response. I was referring to your Metadata Service class which has about three different variations of readMetaData methods. In any case, I did use the right readMetaData method and was unable to pull back the URL for the RemoteSite. It simply reads as “null.” This may be one of those fields that Salesforce doesn’t want to make accessible. Thanks for your blog, I find it very informative and interesting.
August 26, 2017 at 9:30 pm
Yes maybe this is a security feature. Run your code via exec anonymous and review the debug log it will show the actual xml returned from Salesforce. This will help confirm.
August 26, 2017 at 9:31 pm
And thank you! 👍🏻
October 25, 2017 at 1:33 am
Hi Andrew,
Is there any way to get list of standard objects fields. I have tried using Partner API, But I am getting callout exception. Can you suggest on this?
October 27, 2017 at 3:33 pm
Yes you can use Apex and the Describe API. See the Apex Developers guide.
November 14, 2017 at 1:47 am
Hi Andrew,
I am facing an issue while in the xml response while solving a problem statement regarding security settings from metadata class stating that “Web service callout failed: Unable to parse callout response. Apex type not found for element referrerPolicy”, and before this i got a similar error with “Web service callout failed: Unable to parse callout response. Apex type not found for element hstsOnForcecomSites”,i solved this thing by adding this newly added metadata field in the class,but now i am unable to trace the error for the first error, is there any way to find that error?any help would be greatly appreciated and Thanks in advance
November 14, 2017 at 1:36 pm
Have you downloaded the latest file from GitHub? It seems these are fields that are not in the wsdl used to generate the class code. Sometimes this happens, your approach is one way to solve it. But if the missing info is complex it can be hard to guess. Please check the Apex Metadata api for your type and also check the wsdl. Did you by any chance change the version of Metadata api used by the class? If so this can also cause the issue, as the server will return xml not supported by the code since it was generated from an earlier version. If you need a new api version check for the latest in GitHub or follow the notes in the readme to generate a new one (though I think the latest was recently uploaded). Hope this helps. If not raise an issue on the GitHub sure and myself or another can try to help. Easier than chatting here. Andy
November 14, 2017 at 10:11 pm
Thanks for the valuable suggestions Andy!! I Solved The issue by adding addditional required code in my metadata class by debugging the response,now its working fine
November 19, 2017 at 8:19 am
Thanks! Would you mind sharing snippets of what you added on the GitHub repo please?
June 26, 2018 at 4:54 am
Hi Andy, I was writing a test class for some code that calls your wrapper to retrieve and modify picklist values. I was wondering if it’s possible to interrogate the request object’s parameters in the doInvoke method in the mock class? The use-case is that I would like to differentiate between a request for one picklist and another. Ideally I would also like to be able to see the metadatatype being requested, though this is optional at this point as so far my code just asks for ‘CustomField’ metadatatype . The type of request I am dealing with it readMetadata().
December 15, 2018 at 2:33 pm
Yep you should be able to cast the ‘request’ parameter passed into the doInvoke method to the request type and then assert on the contents of it. Have a read through the readMetadata method code to figure out the request type or do System.debug(String.valueOf(request));
January 22, 2019 at 3:02 am
Hi Andy, thanks for this tool.
I’ve written a SandboxPostCopy class and it was used yesterday for the first time when we refreshed our sandbox. I see this line in the debug logs so I don’t think it’s worked. It’s from WebServiceCallout.invoke() in MetadataService.listMetadata(); Is there something extra I have to do to use Metadata API with SandboxPostCopy?
CALLOUT_REQUEST|[9218]|listMetadata_element:[apex_schema_type_info=(http://soap.sforce.com/2006/04/metadata, true, false), asOfVersion=43.0, asOfVersion_type_info=(asOfVersion, http://soap.sforce.com/2006/04/metadata, null, 1, 1, false), field_order_type_info=(queries, asOfVersion), queries=(ListMetadataQuery:[apex_schema_type_info=(http://soap.sforce.com/2006/04/metadata, true, false), field_order_type_info=(folder, type_x), folder=null, folder_type_info=(folder, http://soap.sforce.com/2006/04/metadata, null, 0, 1, false), type_x=WorkflowOutboundMessage, type_x_type_info=(type, http://soap.sforce.com/2006/04/metadata, null, 1, 1, false)]), queries_type_info=(queries, http://soap.sforce.com/2006/04/metadata, null, 0, -1, false)]::SOAPAction=”” Accept=text/xml User-Agent=SFDC-Callout/44.0 SFDC_STACK_DEPTH=1 Content-Type=text/xml; charset=UTF-8
EXCEPTION_THROWN|[9218]|System.CalloutException: Web service callout failed: WebService returned a SOAP Fault: INVALID_SESSION_ID: Invalid Session ID found in SessionHeader: Illegal Session faultcode=sf:INVALID_SESSION_ID faultactor=
Thanks!
January 22, 2019 at 6:55 pm
This is by design the user context for sandbox copy classes does not expose a session id. This is a platform restriction. There was a discussion on twitter about this recently with no clear workaround other than having a vf page host the logic, which of course is not as automated as one would like.
January 23, 2019 at 1:24 am
Thanks for the quick response. Shame there’s no proper workaround but now I know I’ll manually work around it! Thanks!
October 16, 2019 at 4:53 am
Hi Andy, I am working on creating a script to create custom force.com site. Thanks to the examples in git, I can do that. However, I wanted to know if I can also create Force.com site subdomain via MetadataService to create a domain, if no domain exists. Thanks!
December 28, 2019 at 7:17 pm
Yes, this can be defined in the custom site Metadada type I believe.