Update Custom Metadata Types using Metadata Api


With the introduction of Custom Metadata Types, the use of Custom Settings have been marginally reduced. Custom Metadata Types gives more flexibility in context of creating/maintaining Metadata, however when it comes to the point of updating Custom Metadata Types it involves some manual steps to be done from the Admin side which is a bit pain!

With the help of Metadata Api this process can be made more simple just by writing some Apex. Lets go ahead and put some code to make use of Metadata Api and update Custom Metadata Types.
Step 1: Create a new Custom Metadata Type "US_States" with one custom field "State__c".



Step 2: Create few records for the above Custom Metadata Type using "Manage" button.



Step 3: When we say that we update Custom Metadata Types using Metadata Api, behind the scenes it does an actual deployment and we use below class to track those deployment results. Create an Apex class "CustomMetadataCallback.apxc" using below code.

/*
* Purpose : Apex class to return custom metadata type deployment results
* 
* Developer: SFDC_Dev
* 
*/
public class CustomMetadataCallback implements Metadata.DeployCallback {
    // Method to track deploy results
    public void handleResult(Metadata.DeployResult result,
                             Metadata.DeployCallbackContext context) {
        if (result.status == Metadata.DeployStatus.Succeeded) {
            System.debug('success: '+ result);
        } else {
            System.debug('fail: '+ result);
        }
    }
}

Step 4: Create a Visualforce Page "UpdateCustomMetadata.vfp" with an extension "UpdateCustomMetadataController.apxc" using below code.

<apex:page standardController="US_States__mdt" extensions="UpdateCustomMetadataController" showHeader="false" sidebar="false" lightningStylesheets="true">
    <apex:form id="frmId">
        <apex:pageBlock title="Update Custom Metadata Type" id="pgBlkId">
            <apex:pageMessages escape="false"/>
            <apex:pageBlockButtons location="top">
                <apex:commandButton value="Update" action="{!updateValues}"/>
                <apex:commandButton value="Refresh" action="{!refresh}"/>
            </apex:pageBlockButtons>
            <apex:pageBlockSection columns="1">
                <apex:pageBlockTable value="{!UsStates}" var="val" columns="4">
                    <apex:column headerValue="State Name" value="{!val.usStates.DeveloperName}"/>
                    <apex:column headerValue="State Code" value="{!val.usStates.State__c}"/>
                    <apex:column headerValue="New Value">
                        <apex:inputText value="{!val.stVal}"/>
                    </apex:column>                                        
                </apex:pageBlockTable>
            </apex:pageBlockSection>
        </apex:pageBlock>
    </apex:form>
</apex:page>

/*
* Purpose : Controller class to update custom metadata type values
* 
* Developer: SFDC_Dev
* 
*/
public with sharing class UpdateCustomMetadataController { 
    // Getter and setters
    public List <MetaDataWrapper> lstMtdWrapper {get;set;}
    
    // Constructor
    public UpdateCustomMetadataController(ApexPages.StandardController controller) {
        
    }
    
    // Method to return all custom metadata type records
    public List<MetaDataWrapper> getUsStates() {
        lstMtdWrapper = new List<MetaDataWrapper>();
        List<reddydev2018__US_States__mdt> lstUsSts = [Select DeveloperName, Label, State__c From reddydev2018__US_States__mdt];
        for (reddydev2018__US_States__mdt iter: lstUsSts) {
            lstMtdWrapper.add(new MetaDataWrapper(iter, iter.State__c));
        }
        return lstMtdWrapper;
    }
    
    // Method to send deploy call to update custom metadata type values
    public pageReference updateValues() {
        try {        
            // Instantiate metadata deploycontainer class
            Metadata.DeployContainer mdContainer = new Metadata.DeployContainer();
            
            // Loop through each wrapper record and add it to the list for update
            for (MetaDataWrapper iter: lstMtdWrapper) {
                if (iter.usStates.State__c != iter.stVal) {
                    Metadata.CustomMetadata customMetadata = new Metadata.CustomMetadata();
                    customMetadata.fullName = 'reddydev2018__US_States__mdt' + '.' + iter.usStates.DeveloperName;
                    customMetadata.label = iter.usStates.DeveloperName;
                    
                    Metadata.CustomMetadataValue customField = new Metadata.CustomMetadataValue();
                    customField.field = 'State__c';
                    customField.value = iter.stVal;
                    
                    customMetadata.values.add(customField);                
                    mdContainer.addMetadata(customMetadata);            
                }                 
            }                    
            // Instantiate metadata deployment class
            CustomMetadataCallback callback = new CustomMetadataCallback();             
            // Send deploy call
            Id jobId = Metadata.Operations.enqueueDeployment(mdContainer, callback);                        
            // Display success message
            ApexPages.Message myMsg = new ApexPages.Message(ApexPages.Severity.confirm,'Successfully updated metadata values');
            ApexPages.addMessage(myMsg);   
            
        } catch (exception e) {
            // Display exception message
            ApexPages.Message myMsg = new ApexPages.Message(ApexPages.Severity.error,'Exception Occured: ' + e.getMessage());
            ApexPages.addMessage(myMsg);        
        }
        PageReference tempPage = ApexPages.currentPage();            
        tempPage.setRedirect(false);
        return tempPage;                                                 
    }
    
    // Method to refresh custom metadata type records values
    public void refresh() {
        getusStates();
        ApexPages.Message myMsg = new ApexPages.Message(ApexPages.Severity.confirm,'Refresh successful');
        ApexPages.addMessage(myMsg);
    }
    
    // Wrapper class to hold all custo metadata type records and new values changes
    public class MetaDataWrapper {
        public reddydev2018__US_States__mdt usStates {
            get;
            set;
        }
        public String stVal {
            get;
            set;
        }        
        public MetaDataWrapper(reddydev2018__US_States__mdt usSt, String sV) {
            this.usStates = usSt;
            this.stVal = sV;
        }
    }
}

Note: Please make sure to enable 'Available for Lighting Experience' checkbox on Page Detail for "UpdateCustomMetadata.vfp". As we are going to use this page on Lighting Home Page using Lightning App Builder.



Step 5: Now lets go ahead an pull our VF Page on Lightning Home Page using Lightning App Builder.



Finally when I go ahead and update the values from VF Page, I should be able to view updated values as below.....



Below are some resource links for Metadata Api:
  • Metadata Api Developer Guide
  • Andrew Fawcett Blog Link

  • Thank you!

    Comments

    Popular posts from this blog

    Displaying Toast Message from Modal in Lightning Components

    Lightning RecordForm - An enhanced Lightning Data Service

    Lightning Component to display dynamic sObject data