Updating Subscriber-Editable Custom Metadata Fields via Apex

Updating Subscriber-Editable Custom Metadata Fields via Apex

On January 18, 2025, Posted by , In Salesforce Technical Questions, With Comments Off on Updating Subscriber-Editable Custom Metadata Fields via Apex
Salesforce Technical Questions with Answers Apex, LWC, Marketing Cloud

Question:

I have created a managed package in a Developer org containing custom objects, Apex classes, and custom metadata types (CMT). The custom metadata type records are set as public, and the fields I need to modify are marked as “Subscriber Editable.” After installing the package in a subscriber sandbox, I tried updating these fields using the Metadata API from Apex code within the same managed package. However, the updates failed, and I can only modify these fields through the setup page in the Salesforce UI.
Is there a way to modify subscriber-editable custom metadata type record fields programmatically in the subscriber org using Apex code?

CRS Info Solutions offers industry-leading Salesforce online training with real-time projects, certification guidance, interview coaching, and a practical approach to make you job-ready.

Answer:

Yes, it is possible to modify subscriber-editable fields of custom metadata type (CMT) records in a subscriber org using Apex code, but this involves specific considerations.
Here’s how you can do it effectively:

1.Using the Metadata.Operations.enqueueDeployment Method:

The Metadata.Operations.enqueueDeployment method enables you to update CMT records programmatically. This approach is asynchronous, meaning the changes are not applied immediately. You need to build a Metadata.DeployContainer and populate it with the desired updates.
Here is a detailed implementation:

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 cannot be enqueued during tests
            jobId = Metadata.Operations.enqueueDeployment(mdContainer, new SalesforceOrgSettingsHandler());
        }
        return jobId;
    }
    
    public void handleResult(Metadata.DeployResult result, Metadata.DeployCallbackContext context) {
        System.debug('Metadata Deployment Status: ' + (result.status == Metadata.DeployStatus.Succeeded));
    }
}

Explanation:

  • Purpose: This class handles updating a custom metadata type (CMT) record via the Metadata.Operations.enqueueDeployment method. The deployment operation is asynchronous and needs to be enqueued.
  • Key Steps:
    • A Metadata.CustomMetadata object is created and populated with the full name, label, and values of the fields to be updated.
    • The getPopulatedFieldsAsMap() method fetches all populated fields from the provided Salesforce_Org_Setting__mdt record. This ensures you only update fields with actual values.
    • Each field is mapped to a Metadata.CustomMetadataValue object, which holds the field name and value for the update.
    • The Metadata.DeployContainer object acts as a container for the custom metadata changes and is passed to the enqueueDeployment method, initiating the deployment.
    • The handleResult method processes the deployment result, logging whether it succeeded or failed.

To update your preferences:

public void savePreferences() {
    Salesforce_Org_Setting__mdt cmtRecord = Salesforce_Org_Setting__mdt.getInstance('Defaults');
    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);
}

Explanation:

  • Purpose: This method prepares a CMT record with updated values and triggers the deployment to save the changes.
  • Key Steps:
    • The Salesforce_Org_Setting__mdt.getInstance('Defaults') retrieves the default CMT record.
    • The clone method creates a copy of the record, ensuring no reference conflicts with the original record.
    • Subscriber-editable fields (Enable_Debug_Logs__c, Synchronous_Threshold1__c, etc.) are updated with new values.
    • The updated record is passed to the updateRecord method of the SalesforceOrgSettingsHandler class, which handles the deployment.

      This approach requires the user in the subscriber org to have the following system permissions:

  • Modify Metadata Through Metadata API Functions
  • Customize Application
  • If your managed package is not Salesforce-certified, the subscriber org must enable Deploy Metadata from Non-Certified Package Version via Apex from Setup > Apex Settings.

2.Alternative Using the updateMetadata Method:

You can also use the updateMetadata method from the MetadataService class. This method is synchronous but has limitations in terms of flexibility.
Here’s an example:

public void savePreferences() {
    try {
        MetadataService.MetadataPort oMetadataPort = MetadataServiceExamples.createService();
        MetadataService.CustomMetadata oCustomMetadata = new MetadataService.CustomMetadata();
        oCustomMetadata.fullName = 'MyNamespace__Salesforce_Org_Setting__mdt.Defaults';
        oCustomMetadata.label = 'Defaults';
        oCustomMetadata.values = new List<MetadataService.CustomMetadataValue>{
            createCustomMetadataValue('MyNamespace__Enable_Debug_Logs__c', String.valueOf(EnableDebugLogs)),
            createCustomMetadataValue('MyNamespace__Synchronous_Threshold1__c', String.valueOf(SyncThreshold1)),
            createCustomMetadataValue('MyNamespace__Synchronous_Threshold2__c', String.valueOf(SyncThreshold2)),
            createCustomMetadataValue('MyNamespace__Batch_Size__c', String.valueOf(BatchSize))
        };

        List<MetadataService.SaveResult> results = oMetadataPort.updateMetadata(new MetadataService.Metadata[] { oCustomMetadata });
        if (results[0].success) {
            System.debug('Metadata Updated Successfully');
        } else {
            System.debug('Error: ' + results[0].errors[0].message);
        }
    } catch (Exception e) {
        System.debug('Error saving preferences: ' + e.getMessage());
    }
}

Explanation:

  • Purpose: This method uses the synchronous MetadataService API to update a CMT record directly.
  • Key Steps:
    • A MetadataService.MetadataPort object is created to interface with the Metadata API.
    • A MetadataService.CustomMetadata object is populated with the record’s full name, label, and updated values.
    • The updateMetadata method sends the updated metadata to Salesforce for processing. The result is checked for success or errors.
  • Limitations:
    Unlike the enqueueDeployment method, this approach is synchronous and suitable for simpler updates but less efficient for handling multiple records.
  • Important Notes:
  • These methods depend on the subscriber org having the required permissions.
  • The Metadata API actions will appear under Setup > Deployment Status in the subscriber org.
  • Use the asynchronous approach for complex updates or when handling multiple records.

Summing up:

Updating subscriber-editable custom metadata fields via Apex is a nuanced process that involves leveraging the Metadata API to modify records programmatically. While fields marked as subscriber-editable allow updates, direct changes through Apex require permissions like “Modify Metadata Through Metadata API Functions” and the proper use of tools such as Metadata.Operations.enqueueDeployment for asynchronous updates or MetadataService.updateMetadata for synchronous ones.

These methods enable dynamic updates to metadata records, ensuring flexibility and customization in managed packages, but they come with requirements like deployment permissions in the subscriber org. By understanding the appropriate approach and limitations, developers can efficiently update metadata while maintaining compliance with Salesforce’s managed package restrictions.

Jumpstart Your Salesforce Career with Training in Pune

Ready to unlock new career opportunities with Salesforce? CRS Info Solutions offers Salesforce training in Pune, designed to provide you with the skills and knowledge needed to excel in the Salesforce ecosystem. Our courses cover all critical areas, including Salesforce Admin, Developer, and AI modules, with a focus on real-time project-based learning. Led by industry professionals, our training ensures you gain practical experience that prepares you for the challenges of the real world. Whether you’re new to Salesforce or looking to advance your skills, our structured approach helps you become job-ready.

Our Salesforce training in Pune emphasizes hands-on learning, personalized mentorship, and comprehensive resources. You’ll receive detailed class notes, expert guidance for certification, and interview preparation to ensure you’re ready to step into your next Salesforce role.

Don’t miss the chance to accelerate your career—enroll today and join our free demo session to kickstart your Salesforce journey!

Comments are closed.