I am creating an Apex action according to the info described here, using custom defined objects for passing in the request and out the result.
Having defined those objects, in flow, I am able to create an apex-defined single-value variable for the request object type as well as a collection of that object type.
However, when I try to invoke the apex action, it only allows me to enter the single object variable and not the collection one.
My understanding is that the Apex action is expecting a List of these custom objects. So I initially defined the method for this action as follows:
global class FindOrCreateContacts {
@InvocableMethod(label='Find or Create Contacts' category='Contact')
global static List<FOCCActionResult> GetIDs(List<FOCCActionRequest>> requests) {
...
With the custom object defined as:
global class FOCCActionRequest {
@InvocableVariable(required=true) @AuraEnabled
global String email;
@InvocableVariable(required=true) @AuraEnabled
global String lastname;
@InvocableVariable @AuraEnabled
global String firstname;
@InvocableVariable @AuraEnabled
global String country;
}
This is how it is shown in the example code on that page. However, when I tried to invoke the action from flow, it was requesting the individual variables of the class (email, lastname, firstname, etc.) instead of an instance of that class.
OK, so I changed the method signature to:
global class FindOrCreateContacts {
@InvocableMethod(label='Find or Create Contacts' category='Contact')
global static List<List<FOCCActionResult>> GetIDs(List<List<FOCCActionRequest>> requests) {
Now in Flow, it asks for an instance of requests, but it will only allow me to select a single instance variable but not a collection of those objects.
I tried adding another layer of List< to the method signature, but that was not accepted.
Can anyone tell me what I'm doing wrong? Thanks!
Here's the full source of this (I hope!) useful function, in case anyone is interested.
global class FindOrCreateContacts {
@InvocableMethod(label='Find or Create Contacts' category='Contact' description='Called by flow Upload CNVC CSV to return a list of all transactions passed to it with the ContactIDs and AccountIDs added')
// This method makes use of a custom object created in the host system called "CNVC_Import__c".
// This allows us to pass and manipulate data as SObjects for convenience. However, we never write them to the database.
global static List<List<CNVC_Import__c>> GetIDs(List<List<CNVC_Import__c>> requests) {
// Because Flow expects lists of lists as input and output, create "results" variable and then create the first list inside the first list
List<List<CNVC_Import__c>> results = new List<List<CNVC_Import__c>>();
results.add(new List<CNVC_Import__c>());
// These are sets to ensure uniqueness so we only create users once if they appear more than once
Set<String> allEmails = new Set<String>();
Set<String> allNewEmails = new Set<String>();
Set<String> foundEmails = new Set<String>();
// Used as lookup tables to link emails to Contact and Account IDs
Map<String,ID> contactIDs = new Map<String,ID>();
Map<String,ID> accountIDs = new Map<String,ID>();
system.debug('Input' + requests);
// Extract all emails from input records into a set which will be used to look them all up at once
// We're referencing "requests[0]" because it was passed as a list of lists.
for (CNVC_Import__c request : requests[0]) {
allEmails.add(request.Email__c);
}
system.debug('allEmails set:' + allEmails);
// Find any contact records that have an email from the request list
List<Contact> existingContacts = [SELECT Id, FirstName, LastName, Country__c ,AccountId, Email FROM Contact where Email in :allEmails];
system.debug('Existing Contacts' + existingContacts);
// Add existing contact IDs to contactIDs and AccountIDs lookup maps
for(Contact contact : existingContacts){
contactIDs.put(contact.email, contact.Id);
accountIDs.put(contact.email, contact.AccountId);
foundEmails.add(contact.Email);
}
//system.debug('FoundEmails after adding existing' + foundEmails);
//system.debug('Results after adding existing' + results[0]);
// Now loop through request records and prepare to create any emails not already found
List<Contact> newContacts = new List<Contact>();
for (CNVC_Import__c request : requests[0]){
if (foundEmails.contains(request.Email__c)) {
system.debug('Was Found: ' + request);
// Do nothing - Contact already exists
} else {
system.debug('Adding New: ' + request);
newContacts.add(new Contact (LastName = request.Lastname__c,
FirstName = request.Firstname__c,
Email = request.Email__c,
Country__c = request.Country__c));
// Add to list of IDs for later query to get AccountIDs
allNewEmails.add(request.Email__c);
}
}
system.debug('Newcontacts before insert' + newContacts);
// Now insert list of contacts
try {
insert newContacts;
} catch(DMLException e) {
return null;
}
//system.debug('Newcontacts after insert' + newContacts);
//system.debug('Results after insert' + results);
// Now retrieve those same new contacts. We have to do this so we can get access to the accountIDs.
// Because we are on NPSP with house accounts, accounts get created automatically as Contacts get created,
// but their IDs are not available unless you query for those contacts.
List<Contact> retrievedNewContacts = [SELECT Id, AccountId, Email, Firstname, Lastname, Country__c FROM Contact where Email in :allNewEmails];
//Add new contacts IDs to Maps for ContactIDs and AccountIDs
for(Contact contact : retrievedNewContacts){
contactIDs.put(contact.email, contact.Id);
accountIDs.put(contact.email, contact.AccountId);
}
// Now we add any newly-created contact info into the results list of Sobjects
For (CNVC_Import__c trans : requests[0]) {
results[0].add(new CNVC_Import__c(
Firstname__c = trans.Firstname__c,
Lastname__c = trans.Lastname__c,
Country__c = trans.Country__c,
Email__c = trans.Email__c,
Contact_ID__c = contactIDs.get(trans.Email__c),
Account_ID__c = accountIDs.get(trans.Email__c),
Amount__c = trans.Amount__c,
Date__c = trans.Date__c,
Existing_User__c = foundEmails.contains(trans.Email__c)));
}
system.debug('Final Results' + results);
return results;
}
public virtual class CNVCException extends Exception {}
}