Français
Presentations About Resources

Salesforce, Python, SQL, & other ways to put your data where you need it -- a bilingual blog in English & French

Tutorial: Flow Apex-Defined Data Types for Salesforce Admins

29 May 2019 🔖 tutorials api salesforce flow integration architecture
💬 EN ( Lire cet article en français )

Post Header Image

Table of Contents

This post is for Salesforce admins and developers, showing you how you can more easily work together to combine Flows and Apex with a new Summer ‘19 release called “Apex-Defined Data Types.”

It’s mostly for admins, but I included “dev notes” so you can show the post to your dev once it gets you all excited.

(Note: “Adminelopers” hoping to “dev it themselves” might find the code in the “3rd-party APIs” part of this post a bit overwhelming. Never fear – this summer, I’ll be writing a deep-dive beginner’s tutorial about HTTP and API callouts!)

#GIFSquad, get ready, because we’re going to build this random-meme-generating flow:

screenshot


TL, DR

  1. Apex-Defined Data Types use a small amount of Apex code to let admins building Flows collect multiple sub-variables into a single outer variable, much like a “Contact” record has a “FirstName” & “LastName” within it.
  2. This makes an admin’s life easier when using “Invocable Apex” in Flows.
  3. This makes a dev’s life easier when building “Invocable Apex” that does “callouts” to 3rd-party “APIs.”

What’s New?

Salesforce’s Summer ‘19 release notes announce a new feature for Flow variables called “Apex-Defined Data Types.”

The notes promise that Apex-Defined Data Types will make it easier to tie certain types of Apex code into Flow.

(After trying it, I’d agree!)

  • Previously, you could only set the “data type” of a variable in Flow to be something simple like Text, or Number, or Boolean.
  • There was also an option of Record, which was capable of holding the contents of a row from an object in your Salesforce org.

But that was it – nothing more complicated than that.

As of Summer ‘19 you can set the “data type” of a variable in Flow to be BestEverDataType or GreetingPhrasebook.

(Or, if you want to be boring like in the release notes, Car.)

Screenshot of setting the data type to Car in the release notes


Why Would I Do That?

Now, if you’re an admin, you’re probably asking:

What on earth is a BestEverDataType?

Great question!

You know how if you store a “Contact” record into a Record-typed Flow Variable called myVariable1, you’ll later be able to reference fields from Contact throughout Flow with code like {!myVariable1.FirstName} and {!myVariable1.LastName}?

The way things like FirstName and LastName will just sort of “be there for you, ready and waiting in a picklist,” whenever you start to say you’re interested in using myVariable1 somewhere else in the Flow?

Imagine that if you could do that with arbitrary structures.

Imagine if you could say:

  • I’m going to want to be passing a concept called a GreetingPhrasebook around my Flow.
  • I’d like to use it to keep sub-components like language and howYouSayHi and howYouSayBye together as if they were records of a table.
  • But I don’t want to actually make a whole object in Salesforce to store data like French / Salut / A plus. That feels wasteful; it’s only needed for Flows, not in Reports and such.

Well … now you can!

If a developer created the following class for you:

public class GreetingPhrasebook {
    @AuraEnabled public String language;
    @AuraEnabled public String howYouSayHi;
    @AuraEnabled public String howYouSayBye;
}

Then you could do this in Flow:

  1. Click Toolbox -> Manager -> New Resource
    screenshot
  2. Drop down “Resource Type” and choose “Variable”
  3. Give it a nice “API name” like aGreetingVar
  4. In “Data Type,” pick “Apex-Defined”
  5. In “Apex Class,” pick “GreetingPhrasebook”
    screenshot
  6. Click “Done”

At left, in the “Resources” list under Toolbox -> Manager, you can see that you have a new resource in a sublist called “Apex-Defined Variables” named aGreetingVar.

screenshot

This isn’t particularly interesting, but let’s put some data in it and display it on a Screen (if you’re not in a Screen Flow, start over and make your flow a Screen Flow).

  1. Click Toolbox -> Elements and drag an “Assignment” onto the flow builder.
  2. Give it a “Label” like “Set to French” and let Salesforce generate an API name.
  3. Beneath “Set Variable Values,” click the “Search Variables” box under “Variable”, scroll down into the “Apex-Defined Variables,” click aGreetingVar, and click “language.” Salesforce will populate the box with {!aGreetingVar.language}.
  4. Leave the “Operator” as “Equals.”
  5. Click the box under “Value” and type French.
    screenshot
  6. Add assignments for {!aGreetingVar.howYouSayHi} as Salut and for {!aGreetingVar.howYouSayBye} as Salut and click “Done.” (Yes, “Hi” and “Bye” are the same in French, like “Ciao” in Italian or “Aloha” in Hawaiian.) screenshot
  7. Click Toolbox -> Elements and drag a “Screen” onto the flow builder.
  8. Give the “Screen Properties” a “Label” of “About This Language” and let Salesforce generate an API name.
  9. Drag a “Text”-typed “Screen Component” onto the screen and give it a label of “Language.” Let Salesforce generate an API name.
  10. Click the box below “Default Value,” scroll down into the “Apex-Defined Variables,” click aGreetingVar, and click “language.” Salesforce will populate the box with {!aGreetingVar.language}.
    screenshot
    screenshot
  11. Add more “Text”-typed “Screen Components” for {!aGreetingVar.howYouSayHi} and {!aGreetingVar.howYouSayBye} “Done.”
    screenshot
  12. In the flow builder, add arrow connectors flowing from Start to the Set to French assignment and onward to the About This Language screen.
  13. Click “Save”
  14. Click “Run”

