0

I am having a really hard time bulkifying my code, and need to do so becasue I am reaching the SOQL limit on execution.

Here is my code as of now.

public class Zenefits_Time_Off implements Schedulable, Database.AllowsCallouts {

    public void execute(SchedulableContext SC) {
        Zenefits_Time_Off.makeCallout();
    }


    @future(callout = true)
    public static void makeCallout() {

        List<Time_Off_Request__c> torToUpdate = new List<Time_Off_Request__c> ();
        string url = 'https://api.zenefits.com/time_off/vacation_requests';
        system.debug('before while');
        while (url != null) {
            url = makeHttpCall(url, torToUpdate);
            system.debug('after while');
        }
        insert torToUpdate;
        system.debug('SECONDTOR ' + torToUpdate);
    }

    public static String makeHttpCall(String url, List<Time_Off_Request__c> torToUpdate) {
        string nextUrl = '';
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setHeader('Authorization', 'Bearer xxxxxx');
        request.setEndpoint(url);
        request.setMethod('GET');
        HttpResponse response = http.send(request);
        // If the request is successful, parse the JSON response.
        if (response.getStatusCode() == 200) { 
            // Deserializes the JSON string into collections of posts.
            Map<String, Object> wrapper = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
            if (wrapper.containsKey('data')) {
                Map<String, Object> wrapper2 = (Map<String, Object>) wrapper.get('data');
                if (wrapper2.containsKey('data')) {
                    nextUrl = (String) wrapper2.get('next_url');
                    system.debug('next URL' + nextUrl);
                    List<Object> vacationRequests = (List<Object>) wrapper2.get('data');
                    System.debug('Received the following vacation requests:');
                    for (Object vacationRequestWrapper : vacationRequests) {
                        Map<String, Object> vacationRequest = (Map<String, Object>) vacationRequestWrapper;
                        if (vacationRequest.get('status').equals('approved')) {
                            Map<String, Object> wrapper3 = (Map<String, Object>) vacationRequest.get('creator');

                            string ptoUrl = (String) wrapper3.get('url');
                            string employeeId = ptoUrl.substring(ptoUrl.length() - 7, ptoUrl.length() - 0);
                            String Start_Date = (String) vacationRequest.get('start_date');
                            String End_Date = (String) vacationRequest.get('end_date');
                            string timeoff_ID = (String) vacationRequest.get('id');
                            string hours = (String) vacationRequest.get('hours');

                            List<Contact> contactList = [SELECT Id, FirstName, LastName, Zenefits_ID__c FROM Contact WHERE Zenefits_ID__c = :employeeId LIMIT 200];
                            for (Contact con : contactList) {
                                System.debug('contactList ' + contactList); 

                                Time_Off_Request__c tor = new Time_Off_Request__c();
                                tor.Employee__c = con.Id;
                                tor.Project__c = 'a8q8A000000CafTQAS';
                                tor.First_Day_Off__c = date.valueOf(Start_Date);
                                tor.Last_Day_Off__c = date.valueOf(End_Date);
                                tor.Status__c = 'Saved';
                                torToUpdate.add(TOR);

                                system.debug('TOR' + tor);
                                system.debug('vacation ' + vacationRequest);
                                system.debug('creator ' + wrapper3);
                                System.debug('url ' + ptoUrl);
                                System.debug('ptoID ' + employeeId);
                                System.debug('start date ' + Start_Date);
                                System.debug('end date ' + End_Date);
                                System.debug('timeoffID ' + timeoff_ID);
                                System.debug('hours ' + hours);  
                                System.debug('TORtoUpdate ' + torToUpdate); 
                            }
                        }
                    }
                }
            }
        }
        return nextUrl;
    }
}

