1

I'm using the Box Toolkit for Salesforce. As you know, in Apex I have to make asynchronous calls to Box. My problem is that sometimes when am performing multiple operation, such as creating a folder, and then using that folder as a parent folder for a set of subfolders I am running into issues. Here is what I am trying to do:

  1. Create the parent folder using the createFolderForRecordId method in the toolkit. This ties the folder to a SFDC record in Box and allows me to display it in the record using the standard Box.com integration tools.

  2. Retrieve the box id for the newly created folder using getFolderIdByRecordId, a method in the Box toolkit that returns the Box folder Id. Since this can take a small amount of time, I use a while loop to keep calling getFolderIdByRecordId until an Id is returned from Box. This delay is causing me issues because it is difficult to get Salesforce to wait for asynchronous calls to complete. I am also concerned about potentially hitting governor limits because of the potential for the while loop to run many times, each time making another callout to Box attempting to retreive the folder Id. My average number of loops as been in the 100-200 loop range before breaking out after receiving the folder Id from box. I also limit on the number of times it can loop, just in case. Bottom line, is I don't like this step. It feels wasteful and clumsy, but I'm not sure how to get SFDC to wait for a few seconds.

  3. Once I have the Box folder Id I update the associated SFDC record with the Box Id which I store in a field.

Here is the code up until this point:

@future(callout=true)
public static void createObjectFolderforRecordId(List<Id> theIds, String objType){
    system.debug('-->>Create QSF Folder ' + objType);
    if (objType == 'QSF__c') {
        List<QSF__c> theQSFs = [SELECT id, Name FROM QSF__c WHERE id IN :theIDs];
        system.debug('the QSFs size: ' + theQSFs.size());
        for(QSF__c aQSF : theQSFs) {
            String qsfFolderId = boxToolkit.createFolderforRecordId(aQSF.Id, aQSF.Name, true);
            aQSF.FolderId__c = qsfFolderId;
            boxToolkit.commitChanges(); 
            String existingBoxFolderId = null;
            integer br = 0;
            while (existingBoxFolderId == null && br < 1000) {
                existingBoxFolderId = boxToolkit.getFolderIdByRecordId(aQsf.Id);
                system.debug('>>Existing: ' + existingBoxFolderId + ' LoopCount: ' + br);
                if (aQSF.FolderId__c != null){
                    newFolderRecs.add(createDocRootEntry(aQsf.FolderId__c,aQsf.Name, objType, aQsf.Id, null));  //add the new folder into the docRoot custom object which tracks all box activity for any future needs (audit, folder manipulation, etc...)
                } //end if
                br++;
            } //end while          
        }
        update theQSFs; //adds the folderID values into the QSF records
        system.debug('-=>newdocrecs: ' + newFolderRecs.size());
        insert newFolderRecs;
        return;
    } //end if (QSF__c)
}  // end createClaimantFolder method
  1. My next step, is to use the folder Id obtained in step 2 to create additional child folders under the folder created in step 1 using the createFolder method from the toolkit and supplying it with the folderid of the parent. However. there are additional issues I'm running into around getting SFDC to wait for this step such as having the code run early, or getting errors on additional asynchonous calls. I'm currently trying this using a Flow that watches the custom field I update in step 3 and launching Apex that attempts to create the folders. Since it is also a callout to Box I have to use an @Future(callout=true) annotation and that is where I'm currently stuck. SFDC is complaining that a Future method being called from a future or batch method. Which isn't my intention but seems to be happening. I would think that since that method above is finished and the Flow is calling the next @future method that creates the subfolders. By definition this can't occur until the record is updated with the folder If that triggers the flow which is the end of the previous @future method.

I'm not writing necessarily to ask you how to fix my specific problem, although advice on that front would be welcome! I am also writing to see if you have solved any similar issues yourselves when working in similar situations. I like the Flow part of the solution but isn't working and I hate the while loop part of the solution and it does work. Isn't coding fun?

Any help would be appreciated!

Thanks,

Mitch

