Build Automated Metadata Updates for Multi-Org Deployment
Learning Objectives
Solve a Common Multi-Org Installation Issue
OK, time to solve a common problem that you’ve probably faced if you work in a multi-org environment. Let’s say you have 10 orgs, one for each of the countries in which you do business. You use managed packages to update the orgs. You’ve created a custom field for Accounts, and you want it to show up on the page layouts in all your orgs.
Ideally, you want to make these updates without creating repetitive work in the Setup UI for your admins. Let’s see how we can automate the process by using Apex Metadata API.
We walk through Apex code that retrieves the existing metadata for the Account page layout and updates it with the new field. Then we create an Apex post install script that deploys the metadata update. Instead of having to add our field to the Account page layout in each of our orgs, we simply upgrade our managed package in our orgs. Apex does all the work to add the new field behind the scenes.
Retrieve Metadata and Add a Custom Field to a Layout
Here’s some example code that retrieves the current metadata for the Account page layout. Then it uses the metadata to locate the Account Information section of the page layout. Finally, it creates a metadata layout item for the custom field and adds it to the list of metadata layout items in this section.
- Open the Developer Console by clicking the Setup gear, then Developer Console.
- In the Developer Console, click File | New | Apex Class.
- Name the class UpdatePageLayout and click OK.
- Get rid of any existing code, and copy and paste the following example code in its place.
public class UpdatePageLayout { // Add custom field to page layout public Metadata.Layout buildLayout() { // Retrieve Account layout and section List<Metadata.Metadata> layouts = Metadata.Operations.retrieve(Metadata.MetadataType.Layout, new List<String> {'Account-Account Layout'}); Metadata.Layout layoutMd = (Metadata.Layout) layouts.get(0); Metadata.LayoutSection layoutSectionToEdit = null; List<Metadata.LayoutSection> layoutSections = layoutMd.layoutSections; for (Metadata.LayoutSection section : layoutSections) { if (section.label == 'Account Information') { layoutSectionToEdit = section; break; } } // Add the field under Account info section in the left column List<Metadata.LayoutColumn> layoutColumns = layoutSectionToEdit.layoutColumns; List<Metadata.LayoutItem> layoutItems = layoutColumns.get(0).layoutItems; // Create a new layout item for the custom field Metadata.LayoutItem item = new Metadata.LayoutItem(); item.behavior = Metadata.UiBehavior.Edit; item.field = 'AMAPI__Apex_MD_API_sample_field__c'; layoutItems.add(item); return layoutMd; } }
This example uses classes and methods from Apex Metadata API, which can be found in the Metadata namespace. The Metadata.Operations.retrieve method retrieves metadata from the org synchronously. We provide a list of metadata component names to retrieve. In this case, we’re retrieving metadata for the Account page layout. The retrieve method returns a list of matching component data, represented by component classes that derive from Metadata.Metadata.
For more information on classes in the Metadata namespace, see the Apex Developer Guide.
Provide a Callback Class
Now that we’ve created the layout field metadata, we get ready to deploy the metadata to our orgs. Because the deployment is asynchronous, we provide a callback so that we are notified when the queued deployment completes. The callback class must implement the Metadata.DeployCallback interface.
From the Developer Console, create an Apex class with the name PostInstallCallback and the following code.
public class PostInstallCallback implements Metadata.DeployCallback { public void handleResult(Metadata.DeployResult result, Metadata.DeployCallbackContext context) { if (result.status == Metadata.DeployStatus.Succeeded) { // Deployment was successful, take appropriate action. System.debug('Deployment Succeeded!'); } else { // Deployment wasn’t successful, take appropriate action. System.debug('Deployment Failed!'); } } }
Create a Deployment Container
Apex Metadata API provides the Metadata.Operations.enqueueDeployment method to deploy metadata to the current org. When this method is called, the deployment request is queued for asynchronous processing.
Create an Apex class with the name DeployMetadata using the following code. This class creates a deployment container and adds the new layout metadata to it. It instantiates our callback class, which is called when the deployment completes. It then calls the Metadata.Operations.enqueueDeployment method to deploy our new metadata to the org.
public class DeployMetadata { // Create metadata container public Metadata.DeployContainer constructDeploymentRequest() { Metadata.DeployContainer container = new Metadata.DeployContainer(); // Add components to container Metadata.Layout layoutMetadata = new UpdatePageLayout().buildLayout(); container.addMetadata(layoutMetadata); return container; } // Deploy metadata public void deploy(Metadata.DeployContainer container) { // Create callback. PostInstallCallback callback = new PostInstallCallback(); // Deploy the container with the new components. Id asyncResultId = Metadata.Operations.enqueueDeployment(container, callback); } }
Deploy the Metadata Asynchronously
Now that we’ve written the Apex classes we need, we can deploy our custom metadata. We initiate the deployment from a post install script, which is an Apex class that implements the InstallHandler interface. If a package contains a post install script, the script is run automatically after the package is installed or upgraded. From the Developer Console, create the PostInstallScript Apex class.
public class PostInstallScript implements InstallHandler { // Deploy post-install metadata public void onInstall(InstallContext context) { DeployMetadata deployUtil = new DeployMetadata(); Metadata.DeployContainer container = deployUtil.constructDeploymentRequest(); deployUtil.deploy(container); } }
That’s it! When you upgrade your package in each of your orgs, this script adds your new field to all the page layouts. You (and your admins) don’t have to make a single change in the Setup UI. In the next unit, we look at a different scenario. What happens when you’ve created a custom metadata type that has to be configured and deployed into multiple orgs?
But first, give it a try. Complete the following challenge to test your knowledge.
Resources
Apex Developer Guide: Retrieving and Deploying Metadata
Apex Developer Guide: InstallHandler Interface