screenshot

And ta-da … a web page that tells me a little bit about French.

Takeaway

This “clumping variables into a bigger variable” functionality alone might or might not be worth bothering your developer for. (You know your business processes best!)

What makes Apex-Defined Data Types really powerful is how nicely they integrate with Invocable Apex.

(Unfortunately, as of Summer ‘19, they don’t seem to be useful for passing data between flows, because Apex-defined variables don’t seem to have an “available outside the flow” option.)

We’ll cover that next!

But first … a note for our devs.

Developer Note

The moment you add the @AuraEnabled annotation to an attribute of a standalone Apex class, that class’s name will show up in Flow for your admins as a “data type” they can use for variables.

Adding this annotation to the attributes of classes that are “nested” inside other Apex classes does not make those “inner” classes show up in Flow. You need to use a standalone class.


Invocable Apex

Combining the special flags @InvocableVariable and @AuraEnabled together in Apex code lets your developer write you an “invocable Apex method” for use in your Flows that can produce “Output” in a format that already has the same sub-fields as a variable you’ve created.

Unfortunately, Flow won’t let you do one swift move that dumps the entirety of a GreetingPhrasebook record into your aGreetingVar variable.

You do have to hand-map the Invocable Apex output’s language sub-field to {!aGreetingVar.language}, the output’s howYouSayHi sub-field to {!aGreetingVar.howYouSayHi}, etc.

Nevertheless, performing this mapping a lot more intuitive than it would if you had a bazillion different variables with names like greeting1Language and greeting1HowYouSayHi.

First, I have to update the GreetingPhrasebook Apex class to have @InvocableVariable before each of the 3 attributes I defined.

public class GreetingPhrasebook {
    @AuraEnabled @InvocableVariable public String language;
    @AuraEnabled @InvocableVariable public String howYouSayHi;
    @AuraEnabled @InvocableVariable public String howYouSayBye;
}

Next, I’m going to create a new Apex class called GreetingGenerator with an invocable “method” called getGreeting() (a.k.a. Get Greeting within Flow).

getGreeting() and its “helper method” getRandomGreeting() have one job: to pick randomly among French, English, and Spanish, and to let me know how to say “hi” and “bye” in its language of choice.

I could have put getGreeting() and getRandomGreeting() inside of the GreetingPhrasebook Apex class, but stylistically, it’s probably best to break them up.

We’ll let the Apex class GreetingPhrasebook specialize in defining a “noun” and we’ll let GreetingGenerator specialize in defining a “verb,” if you will.

public class GreetingGenerator {
    
    @InvocableMethod(label='Get Greeting')
    public static List<GreetingPhrasebook> getGreeting() {
        return new List<GreetingPhrasebook>{getRandomGreeting()};
    }
    
    private static GreetingPhrasebook getRandomGreeting() {
        List<GreetingPhrasebook> possibleGreetings = new List<GreetingPhrasebook>{};
        GreetingPhrasebook a = new GreetingPhrasebook();
        a.language = 'French';
        a.howYouSayHi = 'Salut';
        a.howYouSayBye = 'Salut';
        possibleGreetings.add(a);
        GreetingPhrasebook b = new GreetingPhrasebook();
        b.language = 'English';
        b.howYouSayHi = 'Hi';
        b.howYouSayBye = 'Bye';
        possibleGreetings.add(b);
        GreetingPhrasebook c = new GreetingPhrasebook();
        c.language = 'Spanish';
        c.howYouSayHi = 'Hola';
        c.howYouSayBye = 'Adios';
        possibleGreetings.add(c);       
        Integer randomNumber = Math.mod(Math.abs(Crypto.getRandomLong().intValue()),possibleGreetings.size());
        return possibleGreetings[randomNumber];
    }
}

Now it’s time to put this Apex our developer so kindly wrote us into action in Flow Builder.

