October 30, 2018
Deploying Custom Metadata Records from Apex Code
For over 6 years we have been working on an internal Salesforce customizations project for one of the clients. Recently, after certain restructuring of the client’s enterprise, there emerged a number of integration tasks. One of those comprises client’s customer’s data synchronization between Salesforce and a partnering company’s external system, including the customer’s data for all subsidiaries. In order to find the customer’s data in the external system, some complicated business logic was involved which required an external system identifier for the corresponding account country or state. So, this integration implementation required that some mappings be established between a country or a state code and the external system identifiers. One of the subsidiaries, also using Salesforce, implemented these mappings as custom object records. When asked about their preferred choice of custom object records over custom metadata records, they told me that they tried to avoid having too much metadata downloaded on a project initialization, in the Integrated Development Environment. This argument didn’t sound like a valid reason to me. While if they had an ETL implementation to load those records, then that would have been a valid reason to use Custom Object records.
Since custom objects records are not deployable, these configuration mappings cannot be migrated from a sandbox to a production environment during deployment if they are stored in Custom Object. Because of this, a decision to convert those records, from Custom Object to Custom Metadata records, during adoption of their implementation was made and I delegated this task to a Junior Developer. To my great surprise, for two days he converted the only record and preferred to focus on the other tasks I had given him. He actually wasn’t aware of the custom metadata records deployment feature and was too shy to ask how this task could be automatized. Imagine, he thought that I expected him to convert those 5,000 records manually!
I really felt shocked that such a feature, introduced more than a year and a half ago, was not known among Salesforce Developers, which is why I decided to write this article.
The Salesforce Summer 17 Release introduced the Operations class in Metadata namespace, and it allows a developer to retrieve and deploy custom metadata records and layouts. Well, to retrieve custom metadata records, developers can just use a SOQL query, so this part is not a breakthrough. Retrieval and deployment of layouts are most commonly used by package developers, so for developers who do not design packages, it is not really useful. However, deployment of metadata records is really a thing. First of all, if one ever needs to, one can implement a deployment of metadata records from UI to overcome the inability to insert custom metadata records. Actually, there is a customization application based on Salesforce, and provided by the internal Salesforce team, which supports the uploading of Custom Metadata Records which is called Custom Metadata Loader. However, this package’s page also uses Metadata API under the hood instead of this feature.
So I’ll focus here only on the case when it is just needed to deploy custom metadata records to sandbox for conversion purposes.
Let’s assume you have a legacy project and you aim to convert some custom object records, or custom setting records, to custom metadata. In such a case, you can use a enqueueDeployment method of Operations class to deploy the converted custom metadata records to sandbox, and later use an Ant Migration Tool to deploy those custom metadata records from sandbox to production. Previously, developers had to use the complex Andrew Fawcett library which utilized Metadata API under the hood; now things have become easier for similar cases. The only drawback of the Operations class deployment method is the inability to delete custom metadata records. However, when you have to convert an existing custom object, or custom settings record, to custom metadata, you don’t really need to delete any custom metadata records, so the available functionality completely covers the described use case.
There are some peculiarities about Metadata namespace.
In order to populate a custom field on a Custom Metadata record, one has to instantiate a CustomMetadataValue model and then add it to custom metadata record values. However, to populate standard field on a Custom Metadata record one has to populate the label and fullName attributes directly on a Custom Metadata record without modifying values. Label and fullName attributes correspond to MasterLabel and DeveloperName standard fields even though their names differ.
Deployment may either create a new custom metadata record or update existing custom metadata records, depending on the uniqueness of the fullName attribute. If there is a custom metadata record with a given DeveloperName, then that particular record will be updated with new values during deployment but if such a record doesn’t exist, then a new record will be created during the deployment process.
Also, the label attribute is, in fact, required even though it is not populated in the example from documentation. I spent some time trying to figure this out when I was attempting to deploy custom metadata records by Apex code, for the very first time.
Also, when you need to populate a Custom Metadata relationship, you need to use DeveloperName from the corresponding parent records instead of an identifier, which might seem odd since everywhere else in Apex you either use Salesforce Id or External Id to populate relationships. The same applies when you need to populate a Entity or Field Entity relationship, the DeveloperName from the corresponding Entity or Field should be used instead of Ids.
There are some undocumented limitations on a number of custom metadata records which can be deployed by one call of an enqueueDeployment method of Operations class. The actual number depends on the data included in the custom metadata records, and in my case, I was able to deploy around 1,488 custom metadata records at one time while trying to insert or update 1,489 records yielded from a Salesforce System UnexpectedException Error.
Splitting custom metadata records into chunks and invoking an enqueueDeployment method several times helps to deal with the issue.
One more important notice for package developers.
When you develop a managed package, only certified packages are allowed to use this feature of deploying custom metadata records from Apex code.
There is “Deploy Metadata from Non-Certified Package Versions via Apex” checkbox setting, which enables beta packages to perform a custom metadata records deployment from Apex code. This checkbox can be found in Setup \ Build \ Develop \ Apex Settings menu in the setup configuration.
So if you need to test the beta version of your developed managed package before passing a security review, you will have to check this checkbox and save your settings on every organization where you install a beta version of your package.
As we can conclude now, the ability to deploy customization data directly from Apex code is a really great and astonishing feature which has been available since version 40 of Salesforce API. We have considered here several use cases where this might be needed. Sometimes this can be useful for developing an interface to deploy custom metadata records from user experience, or this can be much more useful and beneficial if you ever need to convert your existing customization data, stored in Custom Objects or Custom Settings, in order to be able to include them into deployment scripts.