2

After reading the information around the issue Call error: System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out I tryed everything, like moving my WriteLog funcion outside the main function and it didn't worked.

I'm doing the next logical steps:

  • Create List
  • Save the response into ApiLogs__c using WriteLog
  • Call from my Main class the next method with is ImportSubscribers, which uses the ID that CreateList returned
  • Import subscribers also needs to save the HTTP Response to my ApiLogs__c

I asume that the last step, as the Doc says cannot be performed, since I cannot do an insert after a HTTP Callout.

Which is the workaround for this? Since I moved the ApiLogs__c DML insert out to another Method and it didn't worked.

public String createList(String listName){

    HttpRequest request = new HttpRequest();
    HttpResponse response = null;
    Http http = new Http();

    // Set the endpoint
    request.setEndpoint(DopplerServiceUtil.urlBuilder('createList'));
    request.setHeader('Authorization', 'token ' + DopplerServiceUtil.apiKeyBuilder());
    request.setHeader('content-type', 'application/json; charset=utf-8;');
    request.setMethod('POST');
    request.setTimeout(60000);
    request.setCompressed(false);

    // Calling Serializer to get createList JSON for setBody
    String listNameJSON = DopplerJSONSerializer.createListJSON(listName);

    // Set the httpRequest body
    request.setBody(listNameJSON); // The listId comes @response as 'createdResourceId'

    try {
        // Debugging all the requested data
        System.debug('Request is: ' + request);

        // Fireing the HttpRequest
        response = http.send(request);
        if(response != null) {
            System.debug('Response is: ' + response.getBody());
            System.debug('Connection statusCode: ' + response.getStatusCode() + ' - Connection status: ' +
                    response.getStatus());

            //DopplerJSON2Apex dopplerJSON2Apex = new DopplerJSON2Apex();
            if (String.valueOf(response.getStatusCode()).startsWith('2')) {
                DopplerJSON2Apex jsonparser = DopplerJSON2Apex.parse(response.getBody());
                createdListId = jsonparser.createdResourceId;
                messageResponse = jsonparser.message;

                LogCreatedList(createdListId,messageResponse,null,null,request.getMethod(),
                        request.getEndpoint(),request.getBody(),response.getBody(),response.getStatusCode());

                return createdListId;

            } else if (String.valueOf(response.getStatusCode()).startsWith('4')) {
                DopplerJSON2Apex jsonparser = DopplerJSON2Apex.parse(response.getBody());
                titleResponse = jsonparser.title;
                detailResponse = jsonparser.detail;

                LogCreatedList(null,null,titleResponse,detailResponse,request.getMethod(),
                        request.getEndpoint(),request.getBody(),response.getBody(),response.getStatusCode());

                return 'list-404';
            }
        }

    } catch (System.CalloutException e) {
        System.debug('Call error: ' + e);
        return 'list-error';
    }
    return 'list-error';
}
@future
public static void LogCreatedList(String logCreatedListId,String logMessageResponse, String logTitleResponse,
                                  String logDetailResponse, String logReqMethod, String logReqGetEndpoing,
                                  String logReqGetBody, String logResGetBody, Integer logResGetStatusCode){
    // ApiLog is outside the try-catch to avoid DML uncommited error on Callout.
    // Successful Status Code 200/201/202/404
    if(logCreatedListId != null && logTitleResponse == null) {
        // **** ApiLog__c ****
        DopplerApiLogs.WriteLog(logReqMethod, logReqGetEndpoing, 'Create List', logMessageResponse,
                logCreatedListId, logReqGetBody, logResGetBody, logResGetStatusCode);
        // *** End of ApiLog__c **
        // Error Status Code 400/401/402
    } else if (logCreatedListId == null && logTitleResponse != null){
        String JSONErrorStatus = logTitleResponse + ' - ' + logDetailResponse;
        // **** ApiLog__c ****
        DopplerApiLogs.WriteLog(logReqMethod, logReqGetEndpoing, 'Create List',
                JSONErrorStatus,'', logReqGetBody, logResGetBody, logResGetStatusCode);
        // *** End of ApiLog__c **
    }
}