8
  • 1
    developer.salesforce.com/docs/atlas.en-us.apexcode.meta/… -- I think you need to follow this approach to solve your problems.
    – javanoob
    Commented Sep 9, 2015 at 15:26
  • Interesting. These aren't traditional SFDC callouts in that they aren't being invoked via VF however, it looks like I can find a way to try to use it in my context. Thanks very much for your response, I'm digging in now.
    – Mitch
    Commented Sep 9, 2015 at 16:18
  • javanoob, I appreciate your response, but since I'm calling classes and methods, and not actual http requests, I don't think this technique will work for my situation. Any other ideas? I really need to wait on calling @future methods. My mistake for calling them callouts. Callouts are happening, nit they are hidden by the integration's methods.
    – Mitch
    Commented Sep 10, 2015 at 13:41
  • Can you use queuable classes you can chain these classes.
    – javanoob
    Commented Sep 10, 2015 at 13:42
  • 1
    Hello, again. It has been difficult for me to find some time to attempt to implement queueable classes but managed it over the weekend. I'm running into the same sort of issues. While my initial queued callout works fine, and additional chained callouts fail. It seems that this is a known issue and has been documented in salesforce.stackexchange.com/questions/70149/…. Thank you for you help. I will close this issue for now. I will have to find some other way of accomplishing this task.
    – Mitch
    Commented Sep 21, 2015 at 17:54

1 Answer 1

3

I know this is quite old, but in case someone else is looking for guidance here, I'm doing something similar and it works for me without having to implement a while loop.

I think the main issue you had is calling boxToolkit.commitChanges(); in your for loop.

I suggest waiting to commit any changes to Box until after your loop, but before your DML statements. This should reduce the callout time, since you're only calling out once, and you'll get the info back from Box before the DML happens.

If that doesn't work, you may want to have two different triggers (or flows/processes, whatever it is that is calling your code), one to create the folder and add the id to the record, and the second that is triggered by the update to the record that adds in the folder id to the FolderId__c field, which creates the audit record (I think you called it a docRoot record).

Here's my future method. First it creates (or retrieves) the folder for the record, and then creates subfolders for that parent folder, then it collaborates the current user as an editor on the record folder, all in one step, and then the box callout happens after the loop. Here's how it's written:

@future (callout = true)
public static void createFoldersInBox(List<Id> PIIds){
    box.Toolkit boxToolkit = new box.Toolkit();
    LIST<Property_Interest__c> piList = [SELECT ID, PRFolderID__c, MDFolderID__c, MRFolderID__c 
                                         FROM Property_Interest__c 
                                         WHERE ID IN :PIIds];
    Id userId = UserInfo.getUserId();
    Integer size = piList.size();
    for(Integer i = 0; i < size; i++){
        // Create a folder and associate it with a Property Interest Record
        String PIFolderId = boxToolkit.createFolderForRecordId(piList[i].ID, null, true);
        system.debug('new item folder id: ' + PIFolderId);

        // Create three sub-folders in the newly created account folder
        String PRFolderId = boxToolkit.createFolder('Primary References', PIFolderId, null);
        system.debug('Primary References Folder id: ' + PRFolderId);
        String MDFolderId = boxToolkit.createFolder('Monitoring Documents', PIFolderId, null);
        system.debug('Monitoring Documents Folder id: ' + MDFolderId);
        String MRFolderId = boxToolkit.createFolder('Reports Archive', PIFolderId, null);
        system.debug('Reports Archive Folder id: ' + MRFolderId);

        // Update the folder ID fields 
        piList[i].PRFolderID__c = PRFolderId;
        piList[i].MDFolderID__c = MDFolderId;
        piList[i].MRFolderID__c = MRFolderId;
        piList[i].Create_Box_Folders__c = False;

        // Collaborate the current user on the property interest folder. Note that we're sending false for the optCreateFolder param that shouldn't actually matter since the folder(s) already exists
        box.Toolkit.CollaborationType collabType = box.Toolkit.CollaborationType.EDITOR;
        String collabId = boxToolkit.createCollaborationOnRecord(userId, piList[i].ID, collabType, false);
        system.debug('new collaboration id: ' + collabId);
    }
    // ALWAYS call this method when finished. Since salesforce doesn't allow http callouts after dml operations, we need to commit the pending database inserts/updates or we will lose the associations created
    boxToolkit.commitChanges();

    update piList;
}

Obviously, this only works for one object, and it pre-sets the exact folders that I want created for that object, so long term I'd like to make it more extensible, but just wanted to show that it is possible (now, even if it wasn't when this post was originally written) to create a folder and it's subfolders all in one step, without having to use a while loop or queue-able code.

You must log in to answer this question.

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