Andy in the Cloud

From BBC Basic to Force.com and beyond…

43 thoughts on “Apex Metadata API Spring’13 Update : Org Settings Access

  1. We got to a similar place by extracting the Metadata to an apex class. The issue I have is I don’t know how to build a test for the actual metadata service. I have a subset of service code similar to what you have here: https://github.com/financialforcedev/apex-mdapi/tree/master/apex-mdapi/src/classes, but any wrapper I build that uses this service code gets test coverage while none of the code in the service records any coverage. The wrapper creates and uses methods in the service but still the service sites at 0% coverage.

    I see your test class as still empty. Have you have any traction building tests for the methods in the service? I am seriously stuck. 😦

    Any help would be appreciated. I’m new to apex.

    • There is some guidelines on testing and coverage here. However I can see that people are still struggling with this, so I’ll take a look at updating the readme and/or code samples with something more on this. Likely be the weekend at the earliest though I’m afraid. In the meantime this is actually the testing guidelines Salesforce give and what I’ll be looking at doing in this case, in case you wanted to look in the meantime.

    • Ok, this is a Salesforce bug! Took me about 3hrs to track it down. I’ve reproduced it with this https://gist.github.com/afawcett/5521019. In the meantime there is a workaround to start generating coverage. I’ve also updated the MetadataServiceTest.cls so you can use this if you wish, without having to comment parts out.

      • Oh and make sure you update MetadataService.cls as well, I made a small change to that to support this.

  2. Is it possible to “create” a remote site settings using this approach ?

    • Unfortunately not, you can package them, but you will need to know the URL relating to the target org instance, unless there is one URL that can be used regardless of instance. I’d need to check that. Otherwise we just include it as a post install step for the package.

      • That said, once you have added a Remote Site setting for the Metadata URL’s, yes you can use ‘create’ to create others!

  3. how can we insert a record type using metadata api?

  4. Is it possible to retrieve ListView columns with this? I don’t see any examples of it, but I am sure it has to be possible with this.

    • Yep it’s possible, via the retrieve call, try running the retrieve demo, it should have Listview type in the drop down.

  5. Is there a way to create a new site? CustomSite does not extend Metadata so I was really unsure how to go about it since none of the examples use classes that aren’t extending the Metadata.

    • I am not online fully so cannot check, but I assume CustomSite is a child class of a Metadata type that does extend Metadata? Or it’s simply that it needs to in the MetadataService.cls, as per the approach in the read me notes at bottom. You can confirm via the Metadata API dev guide, search for the type and if it says it should extend it, modify the code to do so as per my read me notes. I will try to check at the weekend for you though.

    • Ok it does extend it, it’s just that it is amongst the types I did not sweep yet, thus follow the notes in the readme on your local copy to make it extend it. And you should be good, feel free to sunlit the change back, or if not i will update it soon and add a sample.

      http://www.salesforce.com/us/developer/docs/api_meta/Content/meta_sites.htm

  6. Is there a way to create a custom site? The customsite class does not extend the metadata class so I was unsure on how to accomplish as none of the example show how to do this.

    • So I figured it out, if you replace the CustomSite class in the MetaData Service with this below, then you can create it similar to creating a new custom field, custom object, etc..

      public class CustomSite extends Metadata {
      public String type = ‘CustomSite’;
      public Boolean active;
      public Boolean allowHomePage;
      public Boolean allowStandardAnswersPages;
      public Boolean allowStandardIdeasPages;
      public Boolean allowStandardLookups;
      public Boolean allowStandardSearch;
      public String analyticsTrackingCode;
      public String authorizationRequiredPage;
      public String bandwidthExceededPage;
      public String changePasswordPage;
      public String chatterAnswersForgotPasswordConfirmPage;
      public String chatterAnswersForgotPasswordPage;
      public String chatterAnswersHelpPage;
      public String chatterAnswersLoginPage;
      public String chatterAnswersRegistrationPage;
      public MetadataService.SiteWebAddress[] customWebAddresses;
      public String description;
      public String favoriteIcon;
      public String fileNotFoundPage;
      public String genericErrorPage;
      public String guestProfile;
      public String inMaintenancePage;
      public String inactiveIndexPage;
      public String indexPage;
      public String masterLabel;
      public String myProfilePage;
      public String portal;
      public Boolean requireInsecurePortalAccess;
      public String robotsTxtPage;
      public String rootComponent;
      public String serverIsDown;
      public String siteAdmin;
      public MetadataService.SiteRedirectMapping[] siteRedirectMappings;
      public String siteTemplate;
      public String siteType;
      public String subdomain;
      public String urlPathPrefix;
      private String[] type_att_info = new String[]{‘xsi:type’};
      private String[] active_type_info = new String[]{‘active’,’http://www.w3.org/2001/XMLSchema’,’boolean’,’1′,’1′,’false’};
      private String[] allowHomePage_type_info = new String[]{‘allowHomePage’,’http://www.w3.org/2001/XMLSchema’,’boolean’,’1′,’1′,’false’};
      private String[] allowStandardAnswersPages_type_info = new String[]{‘allowStandardAnswersPages’,’http://www.w3.org/2001/XMLSchema’,’boolean’,’0′,’1′,’false’};
      private String[] allowStandardIdeasPages_type_info = new String[]{‘allowStandardIdeasPages’,’http://www.w3.org/2001/XMLSchema’,’boolean’,’1′,’1′,’false’};
      private String[] allowStandardLookups_type_info = new String[]{‘allowStandardLookups’,’http://www.w3.org/2001/XMLSchema’,’boolean’,’1′,’1′,’false’};
      private String[] allowStandardSearch_type_info = new String[]{‘allowStandardSearch’,’http://www.w3.org/2001/XMLSchema’,’boolean’,’1′,’1′,’false’};
      private String[] analyticsTrackingCode_type_info = new String[]{‘analyticsTrackingCode’,’http://www.w3.org/2001/XMLSchema’,’string’,’0′,’1′,’false’};
      private String[] authorizationRequiredPage_type_info = new String[]{‘authorizationRequiredPage’,’http://www.w3.org/2001/XMLSchema’,’string’,’0′,’1′,’false’};
      private String[] bandwidthExceededPage_type_info = new String[]{‘bandwidthExceededPage’,’http://www.w3.org/2001/XMLSchema’,’string’,’0′,’1′,’false’};
      private String[] changePasswordPage_type_info = new String[]{‘changePasswordPage’,’http://www.w3.org/2001/XMLSchema’,’string’,’0′,’1′,’false’};
      private String[] chatterAnswersForgotPasswordConfirmPage_type_info = new String[]{‘chatterAnswersForgotPasswordConfirmPage’,’http://www.w3.org/2001/XMLSchema’,’string’,’0′,’1′,’false’};
      private String[] chatterAnswersForgotPasswordPage_type_info = new String[]{‘chatterAnswersForgotPasswordPage’,’http://www.w3.org/2001/XMLSchema’,’string’,’0′,’1′,’false’};
      private String[] chatterAnswersHelpPage_type_info = new String[]{‘chatterAnswersHelpPage’,’http://www.w3.org/2001/XMLSchema’,’string’,’0′,’1′,’false’};
      private String[] chatterAnswersLoginPage_type_info = new String[]{‘chatterAnswersLoginPage’,’http://www.w3.org/2001/XMLSchema’,’string’,’0′,’1′,’false’};
      private String[] chatterAnswersRegistrationPage_type_info = new String[]{‘chatterAnswersRegistrationPage’,’http://www.w3.org/2001/XMLSchema’,’string’,’0′,’1′,’false’};
      private String[] customWebAddresses_type_info = new String[]{‘customWebAddresses’,’http://soap.sforce.com/2006/04/metadata’,’SiteWebAddress’,’0′,’-1′,’false’};
      private String[] description_type_info = new String[]{‘description’,’http://www.w3.org/2001/XMLSchema’,’string’,’0′,’1′,’false’};
      private String[] favoriteIcon_type_info = new String[]{‘favoriteIcon’,’http://www.w3.org/2001/XMLSchema’,’string’,’0′,’1′,’false’};
      private String[] fileNotFoundPage_type_info = new String[]{‘fileNotFoundPage’,’http://www.w3.org/2001/XMLSchema’,’string’,’0′,’1′,’false’};
      private String[] genericErrorPage_type_info = new String[]{‘genericErrorPage’,’http://www.w3.org/2001/XMLSchema’,’string’,’0′,’1′,’false’};
      private String[] guestProfile_type_info = new String[]{‘guestProfile’,’http://www.w3.org/2001/XMLSchema’,’string’,’0′,’1′,’false’};
      private String[] inMaintenancePage_type_info = new String[]{‘inMaintenancePage’,’http://www.w3.org/2001/XMLSchema’,’string’,’0′,’1′,’false’};
      private String[] inactiveIndexPage_type_info = new String[]{‘inactiveIndexPage’,’http://www.w3.org/2001/XMLSchema’,’string’,’0′,’1′,’false’};
      private String[] indexPage_type_info = new String[]{‘indexPage’,’http://www.w3.org/2001/XMLSchema’,’string’,’1′,’1′,’false’};
      private String[] masterLabel_type_info = new String[]{‘masterLabel’,’http://www.w3.org/2001/XMLSchema’,’string’,’1′,’1′,’false’};
      private String[] myProfilePage_type_info = new String[]{‘myProfilePage’,’http://www.w3.org/2001/XMLSchema’,’string’,’0′,’1′,’false’};
      private String[] portal_type_info = new String[]{‘portal’,’http://www.w3.org/2001/XMLSchema’,’string’,’0′,’1′,’false’};
      private String[] requireInsecurePortalAccess_type_info = new String[]{‘requireInsecurePortalAccess’,’http://www.w3.org/2001/XMLSchema’,’boolean’,’1′,’1′,’false’};
      private String[] robotsTxtPage_type_info = new String[]{‘robotsTxtPage’,’http://www.w3.org/2001/XMLSchema’,’string’,’0′,’1′,’false’};
      private String[] rootComponent_type_info = new String[]{‘rootComponent’,’http://www.w3.org/2001/XMLSchema’,’string’,’0′,’1′,’false’};
      private String[] serverIsDown_type_info = new String[]{‘serverIsDown’,’http://www.w3.org/2001/XMLSchema’,’string’,’0′,’1′,’false’};
      private String[] siteAdmin_type_info = new String[]{‘siteAdmin’,’http://www.w3.org/2001/XMLSchema’,’string’,’0′,’1′,’false’};
      private String[] siteRedirectMappings_type_info = new String[]{‘siteRedirectMappings’,’http://soap.sforce.com/2006/04/metadata’,’SiteRedirectMapping’,’0′,’-1′,’false’};
      private String[] siteTemplate_type_info = new String[]{‘siteTemplate’,’http://www.w3.org/2001/XMLSchema’,’string’,’0′,’1′,’false’};
      private String[] siteType_type_info = new String[]{‘siteType’,’http://soap.sforce.com/2006/04/metadata’,’SiteType’,’1′,’1′,’false’};
      private String[] subdomain_type_info = new String[]{‘subdomain’,’http://www.w3.org/2001/XMLSchema’,’string’,’0′,’1′,’false’};
      private String[] urlPathPrefix_type_info = new String[]{‘urlPathPrefix’,’http://www.w3.org/2001/XMLSchema’,’string’,’0′,’1′,’false’};
      private String[] apex_schema_type_info = new String[]{‘http://soap.sforce.com/2006/04/metadata’,’true’,’false’};
      private String[] field_order_type_info = new String[]{‘active’,’allowHomePage’,’allowStandardAnswersPages’,’allowStandardIdeasPages’,’allowStandardLookups’,’allowStandardSearch’,’analyticsTrackingCode’,’authorizationRequiredPage’,’bandwidthExceededPage’,’changePasswordPage’,’chatterAnswersForgotPasswordConfirmPage’,’chatterAnswersForgotPasswordPage’,’chatterAnswersHelpPage’,’chatterAnswersLoginPage’,’chatterAnswersRegistrationPage’,’customWebAddresses’,’description’,’favoriteIcon’,’fileNotFoundPage’,’genericErrorPage’,’guestProfile’,’inMaintenancePage’,’inactiveIndexPage’,’indexPage’,’masterLabel’,’myProfilePage’,’portal’,’requireInsecurePortalAccess’,’robotsTxtPage’,’rootComponent’,’serverIsDown’,’siteAdmin’,’siteRedirectMappings’,’siteTemplate’,’siteType’,’subdomain’,’urlPathPrefix’};
      }

      • Excellent! Will update.

      • I’ve updated the MetadataService.cls (btw your sample above is missing some changes i thought needed to enable a Metadata type to work, does yours work?). You can find a full example and updated class in the repo, https://github.com/financialforcedev/apex-mdapi/blob/master/apex-mdapi/src/classes/MetadataServiceExamples.cls#L408. Thanks again for the feedback, would love to know more about your use case btw, if you can share a summary perhaps?

      • I hadn’t gotten that far in the process… I got as far as the job submitted successfully so thanks for the follow through.

        The use case is a customer is creating an app exchange package that is utilizing the portal functionality. During the initial setup, they want the site administrator to click a button and the site will be setup along with the site’s public access settings. Since all of the package sites will be the exact same settings, we will be utilizing the metadata service class to accomplish this.

  7. Hi Andrew Fawcett,

    I want to retrieve the object schema created from one org and need to store this schema in another org.

    Could me please help me on this.

    Thank you,
    Bharat

    • When you say store in another org, do you mean in XML format?

      • Thanks for the reply,

        For example,

        I have master org and one slave org.

        In the Slave org I have created one object called “Test object”.

        In the master org, this “Test Object” should be created automatically by scheduling or any other process I don’t know. So please help me out.

        Main purpose of the master org is to trace the slaves orgs information of custom objects.

        Thank you

      • Interesting, have you taken a look at Changesets? What your taking about is possible but is not simple to develop, what layouts, validation rules etc? The ‘retrieve’ and ‘deploy’ operations will basically do what you want, but constructing the package.xml (what you want to copy around) and logging into the master org (will need to call login API) is complex.

      • Hi,

        Triggers,validation rules,page layouts etc. everything related to custom object should be reflected in master object.

        I think with changed sets it doesn’t work because, this should be automated whenever changes done in slave org.

        Could you please help me out using metadata api how to retrieve the object schema in apex class and their respective information like fields, page layouts etc.

  8. Thanks for you help, I’ve got the site stuff working now. On to the next challenge. Do you know of a way to get the detail of the custom site’s data? I need the public profile Id for the custom site which is a Read Only field. After the site is created, I only receive a MetaDataService.FileProperties type return which does not give me the info I need. Do you know of a way to retrieve a CustomSite class from the MetaData?

    Thanks for all you help!

  9. Hi,

    Can I track it from your service code that how many languages are active on an organization using translation workbench? My scenario is to identify active languages of organization in a trigger and then create some record in custom object.

    Will really appreciate your help 🙂

  10. Sure, let me quickly visit there.. Thanks alot

  11. Thank you so much Andrew, you have done an awesome work.. making our life easier.. God Bless you

  12. Hi,

    I was trying to retrieve only the active validation rules and workflow rules for a particular object through meta data API. However i could only retrieve all the rules not the active ones specifically. Also is it possible to deactivate the workflow rules/validation rules/triggers by accessing metadata via apex. Please let me know how this can be done.

    Thanks in advance

    • I’m pretty sure there is no way to filter by active/inactive, its really not a use case i would imagine the API was designed. You should be able to toggle Active field via the ‘update’ operation for all but the triggers (which need to be done via deploy or tooling api).

  13. Hi Andrew,

    I am trying to create a work flow rule but not have been successful in creating with Metadata API,I even tried to update other workflow rule ( to de-activate it) using the Update operation but that too didn’t work.

    So just wondering I am doing something wrong or you can’t create workflow with Metadata API? below is the code to create a workflow rule.

    MetadataService.WorkflowRule workflowRuleTest = new MetadataService.WorkflowRule();
    workflowRuleTest.fullName=’Contact.Test Workflow ‘;
    workflowRuleTest.active=true;
    workflowRuleTest.triggerType=’onCreateOrTriggeringUpdate’;

    workflowRuleTest.Description=’Test workflow’ ;

    MetadataService.FilterItem wcriteriaItemTest1 = new MetadataService.FilterItem();
    wcriteriaItemTest1.field= ‘Contact: Allow Portal Access’;
    wcriteriaItemTest1.operation= ‘equals’;
    wcriteriaItemTest1.value= ‘true’;

    workflowRuleTest.criteriaItems = new list{wcriteriaItemTest1};

    MetadataService.WorkflowActionReference wflowactionTest1 = new MetadataService.WorkflowActionReference();
    wflowactionTest1.name = ‘Update_Test_Support_Email_Sent_date’;
    wflowactionTest1.type_x= ‘FieldUpdate’;
    workflowRuleTest.actions = new list{wflowactionTest1};

    MetadataService.AsyncResult[] results1 = service.create(new List { workflowRuleTest});

    Thanks,
    KM

  14. Hi Andrew,

    I have a requirement where in i have to get the list of components where a field has been referenced (validation rules, workflow rules, triggers and other code stuffs). Is it possible through metadata API ?

    Thanks
    NB

    • Sorry for the delayed response here, Dreamforce fallout has kept me busy, but I’m back in action now. Sadly though, while this is a common request, its currently not possible with the Salesforce Metadata API or in fact currently through their UI. It is however something I’ve been hearing more noises from them about of late, so expect to see something in a platform release soon!

  15. Oh.. Thats sad…
    Thanks a lot for your reply !!!

    Regards
    NB

  16. Hi Andrew,

    I tried to delete a field in an object through meta data API. However if the deletion fails , i could not track the reason for the failure. When i tried to check using the checkstatus method, it always returns the same both for success and failure.

    The asynresult i received is below:

    16:49:28:700 USER_DEBUG [28]|DEBUG|Error is:(AsyncResult:[apex_schema_type_info=(http://soap.sforce.com/2006/04/metadata, true, false), done=false, done_type_info=(done, http://soap.sforce.com/2006/04/metadata, null, 1, 1, false), field_order_type_info=(done, id, message, state, statusCode), id=04s90000003BeV3AAK, id_type_info=(id, http://soap.sforce.com/2006/04/metadata, null, 1, 1, false), message=null, message_type_info=(message, http://soap.sforce.com/2006/04/metadata, null, 0, 1, false), state=InProgress, state_type_info=(state, http://soap.sforce.com/2006/04/metadata, null, 1, 1, false), statusCode=null, statusCode_type_info=(statusCode, http://soap.sforce.com/2006/04/metadata, null, 0, 1, false)])

    The apex code is as follows:
    public static void deleteSingleField()
    {
    MetadataServiceCall1.MetadataPort service = createService();
    MetadataServiceCall1.CustomField customField = new MetadataServiceCall1.CustomField();
    customField.fullName = ‘B__c.Subject__c’;

    List fieldMetadata = new list();
    fieldMetadata.add(customField);
    MetadataServiceCall1.AsyncResult[] results = service.deleteMetadata(fieldMetadata);
    string asyncProcessId=string.valueof(results[0].id);
    List processIds=new list();
    processIds.add(asyncProcessId);
    checkAsyncRequest(processIds);
    }

    public static void checkAsyncRequest(String[] asyncProcessId)
    {
    // Check the status of the retrieve request
    MetadataServiceCall1.MetadataPort service = createService();
    MetadataServiceCall1.AsyncResult[] aysncResults = service.checkStatus(new String[] { asyncProcessId[0] });

    if(aysncResults[0].done) // This value is always returned as false for both success and failure
    {
    // Errors?
    if(aysncResults[0].state == ‘Error’)
    {
    // ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.Info, aysncResults[0].message));
    system.debug(‘@@1’+aysncResults[0].message);

    }
    else
    {
    //ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.Info, ‘Deployment complete’));
    system.debug(‘@@2 Deployment complete’);
    }
    }
    else
    {
    //ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.Info, ‘Deploying…’));
    system.debug(‘@@3 Deploying…’);
    }

    }

    I could delete the field successfully through this code however in case of failure i could not track it.
    Please help me out!

    Thanks in advance,
    Andrew

    • Yo need to repeat your call to checkStatus, via apex:actionPoller in VF or via Batch Apex check my last Metadata API blog post or the Readme in the GitHub repo for a link to more information and examples on this pattern.

  17. I guess i have missed your post on that …
    I read it and it definitely answered my question…

    Thanks a lot 🙂

    Regards
    NB

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 )

Facebook photo

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

Connecting to %s