Having just completed some improvements to the Apex Metadata API to support creating, reading, updating and deleting Flows. I wanted to give it a bit more of test drive than the usual Apex examples i write. So i started wondering if there was something i could do as a developer armed with this API to help with the process of configuring and managing Flows? The following is basic example of updating a Flow to add a Screen Step….
// Read Flow MetadataService.Flow flow = (MetadataService.Flow) service.readMetadata('Flow', new String[] { 'NewFlow-1' }).getRecords()[0]; // Add a new step MetadataService.FlowScreen flowScreen = new MetadataService.FlowScreen(); flowScreen.name = 'NewScreen'; flowScreen.label = 'New Screen'; flowScreen.locationX = 100; flowScreen.locationY = 100; flowScreen.allowBack = true; flowScreen.allowFinish = true; flowScreen.fields = new List<MetadataService.FlowScreenField>(); flowScreen.fields.add(new MetadataService.FlowScreenField()); flowScreen.fields[0].name = 'Test_Box'; flowScreen.fields[0].dataType = 'String'; flowScreen.fields[0].fieldText = 'Test Box'; flowScreen.fields[0].fieldType = 'InputField'; flowScreen.fields[0].isRequired = true; flow.screens = new List<MetadataService.FlowScreen> { flowScreen }; // Link it with the previous one flow.recordCreates[0].connector = new MetadataService.FlowConnector(); flow.recordCreates[0].connector.targetReference = 'NewScreen'; // Update handleSaveResults(service.updateMetadata(new List<MetadataService.Metadata> { flow })[0]);
Doing this manually is a simple matter of dragging a Screen Flow element onto the canvas and configuring it with the type of UI fields you want to see. In scenarios where your step is surfacing fields from objects you can mirror those fields by setting up the Screen element with labels and field types based on the underlying field metadata.
However wouldn’t it be much easier if we could simply state which object fields we want and automatically use their metadata to drive the creation of the fields in the UI. Reusing the labels and selecting the applicable field type. The following code uses the Apex Metadata API with Apex Describe to show how something like this can be done.
public static void addFields(MetadataService.FlowScreen flowScreen, List<SObjectField> fieldsToAdd) { for(SObjectField field : fieldsToAdd) { // Construct a FlowScreenField based on the SObjectField metadata DescribeFieldResult fieldDescribe = field.getDescribe(); // Create Screen field MetadataService.FlowScreenField flowScreenField = new MetadataService.FlowScreenField(); flowScreenField.name = fieldDescribe.getName(); flowScreenField.dataType = FLOWTYPEBYDISPLAYTYPE.get(fieldDescribe.getType()); flowScreenField.fieldText = fieldDescribe.getLabel(); flowScreenField.fieldType = 'InputField'; flowScreenField.isRequired = fieldDescribe.isNillable(); flowScreenField.helpText = fieldDescribe.getInlineHelpText(); if(flowScreenField.dataType == 'Number') { flowScreenField.scale = fieldDescribe.getScale(); } // Add to Screen field list if(flowScreen.fields==null) flowScreen.fields = new List<MetadataService.FlowScreenField>(); flowScreen.fields.add(flowScreenField); } }
The sample shown at the start of this blog then becomes…
MetadataService.FlowScreen flowScreen = new MetadataService.FlowScreen(); flowScreen.name = 'NewScreen'; flowScreen.label = 'New Screen'; flowScreen.locationX = 100; flowScreen.locationY = 100; flowScreen.allowBack = true; flowScreen.allowFinish = true; addFields(flowScreen, new List<SObjectField> { Account.AccountNumber, Account.Description, Account.Fax, Account.IsPartner, Account.AnnualRevenue });
This results in the following Screen element being created in the Flow….
You can see the full code for this example here.
Summary
There is certainly i’m sure a number of other possibilities for dynamically generating or indeed updating Flows, that can now be realised in Apex through the Metadata API. I’m certainly keen to know what people think of the above and any other thoughts like it…
November 28, 2015 at 3:30 pm
Andy,
These posts are very inspiring and get the creative juices flowing. What do you think about automating the creation of wizards? For example, one enters an account on step 1, one or more opportunities on step 2, and one or more opportunity lines on step 3. How about a flow that creates flows, since flows can call apex, and that are styled some how so they can be used by staff and through sites for customers?
Code generation like this is one of my favorite things to implement and the possibilities it allows. Thanks for the great work and keep it coming!
November 28, 2015 at 3:37 pm
Yep all great ideas! And I totally agree, “extending” the platform with declarative solution building tools is an awesome way to go!
Pingback: Automating the Creation of Flow Screens with Apex Metadata API | SutoCom Solutions
February 9, 2017 at 12:29 pm
Hi Andrew, first thanks for the toolkit, it’s awesome. I’m having problems getting a read result form the tool kit when querying for flows though (raised an issue on GitHub) for this https://github.com/financialforcedev/apex-mdapi/issues/162
I get the list and then after making a list query I use the fullName attribute returned to query for the details of a specific flow. I’m only seeing an empty object. Really strange. I guess I’m doing something wrong, or the response map isn’t correct?
String[] flowFullNames = new String []{‘Sample_loc’};
flows = (MetadataService.Flow[]) service.readMetadata(‘Flow’, new List(flowFullNames)).getRecords();
Result is a returned MetadataService.Flow of one item, except that every attribute is null ! (Note that there is an active flow matching this full name, and that I had to remove the version number to get the metadata api to respond, appending the version number caused an exception, I’ve raised another issue in github for that but it’s less urgent).
What do you think?
Thanks!
Matt
February 16, 2017 at 1:49 am
Sorry for the late reply, work and life a bit busy at present. This usually happens if the flow name is wrong. It doesn’t give an error just an empty single item. Not ideal I agree. Have you confirmed the flow names via Salesforce Workbench?
August 18, 2021 at 12:24 pm
Hi Andrew When I run the readmetadata on my Flows I get System.CalloutException: Web service callout failed: Unable to parse callout response. Apex type not found for element status
October 9, 2021 at 9:24 am
Yes it looks like the wrapper needs updating to handle new xml elements. There are notes on the readme on how to do this – I have a side project in mind to create a better tool so the wrapper can stay upto date. Sorry meanwhile – if you invoke your code via anonymous Apex you will see the xml in the debug log – this allows you to tweak the apex class a little to see if you can get it to match. Hope this helps. Andy
August 18, 2021 at 12:33 pm
I changed the api version to v40.0 logging in and I dont get the error now, but its not returning any flows eg: Flow:[Metadata.fullName=null, actionCalls=null, ……