How to Update Custom Metadata Records via Apex?

Question

I created a managed package in a developer org, which includes multiple components such as custom objects, Apex classes, and custom metadata types. The custom metadata type records in the package are public, and the fields I want to modify in the subscriber org are set as Subscriber Editable.

After installing the package in a sandbox, I attempted to update the subscriber-editable fields using the Metadata API from Apex within the same managed package. However, I was unable to make any changes programmatically. The only way I could modify the fields was through the Custom Metadata Types setup page in Salesforce.

Is there a way to programmatically update the custom metadata type record fields in a subscriber org?

Answer

Yes, you can update subscriber-editable custom metadata type (CMT) records using Metadata.Operations.enqueueDeployment() in Apex. The standard updateMetadata() method of the Metadata API does not work in subscriber orgs for managed package components, even if the fields are subscriber-editable. Instead, you need to use Metadata.DeployContainer and Metadata.DeployCallback to modify the records.

Here is a working solution:

1. Create a Handler Class for Deploying Metadata

public class SalesforceOrgSettingsHandler implements Metadata.DeployCallback {
    public static Id updateRecord(Salesforce_Org_Setting__mdt cmtRecord) {
        Metadata.CustomMetadata customMetadataRecord = new Metadata.CustomMetadata();
        customMetadataRecord.fullName = cmtRecord.NamespacePrefix + '__Salesforce_Org_Setting__mdt.' + cmtRecord.QualifiedApiName;
        customMetadataRecord.label = cmtRecord.Label;
        customMetadataRecord.values = new List<Metadata.CustomMetadataValue>();

        Map<String, Object> fields = cmtRecord.getPopulatedFieldsAsMap();
        for (String fieldName : fields.keySet()) {
            if (fieldName.startsWith(cmtRecord.NamespacePrefix)) {
                Metadata.CustomMetadataValue cmtProperty = new Metadata.CustomMetadataValue();
                cmtProperty.field = fieldName;
                cmtProperty.value = cmtRecord.get(fieldName);
                customMetadataRecord.values.add(cmtProperty);
            }
        }

        Metadata.DeployContainer mdContainer = new Metadata.DeployContainer();
        mdContainer.addMetadata(customMetadataRecord);
        Id jobId;
        if (!Test.isRunningTest()) { // Deployment is not allowed in test context
            jobId = Metadata.Operations.enqueueDeployment(mdContainer, new SalesforceOrgSettingsHandler());
        }
        return jobId;
    }
    
    public void handleResult(Metadata.DeployResult result, Metadata.DeployCallbackContext context) {
        System.debug('Metadata Deployed: ' + (result.status == Metadata.DeployStatus.Succeeded));
    }
}

The SalesforceOrgSettingsHandler class is designed to update subscriber-editable Custom Metadata Type (CMT) records in a managed package using the Metadata API in Apex. It implements Metadata.DeployCallback, allowing it to handle the asynchronous deployment of metadata updates. The updateRecord method takes a Salesforce_Org_Setting__mdt record as input, clones its values, and prepares a Metadata.CustomMetadata object for deployment.

The fullName property is constructed using the record’s namespace and qualified API name. It then iterates over the populated fields of the record, checking if they belong to the package’s namespace, and adds them as Metadata.CustomMetadataValue objects to the metadata record. A Metadata.DeployContainer is created to hold this metadata update, which is then asynchronously deployed using Metadata.Operations.enqueueDeployment(), provided the code is not running in a test context.

The handleResult method, which is part of the Metadata.DeployCallback implementation, logs whether the deployment was successful. This approach ensures that updates to subscriber-editable fields in a managed package can be made programmatically while complying with Salesforce’s metadata deployment rules.

2. Update Custom Metadata Type Record from Apex

Modify your savePreferences method to update the subscriber-editable fields programmatically:

public void savePreferences() {
    Salesforce_Org_Setting__mdt cmtRecord = Salesforce_Org_Setting__mdt.getInstance('Defaults'); // Namespace not needed in same package
    cmtRecord = cmtRecord.clone(false, false, false, false);
    cmtRecord.Enable_Debug_Logs__c = String.valueOf(EnableDebugLogs);
    cmtRecord.Synchronous_Threshold1__c = String.valueOf(SyncThreshold1);
    cmtRecord.Synchronous_Threshold2__c = String.valueOf(SyncThreshold2);
    cmtRecord.Batch_Size__c = String.valueOf(BatchSize);
    Id jobId = SalesforceOrgSettingsHandler.updateRecord(cmtRecord);
    
    // Handle job status as needed
}

The savePreferences method is responsible for retrieving, modifying, and updating a Custom Metadata Type (CMT) record named “Defaults” within a managed package. It starts by calling Salesforce_Org_Setting__mdt.getInstance('Defaults') to retrieve the existing metadata record. Since this code is within the same managed package, there is no need to specify the namespace prefix.

To avoid modifying the original metadata record directly, the method clones the retrieved record using clone(false, false, false, false). This ensures that the changes are made on a copy instead of the original reference.

Next, the method updates several fields of the cloned CMT record by assigning new values. Since Custom Metadata records store all field values as text, the boolean and numeric values (EnableDebugLogs, SyncThreshold1, SyncThreshold2, and BatchSize) are converted to strings using String.valueOf().

After updating the necessary fields, the method calls SalesforceOrgSettingsHandler.updateRecord(cmtRecord), which triggers an asynchronous metadata deployment to update the record in the subscriber org. The returned jobId represents the deployment process and can be used to track its status.

Since metadata deployment is asynchronous, the method does not immediately apply the changes; instead, they are processed in the background and can be monitored in Setup > Deployment Status.

Important Considerations

Asynchronous Deployment:

Metadata.Operations.enqueueDeployment() enqueues a metadata deployment job, which is asynchronous. The update will not happen immediately, and you can monitor the process in Setup > Deployment Status.

User Permissions:

The user executing this operation in the subscriber org must have the following permissions:

Modify Metadata Through Metadata API Functions
Customize Application
These permissions cannot be included in a permission set inside the managed package; they must be granted manually in the subscriber org.

Non-Certified Package Restriction:

If your managed package is not approved by Salesforce, the subscriber org must enable Deploy Metadata from Non-Certified Package Version via Apex in Setup > Apex Settings to allow updates.

This method ensures that subscriber-editable fields in a managed package custom metadata type record can be updated programmatically within the limits of Salesforce’s metadata deployment framework.

Enroll for Salesforce Training Designed for Career Building Success

Our Salesforce course is carefully designed to give you a thorough understanding of the Salesforce platform, equipping you with the essential skills to succeed in the CRM industry. The program covers core modules like Salesforce Admin, Developer, and AI, combining theoretical concepts with hands-on practice. By engaging in real-world projects and interactive assignments, you’ll develop the expertise to solve business challenges using Salesforce solutions. Our expert instructors ensure you gain both technical proficiency and industry-relevant knowledge to excel in the Salesforce ecosystem.

In addition to technical training, our Salesforce training in India provides personalized mentorship, certification support, and interview preparation to enhance your career prospects. You’ll get access to extensive study resources, hands-on project experience, and dedicated guidance throughout the course. By completion, you’ll be well-prepared for certification exams and possess the problem-solving skills that employers look for. Begin your Salesforce journey today and unlock exciting career opportunities. Sign up for a Free Demo now!



0
Would love your thoughts, please comment.x
()
x