(You may need to exit and re-open Flow Builder to make these instructions work.)

  1. Delete the Set to French assignment to free up “arrows” out of Start and into the About This Language screen.
  2. Click Toolbox -> Elements and drag an “Action” onto the flow builder.
  3. Click into the “Search All actions…” box and start typing “Get.” As the search results narrow down, choose “Get Greeting.”
    (It has a subtitle of apex-GreetingGenerator.)
    screenshot
  4. Give the Action a “Label” of “Pick a Random Language” and let Salesforce generate an API name.
  5. Click the “Store Output Values” tab in the lower portion of the Action definition area.
  6. Click the box below howYouSayBye, scroll down into the “Apex-Defined Variables,” click aGreetingVar, and click “howYouSayBye.” Salesforce will populate the box with {!aGreetingVar.howYouSayBye}.
  7. Repeat this process for howYouSayHi and language.
    (No, I’m not sure why the sub-fields are showing up in this order for me, rather than starting with language.)
    screenshot
  8. Click “Done.”
  9. In the flow builder, add arrow connectors flowing from Start to the Pick a Random Language action and onward to the About This Language screen.
  10. Click “Save”
  11. Click “Run”
  12. Refresh the page that pops up a few times until you’re satisfied that your Flow is randomly picking a language every time you run it.

screenshot

Takeaway

As soon as your flow’s business logic gets complex enough to delegate part of it to “Invocable Apex,” ask your developer if they can make your life more pleasant by combining the technique with Apex-Defined Data Types.

It should help you tidy up Flow Builder!

Also new with Summer 19: remember that any user who’s going to be running a Flow that uses Invocable Apex needs to have permissions set up correctly.

Developer Notes

1: Although official Salesforce documentation on the @InvocableVariable annotation shows it applied to the attributes of an “inner class” within the class defining an @InvocableMethod-annotated method, you can also apply it to attributes of a standalone Apex class just as easily.

2: @InvocableMethod-annotated methods can return a List of any class they can see with at least one @InvocableVariable-annotated attribute.

As with other @InvocableMethod development, you still return a List<> of whatever you actually want one of:

  • You need to return a List<BestEverDataType> if you want to return a single BestEverDataType object to a Flow.
  • If you’re planning to actually return a list of them, you’d want to return a List<List<BestEverDataType>>.
    • (Make sure the admin knows to set up the Flow variable into which they will be dumping your method’s results with the variable configuration page’s “Allow multiple values (collection)” box checked.)

3: Remember:

  • @AuraEnabled makes a class’s attribute available in Flow Builder as part of the definition of a Flow Variable’s data type.
  • @InvocableVariable makes a class’s attribute available in Flow Builder for managing the Input & Output of a Flow Action.

Silly GIFs

Our getGreeting() “invocable Apex method” was kind of “meh,” right? One of 3 little random answers?

It sure would be nice if we could leverage amazing code other people have put on the internet to something more fun.

For example, there’s a really fun “API” on the internet called “YesNo.”

According to its documentation, an HTTP GET request to https://yesno.wtf/api will respond with plain-text formatted according to the JSON standard indicating:

  1. A yes/no/maybe answer
  2. A true/false indicating whether we forced the answer (you can do party tricks with the URL to force a certain answer; in our case, this will always be false)
  3. The URL to a random GIF expressing the yes/no/maybe emotion returned

Here’s an example:

{"answer":"no","forced":false,"image":"https://yesno.wtf/assets/no/27-8befe9bcaeb66f865dd3ecdcf8821f51.gif"}

YesNo has dozens, if not hundreds, of silly GIFs, so it’s endless fun hitting “refresh.”

Wouldn’t it be great if we could incorporate this data into a Flow that always looks different?

We can, and it isn’t even going to be very frustrating for our dev, because Salesforce’s Apex language has a really powerful command called JSON.deserialize() that, when given some simple JSON-formatted text and the name of a class like GreetingPhrasebook or YesNo, can automatically “break up” the JSON-formatted text using the “attributes” (or sub-fields) defined in the Apex class whose name it was provided.

Let’s give this a try – #GIFSquad, are you with me?!

First, you’ll need your dev to build you two new Apex classes, YesNo and YesNoGenerator:

public class YesNo {
    @AuraEnabled @InvocableVariable public String answer;
    @AuraEnabled @InvocableVariable public Boolean forced;
    @AuraEnabled @InvocableVariable public String image;
}
public class YesNoGenerator {
    @InvocableMethod(label='Get YesNo' description='Returns a response from the public API YesNo.wtf')
    public static List<YesNo> getYesNo() {
        List<YesNo> yesNo = new List<YesNo>{getRandomYesNo()};
        return yesNo;
    }
    