public String upsertSubscribers (Id subscribersListId, String dopplerListId) {

    HttpRequest request = new HttpRequest();
    HttpResponse response = null;
    Http http = new Http();

    // Get subscribersList using the SF subscribersListId
    DopplerServiceUtil SubsList = new DopplerServiceUtil();
    List<DopplerServiceUtil.SubsInfo> subscribersList =  SubsList.getSubscribers(subscribersListId);
    System.debug('subscribersList: ' + subscribersList);

    // Set the endpoint url
    request.setEndpoint(DopplerServiceUtil.urlBuilder('upsertSubscribers',dopplerListId));
    request.setHeader('Authorization', 'token ' + DopplerServiceUtil.apiKeyBuilder());
    request.setHeader('content-type', 'application/json; charset=utf-8;');
    request.setMethod('POST');
    request.setCompressed(false);
    request.setTimeout(60000);
    // Calling Serializer to get createList JSON for setBody
    String suscWrapperJSON = DopplerJSONSerializer.upsertSubscribersJSON(subscribersList,DopplerServiceUtil.urlBuilder('apiCallback'));
    // Set the httpRequest body
    request.setBody(suscWrapperJSON); // The created or inserted comes @response as 'createdResourceId'

    try {
        // Debugging all the requested data
        System.debug('Request is: ' + request);

        // Fireing the HttpRequest
        response = http.send(request);
        if(response != null) {
            System.debug('Response is: ' + response.getBody());

            System.debug('Connection status: ' + response.getStatus());
            System.debug('Connection statusCode: ' + response.getStatusCode());

            if(String.valueOf(response.getStatusCode()).startsWith('2')){
                DopplerJSON2Apex jsonparser = DopplerJSON2Apex.parse(response.getBody());

                createdImportId = jsonparser.createdResourceId;
                messageResponse = jsonparser.message;


                LogImportSubscribers(createdImportId,messageResponse,null,null,request.getMethod(),
                        request.getEndpoint(),request.getBody(),response.getBody(),response.getStatusCode());

                return createdImportId;

            } else if(String.valueOf(response.getStatusCode()).startsWith('4')) {
                DopplerJSON2Apex jsonparser = DopplerJSON2Apex.parse(response.getBody());

                titleResponse = jsonparser.title;
                detailResponse = jsonparser.detail;

                LogImportSubscribers(null,null,titleResponse,detailResponse,request.getMethod(),
                        request.getEndpoint(),request.getBody(),response.getBody(),response.getStatusCode());

                return 'import-404';
            }
        }

    } catch (System.CalloutException e) {
        System.debug('Call error: ' + e);
    }

    return 'import-error';
}


@future
public static void LogImportSubscribers(String logCreatedImportId,String logMessageResponse, String logTitleResponse,
                                  String logDetailResponse, String logReqMethod, String logReqGetEndpoing,
                                  String logReqGetBody, String logResGetBody, Integer logResGetStatusCode){
    // ApiLog is outside the try-catch to avoid DML uncommited error on Callout.
    // Successful Status Code 200/201/202/404
    if(logCreatedImportId != null && logTitleResponse == null) {
        // **** ApiLog__c ****
        DopplerApiLogs.WriteLog(logReqMethod, logReqGetEndpoing, 'Import Subscribers', logMessageResponse,
                logCreatedImportId, logReqGetBody, logResGetBody, logResGetStatusCode);
        // *** End of ApiLog__c **
        // Error Status Code 400/401/402
    } else if (logCreatedImportId == null && logTitleResponse != null){
        String JSONErrorStatus = logTitleResponse + ' - ' + logDetailResponse;
        // **** ApiLog__c ****
        DopplerApiLogs.WriteLog(logReqMethod, logReqGetEndpoing, 'Import Subscribers',
                JSONErrorStatus,'', logReqGetBody, logResGetBody, logResGetStatusCode);
        // *** End of ApiLog__c **
    }
}

And I'm calling this Methods from:

    public class DopplerApiService {

    private static String dopplerListId {get;set;}
    private static String dopplerImportId {get;set;}

    public static String SendList(Id ListId) {

        ApiInterface AI;
        AI = new DopplerService();

        // Get the listName using the received ListId
        String listName = DopplerServiceUtil.getListName(ListId);

        // Call the createList method with the retreived listName;
        try {
            System.debug('listName: ' + listName);
            if(listName != null) {
                dopplerListId = AI.createList(listName);
                System.debug('dopplerListId: ' + dopplerListId);

                //if(dopplerListId != null && dopplerListId != 'list-404' && dopplerListId != 'list-error') {
                    PopulateList(ListId, dopplerListId);
                //}
            } else {
                dopplerListId = null;
                System.debug('dopplerListId: ' + dopplerListId);
            }
        } catch (System.CalloutException e) {
            System.debug('Call error: ' + e);
        }

        return null;
    }
    // @Future annotation is not working here since I get CPU Apex limits error
    public static void PopulateList(Id ListId, String DopplerListName){

        ApiInterface AI;
        AI = new DopplerService();
        // Call the createList method with the retreived listName;
        try {
            if(ListId != null && DopplerListName != null) {
                dopplerImportId = AI.upsertSubscribers(ListId, DopplerListName);
                System.debug('dopplerImportId: ' + dopplerImportId);
            } else {
                dopplerImportId = null;
                System.debug('dopplerImportId: ' + dopplerImportId);
            }

        } catch (System.CalloutException e) {
            System.debug('Call error: ' + e);
        }
    }
}

It executes the CreateList, saves CreateList log, gets the createList id return, fires the upsertSubscriber function, but inside it, it hangs on the HTTP response = http.send(request);

0

1 Answer 1

4

Your assumption:

I asume that the last step, as the Doc says cannot be performed, since I cannot do an insert after a HTTP Callout.

is not correct

You can do a dml after the callout just not before.

So after your callout it is perfectly fine to perform DML. If you are making several callouts, store the results in a collection and write them after all callouts are made.

3
  • Does not work for me. I get the same System.CalloutException when I want to do an DML (update call) after a callout. Commented Feb 5, 2020 at 12:25
  • @J.Lietzau then you have something else going on. Not being able to do dml after a callout would fundamentally limit the platform
    – Eric
    Commented Feb 5, 2020 at 16:21
  • Yes, and after a lot of headaches, I figured it out. Turns out to be a "dml in a loop rookie mistake": I processed my list of requests in a loop and did not cache the resulting record updates. Instead, I tried to commit them in the same loop iteration. Major mistake: evidently, the exception appears in the second iteration. Commented Feb 6, 2020 at 16:14

You must log in to answer this question.

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