Compared to past updates the Summer’14 update to the Salesforce Metadata API might not look that exciting at first glance, i know it took me a second look to get more excited! This time its perhaps whats not in it thats the most interesting and what it means for new developers trying to use this often elusive API for the first time!
Basically Salesforce have removed from API 31 onwards most of the pesky async operations. For those who read my past blog on these operations, you’ll see that they require a fair bit of work on the callers behalf to keep polling Salesforce for the results. Spring’14 introduced immediate versions of these operations alongside the old async ones, now with Summer’14 the old ones have gone completely from the WSDL! Before you panic, fear not, if your using the API 30 or below endpoints in existing solutions they are still present.
It is clear now that the Salesforce developers behind the Metadata API have been on a mission to clean it up, simplify it and make it more in alignment with how you currently use the Partner API, REST and Tooling API’s from realtime CRUD API perspective. It now also sports an upsert operation, meaning that the set of database operations you can now perform on the vast array of Metadata Component types it contains is complete! As a result it is now much easier to see what operations you need to get things done and because they are immediate your calling code is now much much simpler!
Salesforce is quite special in having an additional database operation in its terminology, known as upsert. Its is available in Apex and through its existing API’s, and now in the Metadata API. For those not familiar, it basically short cuts the need to check if a record or in this case a component already exists if your aim is to create it if it doesn’t exist or update it if it does.
MetadataService.MetadataPort service = createService(); MetadataService.CustomObject customObject = new MetadataService.CustomObject(); customObject.fullName = 'Test__c'; customObject.label = 'Test'; customObject.pluralLabel = 'Tests Upsert'; customObject.nameField = new MetadataService.CustomField(); customObject.nameField.type_x = 'Text'; customObject.nameField.label = 'Test Record Upsert'; customObject.deploymentStatus = 'Deployed'; customObject.sharingModel = 'ReadWrite'; List<MetadataService.UpsertResult> results = service.upsertMetadata( new MetadataService.Metadata[] { customObject });
In addition to these methods some of the old ones still exist, indeed there is no escaping the fact that if your wanting to create or retrieve many metadata components at once the retrieve and and deploy operations are still needed, and understandably still require aysnc type handling by the caller, since they could be quite long running operations.
Upgrade Advice: As always, i’ve updated the Apex Metadata API library to reflect the very latest release of the Metadata API. So if you are upgrading to the latest MetadataService.cls be aware the older methods will also not be present. The release notes here also cover other changes and new features. If your worried about this and don’t need any new component types for an existing project my advice is to stay with the earlier one and reserve this new version for new work. Finally i’ve retired some of the samples from the Apex Metadata API library focusing on using the Async API’s in batch and in Visualforce, as these would not compile anyway, they are of course still accessible through the repos history.
Of course, with most things there is always room for improvement! The ability to be able to run SOQL queries over the Metadata objects would be most welcome, the listMetadata method is just not flexible enough. Also the ability to create Apex Class and Apex Triggers without having to resort to using the deploy operation would also be appreciated.
I think Salesforce have done an amazing job at being committed to their API true to the core strategy here and with these changes it makes it so easy to go forth and do some cool stuff with the API without the extra overhead it had previously. No excuses now! 😉
Pingback: Introduction to calling the Metadata API from Apex | Andy in the Cloud
August 24, 2014 at 10:45 pm
Andy — using the sandbox deploy from git URL for the Apex metadata API financialforcedev/apex-mdapi , I get this error
….
Deployment Complete
Failures:
package.xml(MetadataServicePatcher/MetadataServicePatchedCopy):An object ‘MetadataServicePatcher/MetadataServicePatchedCopy’ of type Document was named in package.xml, but was not found in zipped directory
August 26, 2014 at 2:42 pm
Fixed, sorry about that.
November 20, 2014 at 5:10 am
Sorry about all the noob questions…I am attempting to create a new layout, not sure what I am missing. I looked in your MetaDataServiceExamples class and did not find one on creating a new layout. This code executes fine but it’s not creating the layout.
I looked here too https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_layouts.htm, still not getting the layout. Can you point me in the right direction?
//Create new layout
MetadataService.MetadataPort service = new MetadataService.MetadataPort();
service.SessionHeader = new MetadataService.SessionHeader_element();
service.SessionHeader.sessionId = UserInfo.getSessionId();
MetadataService.Layout testLayout = new MetadataService.Layout();
testLayout.fullName = ‘test__MyObject__c-Test Layout’;
//
//What else do I need?
//
List results = service.createMetadata(new MetadataService.Metadata[] { testLayout });
November 20, 2014 at 8:53 am
No problem, i’ve raised an issue on the GitHub repository to focus on this challenge for you. https://github.com/financialforcedev/apex-mdapi/issues/56
July 20, 2015 at 7:03 am
Hi Andy Amazing blog with Awasome Knowladge.
I have query can you halp me
I just want to create and Update Profile object permission and profile Field permission related to an object
actually i am a beginner and want to do this task
July 20, 2015 at 7:18 am
Recommend you first study the apex developers guide, bits on classes and inner classes, also consuming web services. Then review the sample code in metadadaserviceexamples.cls I can help with specific questions, but I don’t have the bandwidth to train you. Hope that’s ok and good luck!
July 20, 2015 at 1:15 pm
oh sorry by mistake i have write profile field lavel permission ,i have find out in metadata service api example but not profile object lavel permission, want to ask is it updatable because i have try to update it but get failled .
July 20, 2015 at 10:29 pm
I would recommend permissions sets personally, but yes profiles can be updated
July 21, 2015 at 6:33 am
i will try more in this direction , thanks for your help
July 27, 2015 at 7:01 am
This is my code but not working even not giving any error , can you help me
MetadataService.MetadataPort service = createService();
MetadataService.Profile admin = new MetadataService.Profile();
admin.fullName = ‘Admin’;
admin.custom = false;
MetadataService.ProfileObjectPermissions objPerm = new MetadataService.ProfileObjectPermissions();
objPerm.object_x=’Account’;
objPerm.modifyAllRecords = false;
objPerm.allowDelete = false;
//fieldSec.editable=true;
admin.objectPermissions = new MetadataService.ProfileObjectPermissions[] {objPerm} ;
List results =
service.updateMetadata(
new MetadataService.Metadata[] { admin });
MetadataService.handleSaveResults(results[0]);
July 27, 2015 at 7:02 am
Have you compared it with the example?
July 27, 2015 at 8:34 am
There is not any update example on ObjectPermission but there was example for FieldPermission And i have write these code according to that .
July 27, 2015 at 8:37 am
Ok please raise a GitHub issue, it’s easier to discuss in that site. Also have you considered simply creating a permission set and assigning it to the users? This can all be done in native apex code.
July 27, 2015 at 8:47 am
yupp I also have create native code to get a permission set of required profile ,till my code working properly and when i try to update its permission, it gives error.
July 27, 2015 at 10:52 pm
Profile permission sets are readonly. You need to create a brand new permission set on apex using dml then you can write to it and assign it, again all on apex.
October 20, 2015 at 6:54 am
are you able to retrieve an ApexClass using renameMetadata() ?
I’ve tried and get this error System.CalloutException: Web service callout failed: WebService returned a SOAP Fault: INVALID_TYPE: This type of metadata is not available for this organization faultcode=sf:INVALID_TYPE faultactor=
October 20, 2015 at 7:12 am
No this is not supported. You can query via soql apexclass object though.
October 20, 2015 at 3:10 pm
ok, thanks Andy!
February 17, 2016 at 9:03 am
Hi Andy,
Is there any way to read content of apex class through API? I’ve been able to read content of the page but not of the class.
February 24, 2016 at 7:55 pm
Yes you can, but only unmanaged classes, managed package classes are not visible to protect the publishers IP
February 24, 2016 at 7:56 pm
You can also query via SOQL the ApexClass object, so don’t need to use Apex Metadata API
March 3, 2016 at 8:53 am
Hi Andy,
Firstly, thanks for putting in effort for creating such an awesome wrapper. Before i deep dive, need some quick advice.
I am working with goal metrics and have a requirement to clone an existing metric and also link it to a new report which basically the clone of existing report linked to the metric but with just the date parameters changed.
Is it possible to clone an existing report to create a new one using a combination of Report API and the metadata API wrapper?
March 3, 2016 at 9:30 am
I am not familiar with goal metrics feature. You can for sure though retrieve a report definition with metadada Api, modify it and create a new one. The Analytics API is read only and the information structures it returns is not compatible with the inputs of the Metadata API. So I would stick with Metadata API for what you need here. Thanks for your kind comments!
April 4, 2016 at 10:22 am
Thanks Andy. I could achieve what was required using the metadata API & it works perfectly but the issue is that it requires ‘Modify All Data’ permission.
Is there any workaround to this? The end user would be using this feature to clone theirs Goals, associated metrics and linked reports & I definitely can’t give them ‘Modify All Data’ permission 😦
April 5, 2016 at 7:20 am
You can use an oauth token from another user, to do the work on their behalf. It’s been discussed in the GitHub issues on the repo if you search the repo for oAuth. It requires basically performing an oAuth web ui authorisation flow with a user as part of the setup of your solution, then storing the oauth code in some ideally secure place for later use. Use of oAuth with the Metadata API is something that keeps coming up so might be something I should dig into further for a future blog. It’s worth noting that you could also just login with another users credentials and use that session id, obviously for that type of solution your less secure as you need to store this user and password some place. Hope this helps for now.
April 5, 2016 at 7:22 am
By the way would like to share your use case with Salesforce? They are looking for funding (justify internal resource allocation) to build this properly into the platform. Your clone use case is a good one, if you check my Twitter feed you will find a link to a Salesforce Community Group they have setup.
April 5, 2016 at 8:17 am
Thanks Andy … i have already started work on using some integration account for login, will give you the updates once done..And yes i definitely don’t mind sharing the use case, will do it as soon i fix this one…
April 6, 2016 at 10:02 am
Hi Andy..I did it using ‘Named Credentials’ & it works with both Password Authentication or OAuth 2.0 protocol.
May 2, 2016 at 7:28 am
Hello Andy, Thank you for the post. I have query regarding ApexClass, Can we create ApexClass using CreateMetadata api? I tried to do so in my Developer addition and getting error as mentioned. Please suggest.
System.CalloutException: Web service callout failed: WebService returned a SOAP Fault: INVALID_TYPE: This type of object is not available for this organization faultcode=sf:INVALID_TYPE faultactor=
May 2, 2016 at 12:49 pm
Sorry Salesforce does not support this, only via deploy operation. See the deploy example on the repo.
June 8, 2016 at 5:43 pm
Hi Andy,
I am big fan of MetadataAPI and built some small utility tools to leverage the api. Thanks for the blog and work that you did on MetadataService class to make it available in Apex.
Quick question – I want to re-name some of the Test Classes in Production and thinking of using renameMetadata() call. Do you know if renameMetadata() runs the test classes when used in Prod? I am going to give it a try, but just in case if you know.
June 9, 2016 at 4:16 pm
Thanks for the kind comments. Sadly ApexClass Metadada type is not supported for the CRUD methods. Only deploy and retrieve method. Even then it’s not really a rename, you upload a destructive package xml to delete the class and a new one in its place. This operation can be further complicated by references to the class. In short, rename of apex class is not really supported.
November 26, 2016 at 12:48 am
Hello, thanks about this information, I try to update the sharingModel but it does not work, it update the pluralLabel but nosharingModel.
MetadataService.CustomObject customObject = new MetadataService.CustomObject();
customObject.fullName = ‘CIC_Test__c’;
customObject.label = ‘Test’;
customObject.pluralLabel = ‘Tests Actualizado 1’;
customObject.nameField = new MetadataService.CustomField();
customObject.nameField.type_x = ‘Text’;
customObject.nameField.label = ‘Test Record Upsert’;
customObject.deploymentStatus = ‘Deployed’;
customObject.sharingModel = ‘Private’ ;
results =
service.upsertMetadata(
new MetadataService.Metadata[] { customObject });
Greetings.
November 26, 2016 at 12:51 am
Have you inspected the results variable to see if there is any error message? If you look at the MetadataServiceExamples.cls there is some samples and each show how to handle the results object to learn about errors.
November 27, 2016 at 10:59 pm
Thank you for you reply, i make a handler this is those are results, the operation was successful
success: true
fullName: CIC_Test__c
created: false
errors: null
Full service return:
results: [UpsertResult:[apex_schema_type_info=(http://soap.sforce.com/2006/04/metadata, true, false), created=false, created_type_info=(created, http://soap.sforce.com/2006/04/metadata, null, 1, 1, false), errors=null, errors_type_info=(errors, http://soap.sforce.com/2006/04/metadata, null, 0, -1, false), field_order_type_info=(created, errors, fullName, success), fullName=CIC_Test__c, fullName_type_info=(fullName, http://soap.sforce.com/2006/04/metadata, null, 1, 1, false), success=true, success_type_info=(success, http://soap.sforce.com/2006/04/metadata, null, 1, 1, false)]]
Greetings.
November 28, 2016 at 3:10 am
If you run the code form anonymous apex prompt do you see the xml request and response in the debug log?