Tutoriel : types de données définis par Apex pour admins Salesforce
12 Jun 2019
Depuis l’édition été 2019, Salesforce facilite la collaboration entre administrateurs et développeurs Salesforce pour augmenter des flux (Flow Builder) avec le code Apex à travers les «types de données définis par Apex».
Cet article est déstiné aux admins, mais avec des notes pour partager avec vos devs !
Bienvenue, les participants du Global Gathering TrailheaDX
Désolée de ne pas avoir fini la traduction du tutoriel.
- Revenez dans quelques semaines pour la version finale de cet article, parce que c’est trop cool – vous apprendrez construire ce flux d’écran qui affiche des GIF drôles au hazard:
- Suivez les annonces des évenements du groupe Paris Developers Group – j’espère animer une démonstration pendant un Meetup (en téléconférence pour ceux qui ne sont pas à Paris).
TL, PL
- 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.
- This makes an admin’s life easier when using “Invocable Apex” in Flows.
- This makes a dev’s life easier when building “Invocable Apex” that does “callouts” to 3rd-party “APIs.”
Quoi de neuf ?
Selon les notes de publication, l’édition de Salesforce «été 2019» inclut une nouvelle fonctionnalité pour les variables de Flux («Flow») : les «types de données définis par Apex».
Cette fonctionnalité prétend de faciliter l’augmentation des flux avec du code Apex.
(Après l’avoir essayé, je confirme !)
- Auparavant, il fallait choisir un type de données très simple pour un variables de flux comme Texte, Numérique, Booléen, etc.
- Il éxistait aussi une option d’Enregistrement, ce qui pouvait représenter les contenus d’un enregistrement d’un objet (une table) de votre organisation Salesforce.
Mais c’était tout ce qu’il y avait – rien de plus complexe.
Depuis l’édition Summer ‘19, comme «type de données» pour un variable de Flux, l’on peut choisir aussi MeilleurTypeDeDonnees
ou GuideDeLangue
.
(Ou, comme se voit aux notes de publication, Car
– c’est à dire «voiture».)
Mais pourquoi ?
Bon … si vous êtes admin, j’imagine que vous vous demandez en ce moment :
Qu’est-ce qu’un
MeilleurTypeDeDonnees
??
Bonne question !
Alors … c’est un peu comme le type de données «enregistrement» qui éxistait déjà.
C’est à dire : Si l’on stockait un enregistrement «Contact» dans un variable de flux typé «enregistrement» qui s’appelle «monVariable1
», on pourrait faire référence aux champs tels que FirstName
et LastName
avec ces morceaux de code :
{!myVariable1.FirstName}
{!myVariable1.LastName}
Dans ce cas, l’on peut tenir pour acquis la présence de FirstName
et de LastName
en toute liste de sélection du Flow Builder au moment de faire référence à myVariable1
.
Imaginez alors de pouvoir faire pareil avec quelconque structure de données – selon vos voeux.
Imaginez de vous dire :
- Je voudrais representer l’idée d’une «guide de langue» – disons
GuideDeLangue
.- Je veux qu’elle se compose des éléments comme :
langue
etdireBonjour
etdireAuRevoir
, regroupés comme les champs d’un objet Salesforce.- Mais je ne veux pas créer un objet Salesforce pour stocker de tels petits détails que
anglais
/Hi
/Bye
. C’est trop d’effort de maintenir; l’on n’accède pas à ces infos en hors des flux.
Depuis l’été 2019, c’est possible d’éffectuer !
D’abord, demander à un développeur de créer la classe Apex suivante :
public class GuideDeLangue {
@AuraEnabled public String langue;
@AuraEnabled public String direBonjour;
@AuraEnabled public String direAuRevoir;
}
Ensuite, ouvrez un flux en Flow Builder et:
- Clicquez sur Boîte à outils -> Gestionnaire -> Nouvelle ressource
- Choisissez «Variable» de la liste de sélection «type de ressource».
- Tapez un bon «nom d’API» comme
uneGuide
- Choisissez «Définie par Apex» de la liste de sélection «Type de données».
- Choisissez «GuideDeLangue» de la liste de sélection «Classe Apex».
- Cliquez sur «Terminé»
A gauche, sous «Ressources», vous verrez une nouvelle ressource «uneGuide
» dans la section «Variables définies par Apex».
Cette variable, en soi, n’est pas très intéressant.
Alors allons le stocker de données et l’afficher sur un élément de flux «écran», mais stockons des données dans cette variable et 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).
- Click Toolbox -> Elements and drag an “Assignment” onto the flow builder.
- Give it a “Label” like “Set to French” and let Salesforce generate an API name.
- 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}
. - Leave the “Operator” as “Equals.”
- Click the box under “Value” and type
French
.
- Add assignments for
{!aGreetingVar.howYouSayHi}
asSalut
and for{!aGreetingVar.howYouSayBye}
asSalut
and click “Done.” (Yes, “Hi” and “Bye” are the same in French, like “Ciao” in Italian or “Aloha” in Hawaiian.) - Click Toolbox -> Elements and drag a “Screen” onto the flow builder.
- Give the “Screen Properties” a “Label” of “About This Language” and let Salesforce generate an API name.
- Drag a “Text”-typed “Screen Component” onto the screen and give it a label of “Language.” Let Salesforce generate an API name.
- 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}
.
- Add more “Text”-typed “Screen Components” for
{!aGreetingVar.howYouSayHi}
and{!aGreetingVar.howYouSayBye}
“Done.”
- In the flow builder, add arrow connectors flowing from
Start
to theSet to French
assignment and onward to theAbout This Language
screen. - Click “Save”
- Click “Run”
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 GuideDeLangue {
@AuraEnabled @InvocableVariable public String langue;
@AuraEnabled @InvocableVariable public String direBonjour;
@AuraEnabled @InvocableVariable public String direAuRevoir;
}
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 LangueAuHazard {
@InvocableMethod(label='Choisir Langue Au Hazard')
public static List<GuideDeLangue> choisirLangueAuHazard() {
List<GuideDeLangue> langues = new List<GuideDeLangue>{};
GuideDeLangue a = new GuideDeLangue(); a.langue = 'arabe'; a.direBonjour = 'اسلا عليكم'; a.direAuRevoir = 'الله يمسك علي خير'; langues.add(a);
GuideDeLangue b = new GuideDeLangue(); b.langue = 'anglais'; b.direBonjour = 'Hi'; b.direAuRevoir = 'Bye'; langues.add(b);
GuideDeLangue c = new GuideDeLangue(); c.langue = 'espagnol'; c.direBonjour = 'Hola'; c.direAuRevoir = 'Adios'; langues.add(c);
return new List<GuideDeLangue>{langues[Math.mod(Math.abs(Crypto.getRandomLong().intValue()),langues.size())]};
}
}
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.)
- Delete the
Set to French
assignment to free up “arrows” out ofStart
and into theAbout This Language
screen. - Click Toolbox -> Elements and drag an “Action” onto the flow builder.
- Click into the “Search All actions…” box and start typing “Get.” As the search results narrow down, choose “Get Greeting.”
(It has a subtitle ofapex-GreetingGenerator
.)
- Give the Action a “Label” of “Pick a Random Language” and let Salesforce generate an API name.
- Click the “Store Output Values” tab in the lower portion of the Action definition area.
- Click the box below
howYouSayBye
, scroll down into the “Apex-Defined Variables,” clickaGreetingVar
, and click “howYouSayBye.” Salesforce will populate the box with{!aGreetingVar.howYouSayBye}
. - Repeat this process for
howYouSayHi
andlanguage
.
(No, I’m not sure why the sub-fields are showing up in this order for me, rather than starting withlanguage
.)
- Click “Done.”
- In the flow builder, add arrow connectors flowing from
Start
to thePick a Random Language
action and onward to theAbout This Language
screen. - Click “Save”
- Click “Run”
- 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.
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 singleBestEverDataType
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:
- A yes/no/maybe answer
- 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
) - 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 OuiNon { // Selon https://yesno.wtf/#api
@AuraEnabled @InvocableVariable public String answer; // réponse oui/non/peut-être
@AuraEnabled @InvocableVariable public Boolean forced; // réponse forcé ?
@AuraEnabled @InvocableVariable public String image; // URL
}
public class DemanderOuiNon {
@InvocableMethod(label='Demander OuiNon')
public static List<OuiNon> ouiNon() {
OuiNon ounn = new OuiNon(); HttpRequest requete = new HttpRequest(); requete.setEndpoint('https://yesno.wtf/api'); requete.setMethod('GET');
Http http = new Http(); HttpResponse response = http.send(requete);
if (response.getStatusCode() == 200) { ounn = (OuiNon)JSON.deserialize(
response.getBody().replace(':"yes"',':"oui"').replace(':"no"',':"non"').replace(':"maybe"',':"peut-être"')
, OuiNon.class); }
return new List<OuiNon>{ounn};
}
}
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
.
- Outside of Flow Builder, in Setup, search for and visit “Remote Site Settings.”
- Click “New Remote Site”
- Enter something useful like
yesno_api
in “Remote Site Name” - Enter
https://yesno.wtf/api
in “Remote Site URL” - Make sure “Active” is checked.
- 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
- 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.
- Click Toolbox -> Elements and drag an “Action” onto the flow builder.
- Click into the “Search All actions…” box and start typing “Get.” As the search results narrow down, choose “Get YesNo.”
(It has a subtitle ofapex-YesNoGenerator
.) - Give the Action a “Label” of “Ask The Internet Yes or No” and let Salesforce generate an API name.
- Click the “Store Output Values” tab in the lower portion of the Action definition area.
- Click the box below
answer
, scroll down into the “Apex-Defined Variables,” clickynVar
, and click “answer.” Salesforce will populate the box with{!ynVar.answer}
. - Repeat this process for
forced
andimage
. - Click “Done.”
Text Template
- Click Toolbox -> Manager -> New Resource
- Drop down “Resource Type” and choose “Text Template”
- Give it a nice “API name” like
viewable_gif
- In “Text Template,” in the big text-editor area below “Insert a resource…” (ignore that), type
<img src="{!ynVar.image}">
- Click “Done”
Screen
- Click Toolbox -> Elements and drag a “Screen” onto the flow builder.
- Give the “Screen Properties” a “Label” of “GIF Squad Heaven” and let Salesforce generate an API name.
- Drag a “Text”-typed “Screen Component” onto the screen and give it a label of “Mood.” Let Salesforce generate an API name.
- 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}
.
- Drag a “Display Text”-typed “Screen Component” onto the screen and give it a good API name like
the_gif
. - Click the “Insert a resource…” box and, under “Text Templates,” choose
viewable_gif
. Salesforce will populate the box with{!viewable_gif}
.
- Click “Done.”
Watch the fireworks
- In the flow builder, add arrow connectors flowing from
Start
to theAsk The Internet Yes or No
action and onward to theGIF Squad Heaven
screen. - Click “Save”
- Click “Run”
- 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.
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.
Summary
- 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.
- This makes an admin’s life easier when using “Invocable Apex” in Flows.
- This makes a dev’s life easier when building “Invocable Apex” that does “callouts” to 3rd-party “APIs.”
- Pssst – I have additional example code just for devs here.