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?

1 Answer 1

6

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;
}

You must log in to answer this question.

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