1

I'm doing some work with the ESAPI Library. In particular, when updating records it is used to enforce FLS with the following method:

global SFDCAccessControlResults.UpdateResults updateAsUser(
    Map<ID, sObject> objMap,
    List<String> fieldsToUpdate
) {}

Example call:

try {
    List<Contact> contactsToUpdate = [select LastName,id from Contact];
    // Make some changes to contacts LastName field...
    SFDCAccessControlResults.UpdateResults res = 
        ESAPI.accessController().updateAsUser(
            new Map<ID, Contact>(contactsToUpdate), 
            new List<String>{'LastName'}
        );
    contactsToUpdate = res.getUpdatedObjects();
} catch (SFDCAccessControlException e) {
    // error handling
}

Note the need to explicitly convert the input list contactsToUpdate into a Map<Id, Contact>. I find this a bit unwieldy and would rather just pass in the existing list of contacts and let the underlying method sort out the Map conversion.

As a quick first attempt I added another method signature to SFDCAccessController that did just that:

global SFDCAccessControlResults.UpdateResults updateAsUser(List<sObject> objs) {
    Map<ID, sObject> objMap = new Map<ID, sObject>(objs);

    // Note: This is only going to build up the field set for the first sObject in the collection.
    // If different sObjects have different fields then they will be ignored.
    Set<String> keySet = objs.get(0).getPopulatedFieldsAsMap().keySet();
    keySet.remove('Id');
    List<String> fieldsToUpdate = new List<string>(keySet); 

    Schema.SObjectType objType = objs.get(0).getSObjectType();

    return getACImpl().updateAsUser(objMap, fieldsToUpdate, objType);   
}

However, this fails with the message:

System.TypeException: Operation only applies to concrete SObject value map types: SObject

This makes sense as a generic SObject could be any number of actual concrete sObject types.

That leaves the question - How can I create a Map from the ID to the concrete type of the sObjects?

5

Here is one approach I've come up with using Type and newInstance().

As anonymous apex:

Account acc = new Account(Name='Test');
insert acc;

List<sObject> accounts = new List<Account>();
accounts.add(acc);

Schema.SObjectType listObjType = accounts.getSObjectType();

//Instantiate a Map based on the input list sObject type.
Type mapType = Type.forName('Map<Id, '+listObjType+'>');

Map<Id, sObject> foo = (Map<Id, sObject>)mapType.newInstance();
foo.put(acc.Id, acc);
System.assertEquals(listObjType, foo.getSObjectType());

As a method to do the conversion:

private Map<Id, sObject> convertListToStronglyTypedMap(List<sObject> inputList) {
    Schema.SObjectType listObjType = inputList.getSObjectType();

    // Seems to work. Consider listObjType.getDescribe().getName()
    Type mapType = Type.forName('Map<Id, '+listObjType+'>');

    Map<Id, sObject> outputMap = (Map<Id, sObject>)mapType.newInstance();
    outputMap.putAll(inputList);

    return outputMap;
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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