Apex Class as action

  • 3
  • Idea
  • Updated 2 years ago
  • Implemented
This would be really really cool. The ability to call a class. Specifically for connecting to trigger-ready flows using the start() method in apex.

Action Name: Apex Class
Lookup function for Apex Class
Display inputs required for Class.
(most of the time all that would be required would be an Id)

This would allow programming to be done almost completely declaretively.

Not only that we could set when to run the code. On model save, on button click, etc
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,714 Points 20k badge 2x thumb

Posted 4 years ago

  • 3
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,714 Points 20k badge 2x thumb
Also, javascript snippets call apex classes?
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,056 Points 20k badge 2x thumb
Great idea, we have been considering doing this since Skuid was first created. For this to work, we would have to do either of the following:

(a) support several of the most common interfaces, such as:
 - Schedulable - execute()
 - Batchable - start()
 - Process.Plugin - invoke()
 - Flow.Interview  - start()

(b) create our own interface that we would expect others to implement, such as:

global interface Executable {
    String execute(String params);
}
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,714 Points 20k badge 2x thumb
hehehehe ... that mostly went over my head. :D

So is this possible and/or likely to ever happen?
Photo of Jack Sanford

Jack Sanford, Champion

  • 8,322 Points 5k badge 2x thumb
In the Chicago training right now, it looks like this is implemented in Banzai... not sure which way they managed to make it happen. 
(Edited)
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,714 Points 20k badge 2x thumb
How about Calling Flow as action. Would be awesome too!
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,056 Points 20k badge 2x thumb
The Banzai release allows you to run Invocable Apex Actions from the Action Framework. Apex can be defined as "invocable" using the @InvocableMethod annotation. 
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,714 Points 20k badge 2x thumb
Ok. My first foray into this and I'm surely missing something.

Can you provide documentation on this method. skuid.sfdc.api.makeRequest

I think it's what I need to use to send a BLOB through to the class. I tried the Skuid action "Run an Apex Action" and get the following error.



Here's the test page. The class is what you'd expect. ;)

<skuidpage unsavedchangeswarning="yes" personalizationmode="server" showsidebar="true" showheader="true">
   <models>
      <model id="Contact" limit="1" query="true" createrowifnonefound="false" adapter="" type="" sobject="Contact">
         <fields>
            <field id="Id"/>
         </fields>
         <conditions/>
         <actions/>
      </model>
   </models>
   <components>
      <pagetitle model="Contact" uniqueid="sk-1NII09-102">
         <maintitle>
            <template>{{Name}}</template>
         </maintitle>
         <subtitle>
            <template>{{Model.label}}</template>
         </subtitle>
         <actions>
            <action type="multi" label="Attach File">
               <actions>
                  <action type="sfdc-custom-apex" sfdcactionname="UploadFileClass">
                     <inputs xmlns="http://www.w3.org/1999/xhtml">
                     </inputs>
                  </action>
               </actions>
            </action>
         </actions>
      </pagetitle>
   </components>
   <resources>
      <labels/>
      <javascript/>
      <css/>
   </resources>
   <styles>
      <styleitem type="background" bgtype="none"/>
   </styles>
</skuidpage>
Photo of Jack Sanford

Jack Sanford, Champion

  • 8,322 Points 5k badge 2x thumb
I'm not used to looking at the XML for this, and even so I could be totally wrong here, but it looks like Apex would have trouble reading your input as a List (InvocableMethod needs to receive a list and return a list). I've had trouble working in run Apex action because of that limitation and end up just using sforce.apex.execute an reworking my InvocableMethod as a webservice.

Some clear examples of what would go in that input field would be super helpful!

J. gave me one example here but I had already switched to webservice and haven't tried it yet...
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,714 Points 20k badge 2x thumb
Ok. Each input as their own list or all inputs in one list. I'd assume the former. If so, I can't see how a single input value would look different than a single input list with a single value.

First need to be be able to set the input value for a BLOB. I was hoping for a popup to be automatically generated, but that was wishful thinking. :P
Photo of Emily Davis

Emily Davis, Employee

  • 3,502 Points 3k badge 2x thumb
I'm not sure if this is helpful, but I think you should still be able to call an invocable method and pass in single arguments, rather than lists. The syntax is not exactly what you would expect. Here's an example of an invocable method that runs an approval process action on a record (in my case, a Case record), and can be called from Skuid.
global class CaseApprovalActions {
    
    @InvocableMethod(label='Approve Record')
    global static List<ApproveResponse> approveRecord(List<ApproveRequest> requests) {
        List<ApproveResponse> responses = new List<ApproveResponse>();
        ApproveResponse res = new ApproveResponse();
        responses.add(res);
        
        ApproveRequest req = requests[0];
        
        ProcessInstanceWorkItem item = [SELECT Id FROM ProcessInstanceWorkItem WHERE Id = :req.workItemId LIMIT 1];
        
        if (item.Id != null && (req.actionType == 'approve' || req.actionType == 'reject')) {
            Approval.ProcessWorkItemRequest pwr = new Approval.ProcessWorkItemRequest();
            pwr.setWorkItemId(item.Id);
            
            if (req.actionType == 'approve') { pwr.setAction('Approve'); }
            else if (req.actionType == 'reject') {pwr.setAction('Reject'); }
            
            pwr.setComments(req.comments);
            
            Approval.process(pwr);
        }
        
        return responses;
    }
    
    global class ApproveRequest {
        @InvocableVariable(required=true description='The pending Work Item Id to approve/reject' label='Work Item Id')
        public Id workItemId;
        
        @InvocableVariable(required=true description='Comments on why the user is approving/rejecting the record' label='Comments')
        public String comments;
        
        @InvocableVariable(required=true description='Options are "approve", "reject"' label='Action Type')
        public String actionType;
    }
    
    global class ApproveResponse {
        @InvocableVariable
        public String message;
    }
    
}
You just need to set up a class (e.g. ApproveRequest) for the List type to pass into the method, and then for every @InvocableVariable defined in the class, you can pass in a value from Skuid.

Skuid button configuration: