Quantcast
Channel: Andy in the Cloud
Viewing all articles
Browse latest Browse all 119

Introducing Custom Metadata Services

$
0
0

mdtlcWhen you have more complex custom metadata needs or multiple custom metadata types, the UI requirements for users to manage records may also need to be more sophisticated. For example if you wish to build a setup wizard or type of visual editor. Much like you see with the Flow or Process Builder tools. So if you find you want to build your own UI for custom metadata records what is involved?

The native Apex Metadata API allows for Layout metadata as well as Custom Metadata records to be manipulated directly in Apex, without the traditional need for callouts or elevated permissions. Due to its generic API design, the data types for custom metadata records are represented as generic name/value pairs. Because of this it does require a bit more coding than you might expect to update records. Certainly compared to the SObject and DML oriented experience you get when working with Custom Object records in Apex. There is also the fact that it is async only, requiring careful transfer of results received via callbacks back to the user.

Custom Metadata Services (CMS) is a small library created to wrap the native Apex Metadata API to leverage the native SObject types for custom metadata in a more DML orientated way. It also aims to simplify the handling of the async results when developing clients in Lightning, inspired in this case by Lightning Data Services.

Updating records in Apex

When you create a Custom Metadata Type under the Setup menu, the platform automatically generates a corresponding SObject type you can use within Apex. This  so that you can query those records using regular SOQL.

List<WidgetPreset__mdt> records =
    [select
        DeveloperName, Label,
        DefaultNotification__c, Alias__c
      from
        WidgetPreset__mdt];

Using the native SObjectType and the related field references (SObjectField tokens) is preferable to using String literals from a referential integrity perspective and also namespace management for packaged code. So CMS uses these whenever possible. The following code will upsert a Custom Metadata record.

    String deployId = CustomMetadata.Operations
        .callback(
            // Simple callback that outputs to debug
            new DebugCallback())
        .enqueueUpsertRecords(
            // Custom Metadata object type
            WidgetPreset__mdt.getSObjectType(),
            new List<Map<SObjectField, Object>> {
                // Custom Metadata record
                new Map<SObjectField, Object> {
                    WidgetPreset__mdt.DeveloperName => 'BluetoohToothbrush',
                    WidgetPreset__mdt.Label => 'Bluetooh Toothbrush',
                    WidgetPreset__mdt.DefaultNotification__c => 'Good day!',
                    WidgetPreset__mdt.Alias__c => 'wdbtt' } } )
        .deployId;
  • The only operation currently supported is an upsert. The use of this terminology reflects the fact that the native Metadata API deploys a list of records that will either insert or update records.
  • Because its not possible to assign values directly to __mdt SObject fields, the above code uses an Apex map that references the fields via their SObjectField tokens.

The native Apex Metadata API uses a background job to update the records so the results are not know immediately to the calling code. CMS tailors the DeployCallback interface with its own base class which is more orientated around custom metadata record results. CMS also uses its own callback ID rather than the native one, allowing the client to more easily identify the corresponding callback.

    public class DebugCallback extends CustomMetadata.SaveResultCallback {
        public override void handleResult(String deployId, List<SaveRecordResult> results) {
            System.debug('Deploy Id is ' + deployId);
            for(SaveRecordResult result : results) {
                System.debug('Fullname of custom metadata reocord is ' + result.fullName);
                System.debug('Success is ' + result.success);
                if(!result.success) {
                    System.debug('Message is' + result.message);
                }
            }
        }
    }

The callback method also supports the ability to send a Platform Event instead. This is preferable in cases where you are building UI’s, since the UI can listen for the callback directly. The Metadata Deployment platform event object is included in the library.

    String deployId = CustomMetadata.Operations
        .callback(
            // Platform event for deploy status
            MetadataDeployment__e.getSObjectType(),
            MetadataDeployment__e.DeploymentId__c,
            MetadataDeployment__e.Result__c)
        .enqueueUpsertRecords(
            // Custom Metadata object type
            WidgetPreset__mdt.getSObjectType(),
            new List<Map<SObjectField, Object>> {
                // Custom Metadata record
                new Map<SObjectField, Object> {
                    WidgetPreset__mdt.DeveloperName => 'BluetoohToothbrush',
                    WidgetPreset__mdt.Label => 'Bluetooh Toothbrush',
                    WidgetPreset__mdt.DefaultNotification__c => 'Good day!',
                    WidgetPreset__mdt.Alias__c => 'wdbtt' } } )
        .deployId;

Building a Lightning UI

Here is a simple UI built with CMS, to demonstrate loading, saving and error handling.

mdtlc

When building UIs you can utilise the Apex code above from your server side controller logic. Though as an alternative, CMS includes a Lightning component for use within client side controllers. The design of this component follows that of the force:recordData component used for managing Custom Object records. It is currently aimed at single record retrieval and updates. Here is the markup for the above component.

lcmdc

The metadataRecordData component has its own Apex controller leveraging the CMS Apex API above and then listens for the MetadataDeployment__e event (included). This is transparent to the consumers of the component. The controller code interacts with the saveRecord method and metadataRecordDataResult event.

lcmdctrl

Building a Visualforce UI

It is possible to include Lightning Components in Visualforce pages using Lightning Out and thus reuse the above code in both Classic and Lightning Experience.

However if you still prefer to develop using Visualforce or simply have a number of pages you’d rather continue to develop for a while. You can still utilise the Platform Event mechanism using the client side library called cometd. The sample code for the page below can be found in the GitHub repo.

vfcms

Summary

I plan on utilising CMS to update the rollup tools UI in the future and avoid the need for callouts (and related permissions) when using its UI. In full disclosure I would also classify the library as alpha at this stage, its still needs a little more refinement and robustness adding. It is open source, so free to join me in fleshing it out further!



Viewing all articles
Browse latest Browse all 119

Trending Articles