Andy in the Cloud

From BBC Basic to Force.com and beyond…

71 thoughts on “Apex Metadata API Q&A

  1. Thanks Andy. Again a great article. Does nee version solve the problem of “having the instance as remote site” before calling the metadata api?

    I need to create fieldsets on package install. I tried using MetaData API in the past, but it would not work unless there was remote site to same Salesforce instance to which the app is being installed, for e.g. na11.salesforce.com. This did not make sense, as it was invoking from same org.

    Do I still have to worry about this?

    • I’m afraid not, you still need to refer to my other blog on how to semi-automate this. But sadly no solution from package install script, best is a post install VF page.

  2. Thanks Andrew. Can you throw in quick example on how I could retrieve an element, let’s say a FieldSet? I would like to create an element only if it does not exist already.

    • There are quite a few examples in the MetadataServiceExamples.cls class (in the repo also) have you checked that out? If so, and your need something more, please raise a GitHub issue and i’ll see what i can do for you! 🙂

  3. Thanks Andy. Can we write the metadata of a Standard Field using the MetaData API?. Particularly the help text of Standard Fields. This can be done by Standard Salesforce screens, but I am not able to figure out how to do it from the API layer

    • Yes you can do this, the help text is exposed as a member variable on the CustomField class. Take a look at the Custom Field examples in the MetadataServiceExamples.cls (also in the repo). I think there is one that updates a Picklist field, you should be able to see what needs doing from that. Let me know via a GitHub issue on the repo if your still needing some help after taking a look at this, and i’d be happy to help more! 🙂

  4. Andy, do you know if we can create custom formula fields via metadata? I looked the class and field types here: https://www.salesforce.com/us/developer/docs/api_meta/Content/customfield.htm

    I do not think it is possible. Can you please confirm?

  5. Thanks Andy. That worked like a charm. Let me know if you have happen to be in Austin area.. beer is on me.

    • Great news and thanks i will! 🙂

      • Declared victory too soon. While I am able to create custom field via Metadata, it turns out I cannot do FLS with such ease. I have to manually set FLS for all profiles. My uses case is simple, all profiles should have read access to it.

        I found this old thread on how you can set FLS using code, this is like killing fly with bazooka. Is there any better way to achieve it? Here is the link: http://salesforce.stackexchange.com/questions/34521/set-field-level-security-for-profile-using-meta-data-api

      • You don’t need to use deploy/retrieve you can use the CRUD methods. Have you seen this example? https://github.com/financialforcedev/apex-mdapi/blob/master/apex-mdapi/src/classes/MetadataServiceExamples.cls#L154 You can also create a Permission Set in Apex code directly using DML and also assign it to your users, again in code. I’d personally recommend this over adapting profiles, but either approach here should do what you want. Let me know if you want any more details. 🙂

      • That was fast! I do not like to create permission set if I don;t have to. Thank you for the example, looks like it would still require to get all profiles in for loop and update FLS for each. Is there global setting where we can set same FLS for all profile at once?

      • Yes that would be a single shared Permission Set assigned to all your users. 😉

      • Made some progress but getting exception with below code if there more than 10 profiles. Hence I restricted to just system admin profiles, now I strange error message in debug log. Can you please tell me what am I missing here? Thank you.

        //Store all profile names in list variable
        list profileNames = new list();
        for(Profile profile : [SELECT Id, Name FROM Profile WHERE UserType=’Standard’ AND Name LIKE ‘System%’ ORDER BY Name]){
        profileNames.add(profile.Name);
        }

        //Get the field
        MetadataService.ProfileFieldLevelSecurity fieldSec = new MetadataService.ProfileFieldLevelSecurity();
        fieldSec.field = ‘custom_object__c.custom_field__c’;
        fieldSec.readable = true;
        fieldSec.editable = true;

        //Update FLS for each profile
        for(MetadataService.Metadata metadata : service.readMetadata(‘Profile’, profileNames).getRecords()){
        MetadataService.Profile profile = (MetadataService.Profile)metadata;
        profile.fieldPermissions = new MetadataService.ProfileFieldLevelSecurity[] {fieldSec};
        updateMetadata.add(profile);
        }

        //Call metadata service
        if(updateMetadata.size()>0 && UserInfo.getOrganizationId() != ’00Dd0000000cDqREAU’)
        service.updateMetadata(updateMetadata);

        // ERROR MESSAGE

        Web service callout failed: WebService returned a SOAP Fault: UNKNOWN_EXCEPTION: An unexpected error occurred. Please include this ErrorId if you contact support: 1896081984-142957 (-1781941849) faultcode=sf:UNKNOWN_EXCEPTION faultactor=

      • I’m not able to decode those types of errors, only Salesforce support can do that. As the message suggests you have to raise a support case and provide your org id and this error message. They usual then tell you the underlying error message or declare it a platform API bug (which is basically what it is, since it’s not giving you a friendly error message).

      • Sorry Andrew. I did not mean that. Only Salesforce can help debug such user-friendly messages. I was asking from logic standpoint. Do you see anything wrong as I have very limited knowledge working with metadata API.

  6. Hi Andrew,

    I just found your MetadataServices class today (the one here https://github.com/financialforcedev/apex-mdapi/blob/master/apex-mdapi/src/classes/MetadataService.cls). I copied the class, test class, and examples class in to my salesforce developer org (along with all the other classes just to be on the safe side) and created a remote site setting for https://na16.salesforce.com (the URL of my dev org). When I try to run any of your examples however (like the customObject example), I get the following error, “Method does not exist or incorrect signature: createService()”. What’s weird though, is that I can’t seem to find the code that calls this method in the MetadataServices class. My org is API 33.0, which I know is the Spring 15 version, but I can’t figure out why the createService() method seems to be missing from the class. Do you have any ideas on why it might be throwing that error or what I might be doing wrong? Any insight would be seriously appreciated.

    Thanks!

    • That’s odd, this method is only in MetadataServiceExamples.cls

      • Hi Andrew, how would you recommend that I adjust my execution of your examples? I’m admittedly a bit of a newb to this area and played with it for about 4 hours on Tuesday before finally throwing my hands up and reaching out to you. Just deleting that section of the example methods (the createService part) doesn’t seem to fix the problem (as it just throws a different error at that point). I’m not quite sure what I need to adjust. Thanks a bunch for your response btw.

      • No worries, can you post on the github repo as an issue with a sample of your code and I will try to help there. Thanks

      • Done. Thanks Andrew! Talk to you on there.

      • Is there any solution for this issue “Method does not exist or incorrect signature: createService()”?

      • Yes create the method, you can copy it from the MetadataServiceExamples.cls

  7. Hi Andy,

    The code at github helped me a lot and I’m able to achieve most of the required things via Metadata but stuck at sharing rule creation.

    I have created new MetadataService class using API version 33.0 specially to create sharing rule using Metadata API and when I try to create sharing rule, I receive either no error or unknown error from salesforce on callout.

    I posted my question on stack exchange:
    http://salesforce.stackexchange.com/questions/69977/issue-with-creating-sharing-rule-using-metadata-api-version-33-0

    If you could take a look and let know what I’m doing wrong.

  8. Hey Andrew,

    First of all, the APEX Metadata API you have created is very helpful and an amazing piece of code. Thank you for taking the time to do this.

    I am trying to see if I can use the MetadataService class you have created to deploy Metadata to other orgs that I have access to. I know it is possible to use APEX to deploy to other orgs as http://www.flosum.com/ already does this very well.

    However, I am trying to just understand how I would go about achieving this using the class you have created.

    Any help or tips you could give me would be extremely appreciated.

  9. Hi Andrew, I am trying to retrieve all the Page Layouts of an Object without having hard-coded Layout names in my MataData API calls from Apex but did find a way to achieve that. Are there any methods available in the API which can be used to pull all Layout names for an Object? Thanks you in Advance.

  10. Hi ALL

    i am getting below mentioned error when try to use META API apex class

    Compile error at line 15 column 41 Method does not exist or incorrect signature: [MetadataService.MetadataPort].create(List)

    i am using Eclipse 3.6 Force.com IDE.

    Regards
    Sumit

  11. Hi Andrew,

    I was unable to retrieve all page layouts in one org. I am using this code but response is null.

    // Read the Layout
    MetadataService.MetadataPort service = createService();
    system.debug( service.readMetadata(‘Layout’,
    new String[] { ‘*’ }).getRecords());

    Please suggest

    • You cannot pass wildcards to this method. You must either use listMetadata method and then call retrieveMetadata or use the retrieve method. You can see examples in the github repo and also don’t forget the salesforce Metadada api docs.

  12. Hi Andrew ,

    I am facing a weird issue regarding update layout. My code is working fine unless its an unmanaged packaged but as soon as I shift it into managed package it starts throwing unknown exception.
    Error like this: Web service callout failed: WebService returned a SOAP Fault: UNKNOWN_EXCEPTION: An unexpected error occurred. Please include this ErrorId if you contact support: 697030706-2552 (-407299373) faultcode=sf:UNKNOWN_EXCEPTION faultactor=3058

    and I am working with standard objects only although I tried adding namespace too but not working. Using code like this : MetadataService.Layout layout =
    (MetadataService.Layout) service.readMetadata(‘Layout’,
    new String[] { ‘Account-dragNdrop__Account-Account Layout’ }).getRecords()[0];
    handleSaveResults(
    service.updateMetadata(
    new MetadataService.Metadata[] { layout })[0]);

    Let me know what you think.

    • Layouts and managed packages are tricky for sure, worth scanning some of the github issues in the repo for tips, also check my faq blog post to see how to use developer workbench to confirm the name

  13. Hello Andy, Great Article, thanks. Is there a way of adding a VF page to a standard layout in lightning experience using the meta data api?

    • You should be able to retrieve the layout, edit it in memory to add the section and item for the page then update it. Take a look at the sample code in the repo and also retrieve via xml a layout to see how it’s setup and replicate in your code

  14. Hello Andy,

    I truly appreciate all your efforts in this area. I’ve been able to use your code as the basis for automating the creation of objects and fields in SF.

    I am puzzled by the “hard-coded” element of your MetadataServiceExamples.cls – is there a reason why you took this approach as opposed to designing the createObject method to be called with parameters? e.g. something akin to the following:

    public static void createObject( string objFullName
    , string objLabel
    , string objPluralLabel
    , string objNameFieldTypeX
    , string objNameFieldLabel
    , string objDescription
    , string objDeploymentStatus
    , string objSharingModel
    , boolean objEnableReports
    , boolean objEnableActivities
    , boolean objEnableHistory
    , boolean objEnableFeeds
    , boolean objEnableBSS // Allow Sharing, Allow Bulk API Access, and Allow Streaming API Access must be enabled together or disabled together
    , boolean objEnableDivisions
    , boolean objEnableEnhancedLookup
    )
    {
    MetadataService.MetadataPort service = createService();
    MetadataService.CustomObject customObject = new MetadataService.CustomObject();
    customObject.fullName = objFullName;
    customObject.label = objLabel;
    customObject.pluralLabel = objPluralLabel;
    customObject.nameField = new MetadataService.CustomField();
    customObject.nameField.type_x = objNameFieldTypeX;
    customObject.nameField.label = objNameFieldLabel;
    customObject.description = objDescription;
    customObject.deploymentStatus = objDeploymentStatus;
    customObject.sharingModel = objSharingModel;
    customObject.enableReports = objEnableReports;
    customObject.enableActivities = objEnableActivities;
    customObject.enableHistory = objEnableHistory;
    customObject.enableFeeds = objEnableFeeds;
    customObject.enableBulkApi = objEnableBSS;
    customObject.enableSharing = objEnableBSS;
    customObject.enableStreamingApi = objEnableBSS;
    customObject.enableDivisions = objEnableDivisions;
    customObject.enableEnhancedLookup = objEnableEnhancedLookup;

    List results =
    service.createMetadata(
    new MetadataService.Metadata[] { customObject });
    handleSaveResults(results[0]);
    }

    • This is by design, which is to mirror Salesforce Metadata API, the MetadataService class is a most system generated wrapper for.

  15. Hi Andy,

    Can we minimize the metadataservice class to have only required metadata helper classes?

    • Only by manually deleting things or copying over incrementally to a new class the things you need. It is an enhancement to provide a better way to do this.

  16. Great Article @Andy

  17. Hi Andy,

    Is it possible to fetch other org’s metadata?

  18. Not able to retrieve Apex class. Code which I am using this :

    MetadataService.MetadataPort service = MetadataServiceExamples.createService();
    system.debug(service.readMetadata(‘ApexClass’,new String[] {‘StringArrayTest’}).getrecords()[0]);

    Error I am getting is this :

    00:59:29:268 FATAL_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=

    • This is a Salesforce API restriction, nothing I can do about it. However see my other comment response for options.

  19. Can we retrieve apex class content ? I am not able to do so

    • Yes with the retrieve API, as per the Retrieve Demo in the GitHub repo. Or a better way is to just use SOQL on the ApexClass object (see soap developers guide), you can query the Body field. You can only read records on this object. To update code, you need to use the deploy metadata API, again there is a deploy example in the GirHub repo.

  20. Andy, can we retrieve Global Pick-list through this?

    • It was recently updated to the API version to support this. Though I have yet to physically try it. In theory it should I would say. Let me know how you get on. It would be great to add a sample to the metadataserviceexamples class. 🙂

      • Thanks for Reply, I tried it for Global Pick list by using this code :

        MetadataService.GlobalPickList report = (MetadataService.GlobalPickList) service.readMetadata(‘GlobalPickList’, new String[] { ‘TestGlobal’ }).getRecords()[0];

        But it throws error “Web service callout failed: WebService returned a SOAP Fault: null: type is not a valid metadata type for reading faultcode=sf:UNKNOWN_EXCEPTION faultactor= “.

        Can you please confirm me that we can’t access global pick-list values directly in Apex?

      • This looks like a restriction of the Metadata API. The docs don’t appear to indicate one way or another… https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_globalpicklist.htm

      • If you raise an issue on the GitHub site I will investigate further for you at some point

  21. Hi Andrew.

    This is was an amazing post. I tried to run it in my org and although i am able to get the results, they are not as expected. I am trying to get a list of Names of all the workflows which are present in my org, i use this code :

    public static void listMetadata()
    {
    MetadataService.MetadataPort service = createService();
    List queries = new List();
    MetadataService.ListMetadataQuery queryWorkflow = new MetadataService.ListMetadataQuery();
    queryWorkflow.type_x = ‘Workflow’;
    queries.add(queryWorkflow);
    //MetadataService.ListMetadataQuery queryValidationRule = new MetadataService.ListMetadataQuery();
    //queryValidationRule.type_x = ‘ValidationRule’;
    //queries.add(queryValidationRule);
    MetadataService.FileProperties[] fileProperties = service.listMetadata(queries, 25);
    for(MetadataService.FileProperties fileProperty : fileProperties)
    System.debug(‘********The Names are :’+fileProperty.fullName);
    }

    public static MetadataService.MetadataPort createService()
    {
    MetadataService.MetadataPort service = new MetadataService.MetadataPort();
    service.SessionHeader = new MetadataService.SessionHeader_element();
    service.SessionHeader.sessionId = UserInfo.getSessionId();
    return service;
    }
    listMetadata();

    But i do not get the names of the workflows, instead a list of objects. What am i doing wrong here?

  22. CustomField is always NULL.


    Can anyone suggest a resolution for this common code snippet below? The field exists and I can create/update using the Apex Metadata API just fine.

    // Read Custom Field
MetadataService.CustomField customField = (MetadataService.CustomField) service.readMetadata(
’CustomField’, new String[] { ‘Account.CustomerPriority__c’ }
).getRecords()[0];

    Ultimately, I want to create pick list values and activate them on record types. I see I need to update all the existing pick list values otherwise they will be deactivated on the record type.

    • Hmmm I have a feeling you can only read by retrieving the object. Though I can only find a reference to the fact in the retrieve API topic not the readmetadata one.

  23. Hi Andy,
    Sorry, I posted this on the wrong thread before, and could really use your assistance with this wonderful api.
    Can you consider why I get nulls for all properties reading any object or element.
    Even the simple snippet below returns all nulls. Creating is PERFECT and error free!
    MetadataService.CustomField customField = (MetadataService.CustomField) service.readMetadata(‘CustomField’, new String[] { ‘Lead.picklist__c’ }).getRecords()[0];

    Thank you

  24. Trying to add data to picklist and here is the error:

    Error is in expression ‘{!saveNew}’ in component in page dynamiclist: Class.MetadataService.MetadataPort.upsertMetadata: line 11599, column 1
    Class.DynamicListController.addPicklistValue: line 119, column 1

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s