    private static yesNo getRandomYesNo() {
        YesNo yn = new YesNo();
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint('https://yesno.wtf/api');
        request.setMethod('GET');
        HttpResponse response = http.send(request);
        // If the request is successful, parse the JSON response.
        if (response.getStatusCode() == 200) {
            // Deserialize the JSON string into collections of primitive data types.
            yn = (YesNo)JSON.deserialize(response.getBody(), YesNo.class);
        }
        return yn;
    }
}

Thanks, dev!
(Pssssst, dev – check out my additional examples and unit test code here.)

All right … here we go.

(You may need to exit and re-open Flow Builder to make these instructions work once your developer finishes.)

Let’s Build A Flow

Security

First, we need to tell Salesforce that it’s safe to let YesNoGenerator.getYesNo() talk to https://yesno.wtf/api.

  1. Outside of Flow Builder, in Setup, search for and visit “Remote Site Settings.”
  2. Click “New Remote Site”
  3. Enter something useful like yesno_api in “Remote Site Name”
  4. Enter https://yesno.wtf/api in “Remote Site URL”
  5. Make sure “Active” is checked.
  6. Click “Save.”

Warning: In theory, one day YesNo could be taken over by hackers and start delivering viruses instead of fun GIFs.

You might want to Delete this from your Remote Sites after you’re done playing so you don’t forget you put it here.

Flow Action

  1. Back in Flow Builder, delete every node from the Flow Builder center panel so you have a clean design area with nothing but a “Start” node. We’re all done with our old components.
  2. Click Toolbox -> Elements and drag an “Action” onto the flow builder.
  3. Click into the “Search All actions…” box and start typing “Get.” As the search results narrow down, choose “Get YesNo.”
    (It has a subtitle of apex-YesNoGenerator.)
  4. Give the Action a “Label” of “Ask The Internet Yes or No” and let Salesforce generate an API name.
  5. Click the “Store Output Values” tab in the lower portion of the Action definition area.
  6. Click the box below answer, scroll down into the “Apex-Defined Variables,” click ynVar, and click “answer.” Salesforce will populate the box with {!ynVar.answer}.
  7. Repeat this process for forced and image.
  8. Click “Done.”
    screenshot

Text Template

  1. Click Toolbox -> Manager -> New Resource
  2. Drop down “Resource Type” and choose “Text Template”
  3. Give it a nice “API name” like viewable_gif
  4. In “Text Template,” in the big text-editor area below “Insert a resource…” (ignore that), type <img src="{!ynVar.image}">
  5. Click “Done”
    screenshot

Screen

  1. Click Toolbox -> Elements and drag a “Screen” onto the flow builder.
  2. Give the “Screen Properties” a “Label” of “GIF Squad Heaven” and let Salesforce generate an API name.
  3. Drag a “Text”-typed “Screen Component” onto the screen and give it a label of “Mood.” Let Salesforce generate an API name.
  4. Click the box below “Default Value,” scroll down into the “Apex-Defined Variables,” click ynVar, and click “answer.” Salesforce will populate the box with {!ynVar.answer}.
    screenshot
  5. Drag a “Display Text”-typed “Screen Component” onto the screen and give it a good API name like the_gif.
  6. Click the “Insert a resource…” box and, under “Text Templates,” choose viewable_gif. Salesforce will populate the box with {!viewable_gif}.
    screenshot
  7. Click “Done.”

Watch the fireworks

  1. In the flow builder, add arrow connectors flowing from Start to the Ask The Internet Yes or No action and onward to the GIF Squad Heaven screen.
  2. Click “Save”
  3. Click “Run”
  4. Refresh the page that pops up a few times until you’re satisfied that your Flow is randomly picking a GIF every time you run it.

screenshot

screenshot

screenshot

Takeaway

Because of the power of a command in Apex called JSON.deserialize(), your developers can make “invocable Apex” for you that fetches data from other people over the internet and lets you use it in your Flow without too many headaches.

Warning: Don’t go crazy on this functionality without researching good security practices. Putting things from strangers’ “APIs” into the web pages your Salesforce users will be looking at is a bit like accepting candy from a stranger!

Developer Notes

If you’ve never made Apex do a callout to a 3rd-party API, study this Trailhead module.

If you’re writing production code against a serious API, you obviously have a lot of concerns about “mocking” so that you can write good Unit Tests, “input sanitation” for security, etc. to research as well.


Videos


Summary

  1. Apex-Defined Data Types use a small amount of Apex code to let admins building Flows collect multiple sub-variables into a single outer variable, much like a “Contact” record has a “FirstName” & “LastName” within it.
  2. This makes an admin’s life easier when using “Invocable Apex” in Flows.
  3. This makes a dev’s life easier when building “Invocable Apex” that does “callouts” to 3rd-party “APIs.”
--- ---