-1

I'm new to Salesforce and I am struggling to resolve an issue with Apex. What I want to do is create a flow that activates daily and sends a GET request to an IAP protected server (I can't just use the HTTP Callout beta function, since the IAP auth flow is a bit complex).

I created a class that successfully does this while debugging, but when I attach it to an Apex Action on a flow I receive the error "An Apex error occurred: System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out"

The class I wrote when I was debugging was like this:

public class HTTPFlowHandler { 


public String asyncAuthorizedGetToCreatorsApiEndpoint(String privateKey, String clientEmail, String targetAudience) {
    // Instantiate JWT generator and get JWT
    GoogleJWTGenerator generator = new GoogleJWTGenerator();
    String jwtToken = generator.generateJWT(privateKey, clientEmail, targetAudience);
  
    // Instantiate API handler, use JWT to get access token, send GET request, and store response
    GoogleAuthHandler authHandler = new GoogleAuthHandler();
    String accessToken = authHandler.getAccessToken(jwtToken);
    
    String serverResponse = authHandler.sendGetRequest(accessToken);
 
    return serverResponse;
}



public void authorizedGetOnEndpoint() {
    // Get the static resource 
    StaticResource sr = [
        SELECT Body
        FROM StaticResource
        WHERE Name = 'serviceAccountData'
    ];
    // Read the content of the JSON file as a string
    String jsonString = sr.Body.toString();
    
    // Parse the JSON content
    Map<String, Object> jsonMap = (Map<String, Object>) JSON.deserializeUntyped(jsonString);

    // Access the data in the JSON
    // Replace 'key1', 'key2', etc. with your actual JSON keys
    String privateKey = (String) jsonMap.get('private_key');
    String clientEmail = (String) jsonMap.get('client_email');
    String targetAudience = 'target';
    
    String serverResponse = getOnEndpoint(privateKey, clientEmail, targetAudience);
    System.debug(serverResponse);
    
}

}

And it works fine, but I need to make this work within a flow and return the "serverResponse" variable. Any solution that would allow me to do this would be great.

I know that to do this I need to tag my main method as @InvocableMethod, to solve the "System.CalloutException" I probably need to tag the method that does the callouts as @future and to access the variables within flow I need to wrap them in an object with an @InvocableVariable tag. So far I have managed to but I have no idea how to make this @future method return the serverResponse I need.

I'll show you guys what I have so far (this doesn't work because @future methods can't return values).

public class HTTPFlowHandler {

public class FlowInput{
    @InvocableVariable public Id recordId;
}

public class FlowOutput{
    @InvocableVariable public String data;
    public FlowOutput(String data) {this.data = data;}
}

//@InvocableMethod(label='HTTP Get on Server' description='HTTP Get on Server')
@future(callout=true)
public static void getOnEndpoint(String privateKey, String clientEmail, String targetAudience) {
    // Instantiate JWT generator and get JWT
    GoogleJWTGenerator generator = new GoogleJWTGenerator();
    String jwtToken = generator.generateJWT(privateKey, clientEmail, targetAudience);
    System.debug('jwtToken');
    System.debug(jwtToken);
    
    // Instantiate API handler, use JWT to get access token, send GET request, and store response
    GoogleAuthHandler authHandler = new GoogleAuthHandler();
    String accessToken = authHandler.getAccessToken(jwtToken);
    String response = authHandler.sendGetRequest(accessToken);

    FlowOutput serverResponse = new FlowOutput(response);
    return serverResponse;
}


@InvocableMethod(label='HTTP Get on Srvr' description='HTTP Get on Server')
public static List<FlowOutput> authorizedGetOnEndpoint() {
    // Get the static resource 
    StaticResource sr = [
        SELECT Body
        FROM StaticResource
        WHERE Name = 'serviceAccountData'
    ];
    // Read the content of the JSON file as a string
    String jsonString = sr.Body.toString();
    
    // Parse the JSON content
    Map<String, Object> jsonMap = (Map<String, Object>) JSON.deserializeUntyped(jsonString);

    // Access the data in the JSON
    // Replace 'key1', 'key2', etc. with your actual JSON keys
    String privateKey = (String) jsonMap.get('private_key');
    String clientEmail = (String) jsonMap.get('client_email');
    String targetAudience = 'target';
    
    FlowOutput serverResponse = getOnEndpoint(privateKey, clientEmail, targetAudience);
    List<FlowOutput> responses = new List<FlowOutput>();
    responses.add(serverResponse);
    return responses;
}

Thanks!!

2

1 Answer 1

0

Why are you using a future method when you want an synchronous response? Salesforce won’t allow callouts when a transaction is open. So if you want to make a callout in your flow, you need to make sure that any open transactions are closed.

I suggest you create a schedulable apex class or do the further calculation in the future apex after getting the callout.

5
  • This doesn't really answer the question.
    – Phil W
    Commented May 14, 2023 at 8:42
  • I think his approach is wrong. That's why added the atlernative. Commented May 14, 2023 at 8:48
  • If he needs to present the callout response to the user in an interactive manner using scheduled apex isn't going to do the job.
    – Phil W
    Commented May 14, 2023 at 9:01
  • Yes that's the case he is already using schedulable flow which will also not going to help. Commented May 14, 2023 at 9:25
  • Fair comment :D
    – Phil W
    Commented May 14, 2023 at 9:40

Not the answer you're looking for? Browse other questions tagged .