Quantcast
Channel: Invocable Methods – Andy in the Cloud
Viewing all articles
Browse latest Browse all 7

Automating Org Setup via Process Builder and Metadata API

$
0
0

ApexPBLikeAs those of you following my blog will know i’ve been exploring Invocable Methods. Having submitted a session abstract to this years Salesforce1 World Tour London event and had it selected. I set about building out some more use cases to demonstrate them. This blog goes into more detail into one of two i had the pleasure of presenting alongside fellow Force.com MVP Simon Goodyear. Simon presented an awesome Twillio integration, allowing the sending of SMS messages from Process Builder! You can view the slide deck here.

In the end the use case I developed for the session attempts to fill a current Admin gap i came across on Ideas Exchange. The idea to provide Universal Picklists has been around a while and looks like its finally getting some attention given some encouraging comments by the Product Manager recently. Still please help further by up voting!

In the meantime, here is my part code and part declarative solution to the problem via Process Builder! Which also gives you a template for perhaps creating other Invocable Methods around the Apex Metadata API.

What problem do Universal Picklists solve?

As you’ll read on the Idea Exchange posting, often as Admins it can be time consuming to maintain picklist entries when they are effectively duplicated across several fields and objects in a large Salesforce organisation. Process Builder is designed to allow us to automate business processes and make our day to day lives easier on the platform, so why can it not help automate Admin processes under the Setup menu as well?

Custom VF Page vs Invocable Method?

Now i know i could have done this all via a dedicated Visualforce page and controller, which presents the user with a list of objects and Picklist fields to select. And then leverage the Apex Metadata API to update the fields accordingly, and maybe this might still be a good approach. Though as i said in my earlier blog, even if you do build fully custom UI’s, you really should give some thought to exposing Invocable Methods as well…

Anyway i wanted to see how well Metadata API would work when exposed as an Invocable Method and thus give Admins the power to build their own automated processes rather than depend completely on an Apex/VF developer now and in the future. Using Invocable Methods is an ideal way to get the best out of both the clicks and code worlds without having to sacrifice one over the other.

Introducing the Add Picklist Item Action Invocable Method

The method itself is pretty small as it delegates its work to an Apex job, this is because the context Process Builder executes your method in, is within a Trigger context. Thus due to the Apex Metadata API being a wrapper around SOAP API and callouts not being permitted in Triggers a job is used.

public with sharing class AddPicklistItemAction {
    
	public class Request {
		@InvocableVariable(label='Picklist Item' required=true)
		public String pickListItem;
		@InvocableVariable(label='Fully Qualified Field Name' required=true)
		public String customFieldName;
	}
    
	@InvocableMethod(
		label='Adds picklist list items to given custom fields'
		description='Will submit a request to add the given picklist items to the given custom fields')
    public static void addPickListItem(List<Request> requests) {
        // Must enqueue the work as Apex Metadata API calls our HTTP callouts
        System.enqueueJob(new DoWork(UserInfo.getSessionId(), requests));
    }
}

As per best practices i explain in the session and my previous blog, i’m using the annotations to help make the method as easy to use as possible for the Admin, ideally without to much help. The resulting UI in Process Builder looks like this (I will show the rest of the Process Builder setup in a moment).

PickListInvocableMethod

Also per best practices, the method implementation must be bulkified. Fortunately the Metadata API itself is also designed to support bulk requests. The following code determines the Custom Fields to readMetadata, bulks the pick list items to add together and calls the Metadata API updateMetadata method.

    public class DoWork implements System.Queueable, Database.AllowsCallouts {
        
        private final String sessionId;
        
        private final List<Request> requests;
        
        public DoWork(String sessionId, List<Request> requests) {
            this.sessionId = sessionId;
            this.requests = requests;
        }
        
        public void execute(System.QueueableContext ctx) {
            
            // Metadata Service
            MetadataService.MetadataPort service = new MetadataService.MetadataPort();
            service.endpoint_x = service.endpoint_x.replace('http:', 'https:'); // Workaround to Apex MD API bug in Batch
            service.SessionHeader = new MetadataService.SessionHeader_element();
            service.SessionHeader.sessionId = sessionId;

            // Custom Fields
            Map<String, List<String>> newPickListItemsByCustomField = new Map<String, List<String>>();
            for(Request request : requests) {
				List<String> pickListItems = newPickListItemsByCustomField.get(request.customFieldName);
				if(pickListItems==null)
                    newPickListItemsByCustomField.put(request.customFieldName, pickListItems = new List<String>()); 
                pickListItems.add(request.pickListItem);
            }
            
            // Read Custom Fields
            List<MetadataService.CustomField> customFields = 
                (List<MetadataService.CustomField>) service.readMetadata('CustomField', 
                    new List<String>(newPickListItemsByCustomField.keySet())).getRecords();
            
            // Add pick list values
            for(MetadataService.CustomField customField : customFields) {
                List<String> pickListItems = newPickListItemsByCustomField.get(customField.fullName);
                for(String pickListItem : pickListItems) {
                    metadataservice.PicklistValue newPicklist = new metadataservice.PicklistValue();
                    newPicklist.fullName = pickListItem;
                    newPicklist.default_x=false;
                    customField.picklist.picklistValues.add(newPicklist);	                                        
                }
            }
                
            // Update Custom Fields and process an failed saves
            List<String> errors = new List<String>();
            for(MetadataService.SaveResult saveResult : service.updateMetadata(customFields)) {
                try {
                    handleSaveResults(saveResult);
                } catch (Exception e) {
                    errors.add(saveResult.fullName + ' : ' + e.getMessage());
                }
            }
            if(errors.size()>0) 
                throw new AddPicklistItemActionException(String.join(errors, '/n'));
        }
}

Since this is happening in the background we need to think about our batch best practices here as well. How will we report errors? Currently error reporting is handled by the platform in this code sample so will appear on the Apex Jobs page under Setup, but could be routed via an email or a Chatter post perhaps.

Doing the Clicks not Code bit…

Once this method is written and deployed, its over to clicks not code to finish the work!

Here the Admin can choose how to use the method. Which actually could be via a Visual Flow UI they have built if they preferred a more wizard UI based experience. The Admin decides to create a new Custom Object who’s records will represent the Univeral Picklist values. Whenever a new record is added to this object Process Builder will respond and perform actions that invoke the above Invocable Method to add the Picklist Items to each Picklist field as desired…

PickListsAndProcessBuilder

The full Process Builder process created by the Admin is shown below, leveraging the Apex Action type to call the Invocable Method wrapping the Apex Metadata API defined above. Note that you can call multiple actions per event to be processed by Process Builder.

PickListProcessBuilderProcess

Implementation Note: Having implemented the Invocable Method as i have done, this does require multiple invocations of it per Action (resulting in a Apex job for each). Another option would have been to pass a list of Custom Fields to it, however the Process Builder UI does not yet support this use case. The alternative might be then to ask the Admin to send a comma separated list in the single Action. Though this approach does erode the ease of use and makes it harder to manage. On balance given the likely low volume of such requests i’d personally be inclined in this case to leave this one as is, but interested to see what others think on this…

Summary

I believe Invocable Methods are a great way for any developer, be they developing code in a sandbox or for a AppExchange package to provide an even greater way for their solution to not only sit on the platform, but sit within it, and extend great tools such as Process Builder and Visual Flow.

As a community, i wonder if we can start to create a library of Invocable Methods of various kinds and perhaps provide a means to package or easily deploy them to Admins. How about an Invocable Method that exposes a means to automate Layout edits? If your interested please let me know your thoughts on this! In the meantime you can see the full source code for this blog here.

Finally, the slide deck does contain a few more links and resources so be sure to check it out also!

P.S. I also had another use case to show how to develop a kind of custom formula function approach to using Invocable Methods with Process Builder, however this hit a wall with a platform issue at the time. Which I now have a workaround for, so will likely blog about this further in the future…



Viewing all articles
Browse latest Browse all 7

Trending Articles