The Metadata API is an effective tool in Salesforce’s developer arsenal, designed for accessing and handling changes across different metadata components simultaneously. It also serves as a carrier for many bulk operations, mainly for migrating metadata changes between distinct Salesforce orgs.
This tutorial will walk you through the steps to getting acquainted with the Metadata API, setting it up, and familiarizing yourself with its various capabilities by building a showcasing project.
What Is Metadata?
Before digging into the realm of the Salesforce Metadata API, let’s first define Metadata.
Metadata represents data about other data. It is composed of structured information that describes and contextualizes the data it references. Moreover, Metadata helps in understanding the relevance of the underlying data and provides valuable guidance on how to use it.
Salesforce Metadata comprises a collection of attributes, relationships, layouts, and configurations that define how your platform instance behaves.
For illustration purposes, refer to the images below that show the metadata XML schema of a custom object named “Employee” that we will consider in the next section and its respective definition within Salesforce:
Exploring Metadata API
The Metadata API offers numerous advantages such as the ability to deploy a set of changes across multiple environments and replicate these changes across unrelated orgs.
In this context, the first thing that springs to mind is this – why not use a native Salesforce feature like Change sets to move metadata customizations?
The answer is that although change sets are widely used for deploying metadata changes between orgs, it’s not a one-size-fits-all – they have notable gaps and limitations that Metadata API allows to overcome, such as:
- Transferring data using change sets is only allowed between orgs that are affiliated with a production org.
- Selecting the metadata components to include in a change set can be cumbersome as this process can involve many clicks.
- There is a lack of partial deployment.
- It can be difficult to gain vital insights into the underlying components of a Salesforce instance such as custom objects, custom fields, and static resources among others.
- Managing large-scale changes to the org’s metadata and reducing manual interactions – knowing that promoting some changes throughout the Salesforce interface – can be sometimes tedious and time-consuming. For example, adding a custom field to the Account object often entails the following steps:
- Navigating to the Account object.
- Clicking on the new Custom Field.
- Choosing the field type and entering other related properties.
- Establishing field-level security.
- Editing the object-related page layouts to include the new field.
Accomplishing the above steps can be automated using a simple program leveraging the Metadata API resources.
Covered Metadata Components
The Metadata API covers a wide range of Salesforce metadata components, including
- Custom objects
- Custom fields
- Picklists
- Profiles, permission sets, and permission set groups
- Apex Classes
- Email templates
For a complete list of supported components, take a look here.
Deployment Procedure
Salesforce exposes the Metadata API via REST resources. The latter have not yet had an APEX native API, and there are no means to manipulate metadata directly through APEX.
Therefore to cater to this issue and while considering the growing number of APEX developers, an APEX wrapper was released to facilitate and simplify consuming these API services.
The deployment process of the Apex wrapper of the Metadata API consists of the steps mentioned in the attached document entitled: “Deployment Procedure”.
Real-Life Scenario
Consider a scenario that involves dynamically creating a custom object for hosting employees’ data while employing the Metadata API.
It’s worth noting that custom objects serve to capture data that doesn’t fit into standard Salesforce objects.
Such a scenario necessitates creating multiple Metadata components and granting respective access rights to these components for example:
Metadata Component | Name |
---|---|
Custom Object | Employee_c |
Custom Field | Company__c |
Custom Field | Active__c |
The steps involved in this exercise listed below aim to demystify the core functions of the Metadata API. So roll up your sleeves, fire up your Salesforce developer console, and let’s head into coding. To put it simply, create a new class named “MetadataController” and incorporate the following functions within it:
- Initialize the Metadata service:
- Refer to “
MetadataController.createMetadataService
”.
- Refer to “
- Create a custom object named “Employee”:
- Refer to “
MetadataController.createCustomObject
”.
- Refer to “
- Add a text custom field named “Company” to the previously created custom object:
- Refer to “
MetadataController.createCustomTextField
”.
- Refer to “
- Add a checkbox custom field named “Active” to the previously created custom object:
- Refer to “
MetadataController.createCustomCheckBoxField
”.
- Refer to “
- Create a permission set named “PS_Employee_Company_Active” while granting read and edit access on the newly created custom field to the permission set:
- Refer to “
MetadataController.createPSFieldLevelSecurity
”.
- Refer to “
- Assign the permission set to the connected user:
- Refer to “
MetadataController.assignPermissionSetToUser
”.
- Refer to “
At last, the resulting class should look like the following:
public class MetadataController {
private static MetadataService.MetadataPort metadataService;
/**
* Initializing the Metadata Service.
*/
private static MetadataService.MetadataPort createMetadataService() {
MetadataService.MetadataPort service = new MetadataService.MetadataPort();
service.SessionHeader = new MetadataService.SessionHeader_element();
service.SessionHeader.sessionId = UserInfo.getSessionId();
return service;
}
/**
* Creating A Custom Object.
*/
private static void createCustomObject(String objAPIName
,String objLabel
,String objPluralLabel
) {
MetadataService.CustomObject customObject = new MetadataService.CustomObject();
customObject.fullName = objAPIName;
customObject.label = objLabel;
customObject.pluralLabel = objPluralLabel;
customObject.nameField = new MetadataService.CustomField();
customObject.nameField.type_x = 'Text';
customObject.nameField.label = 'Name';
customObject.deploymentStatus = 'Deployed';
customObject.sharingModel = 'ReadWrite';
List<MetadataService.SaveResult> results = metadataService.createMetadata(
new MetadataService.Metadata[] { customObject }
);
}
/**
* Creating A Custom Text Field.
*/
private static void createCustomTextField(String objAPIName
,String fieldAPIName
,String fieldLabel
,Integer fieldSize) {
MetadataService.CustomField cf = new MetadataService.CustomField();
cf.fullName = objAPIName + '.' + fieldAPIName;
cf.label = fieldLabel;
cf.type_x = 'Text';
cf.length = fieldSize;
List<MetadataService.SaveResult> results = metadataService.createMetadata(
new MetadataService.Metadata[] { cf }
);
}
/**
* Creating A Custom Checkbox Field.
*/
private static void createCustomCheckBoxField(String objAPIName
,String fieldAPIName
,String fieldLabel
,String fieldDefaultValue) {
MetadataService.CustomField cf = new MetadataService.CustomField();
cf.fullName = objAPIName + '.' + fieldAPIName;
cf.label = fieldLabel;
cf.type_x = 'Checkbox';
cf.defaultvalue = fieldDefaultValue;
List<MetadataService.SaveResult> results = metadataService.createMetadata(
new MetadataService.Metadata[] { cf }
);
}
/**
* Creating a Permission Set
* Granting readable and editable access rights on the custom fields to the permission set.
*/
public static void createPSFieldLevelSecurity(String permissionSetName
, String firstFieldAPIName
, String secondFieldAPIName
)
{
List<MetadataService.PermissionSetFieldPermissions> psfpList = new List<MetadataService.PermissionSetFieldPermissions>();
MetadataService.PermissionSetFieldPermissions psfp1 = new MetadataService.PermissionSetFieldPermissions();
psfp1.field = firstFieldAPIName;
psfp1.readable=true;
psfp1.editable=true;
psfpList.add(psfp1);
MetadataService.PermissionSetFieldPermissions psfp2 = new MetadataService.PermissionSetFieldPermissions();
psfp2.field = secondFieldAPIName;
psfp2.readable=true;
psfp2.editable=true;
psfpList.add(psfp2);
MetadataService.PermissionSet ps = new MetadataService.PermissionSet();
ps.fullName = permissionSetName;
ps.label = permissionSetName;
ps.fieldPermissions = psfpList;
ps.description = 'Permission Set - Grant Readable and Editable Access To Fields';
List<MetadataService.SaveResult> results =
metadataService.createMetadata(
new MetadataService.Metadata[] { ps });
}
/**
* Assigning a Permission Set to a User
*/
private static void assignPermissionSetToUser(String permissionSetName
,String userId
) {
PermissionSet ps = [SELECT Id FROM PermissionSet WHERE Name = :permissionSetName];
PermissionSetAssignment psa = new PermissionSetAssignment();
psa.PermissionSetId = ps.Id;
psa.AssigneeId = userId;
insert psa;
}
/*
* Main Process
*/
public static void runProcess() {
metadataService = createMetadataService();
createCustomObject('Employee__c','Employee','Employees');
createCustomTextField('Employee__c','Company__c','Company',255);
createCustomCheckBoxField('Employee__c','Active__c','Active','True');
//Creating a Permission Set and assigning it to the current user.
createPSFieldLevelSecurity('PS_Employee_Company_Active','Employee__c.Company__c','Employee__c.Active__c');
assignPermissionSetToUser('PS_Employee_Company_Active',UserInfo.getUserId());
}
}
Note: the user executing this process should have adequate rights or be a member of the “System Administrator” profile.
Summary
The Metadata API plays a significant role in tailoring Salesforce to meet peculiar business needs in a streamlined and well-organized manner. It allows you to extend the standard Salesforce features beyond the declarative point-and-click configurations.