The error is coming from line 48 as that is producing too many SOQL Queries. How do I fix this error?

  • Just quickly off the top, you're looping over the variable vacationRequests and in that loop you're perform a query each iteration of that loop – Bryan Anderson Oct 21 at 18:57
  • @BryanAnderson i know.. That is what I am struggling to figure out how to fix – Kris Webster Oct 21 at 19:10
  • Not really related to the question at hand, but a few notes. 1) It may be worth it to make yourself an inner class to deserialize the response from your callout. Makes it so you don't need to do all that explicit typecasting. 2) It might be easier for others (or yourself, in 6 months) read/digest your code if you "fail fast" with serial if statements instead of nested if statements. You could also bake this validation into a method in your deserialization class. 3) Instead of while(url != null), consider while(!String.isBlank(url)). isBlank() handles null and empty strings. – Derek F Oct 22 at 0:42
2

Your code previously was preforming a SOQL query inside a for loop. Each time the query executed in the loop, it was eating up governor limits for the number a of queries used in a single Apex Transaction.

With the code I provided, we now collected the list of employee ids into a set that will be used in 1 SOQL query using the IN operator.

Set<String> empIds = new Set<String>(); //Set of employee Ids to query on

            for (Object vacationRequestWrapper : vacationRequests) {
                Map<String, Object> vacationRequest = (Map<String, Object>) vacationRequestWrapper;
                if (vacationRequest.get('status').equals('approved')) {
                    Map<String, Object> wrapper3 = (Map<String, Object>) vacationRequest.get('creator');

                    string ptoUrl = (String) wrapper3.get('url');
                    string employeeId = ptoUrl.substring(ptoUrl.length() - 7, ptoUrl.length() - 0);
                    String Start_Date = (String) vacationRequest.get('start_date');
                    String End_Date = (String) vacationRequest.get('end_date');
                    string timeoff_ID = (String) vacationRequest.get('id');
                    string hours = (String) vacationRequest.get('hours');


                    empIds.add(employeeId); //Add to set
                }
            }

            //Run Query with IN statement now
            List<Contact> contactList = [SELECT Id, FirstName, LastName, Zenefits_ID__c FROM Contact WHERE Zenefits_ID__c IN: empIds LIMIT 200];
                    for (Contact con : contactList) {
                        System.debug('contactList ' + contactList); 

                        Time_Off_Request__c tor = new Time_Off_Request__c();
                        tor.Employee__c = con.Id;
                        tor.Project__c = 'a8q8A000000CafTQAS';
                        tor.First_Day_Off__c = date.valueOf(Start_Date);
                        tor.Last_Day_Off__c = date.valueOf(End_Date);
                        tor.Status__c = 'Saved';
                        torToUpdate.add(TOR);

                        system.debug('TOR' + tor);
                        system.debug('vacation ' + vacationRequest);
                        system.debug('creator ' + wrapper3);
                        System.debug('url ' + ptoUrl);
                        System.debug('ptoID ' + employeeId);
                        System.debug('start date ' + Start_Date);
                        System.debug('end date ' + End_Date);
                        System.debug('timeoffID ' + timeoff_ID);
                        System.debug('hours ' + hours);  
                        System.debug('TORtoUpdate ' + torToUpdate); 
                    }
  • 1
    Please add a bit more explanation as this post is still almost purely code, and such answers tend to be considered a "code dump" which has lower teaching value. – Adrian Larson Oct 21 at 19:19
  • Understood @AdrianLarson will do – Bryan Anderson Oct 21 at 19:23
  • @BryanAnderson Thank you very much for that. I had my code structures like this earlier but was unable to figure out how to pass the start and end date into the second for loop to update the Time off Request with the Start and End date from the first for loop. How would I go about making it so I dont recieve this error - "Variable does not exist: Start_Date" and "Variable does not exist: End_Date" – Kris Webster Oct 21 at 19:27
  • @BryanAnderson I tried to add the dates to a set just like we did with the ZenefitsID, but I am getting an error that reads "FATAL_ERROR System.TypeException: Invalid date: common.apex.runtime.impl.SetValue@4cf72c89." – Kris Webster Oct 21 at 19:45
  • I assume that is just because the date format is not correct for the system to read and insert onto the new records? – Kris Webster Oct 21 at 19:45

Not the answer you're looking for? Browse other questions tagged or ask your own question.