0

I have this method that checks a user's write permissions on an Apex controller. It works just fine at runtime. However, I'm writing a test to cover it (indirectly) and I'm getting a 'Attempt to de-reference a null object' error when I hit a certain field (Primary_Contact__c, a text field). The field exists, and is accessible to the user (read/write). The user running the code is me (admin) and I should have access to it based on object permissions and FLS.

Here's the code:

private static void checkWritePermissions(DescribeSObjectResult describeResult, String[] fields) {
    Map<String, SobjectField> fieldMap = describeResult.fields.getMap();
    List<String> affectedFields = new List<String>();
    if(DEBUG) System.debug('checkWritePermissions user: ' + UserInfo.getUserId());
    for(String field : fields) {
        if(DEBUG) System.debug('checkWritePermissions on field: \'' + field + '\'');

        if(field.trim().toLowerCase() != 'id' && !fieldMap.get(field).getDescribe().isUpdateable()) {
            affectedFields.add(field);
        }
    }
    if(affectedFields.size() > 0) {
        throw new AuraHandledException(String.format(NO_UPDATE_ACCESS_MESSAGE, new String[] { String.join(affectedFields, ',') } ));            
    }
}

It throws the error when it gets to this line, when it reaches the Primary_Contact__c field:

if(field.trim().toLowerCase() != 'id' && !fieldMap.get(field).getDescribe().isUpdateable())

I simplified the code and ran it in the Developer Console and there were no errors and I got the result I expected (i.e., I had access):

System.debug(Schema.SObjectType.Account.fields.getMap().get('Primary_Contact__c').getDescribe().isUpdateable());

Why would I be getting this error only during my test?

  • 1
    How are you generating these values? It seems odd that you're calling trim() in one spot but not the other. If you expect whitespace, remove it from the value before trying to use it as a map key. – Adrian Larson Apr 18 '18 at 22:20
  • 1
    How is the list fields generated? Is it possible that there's spurious whitespace or punctuation in the test field list? If you debug the fieldMap dictionary, is the field there in test context? – David Reed Apr 18 '18 at 22:20
2

You should use the trimmed value in both places if you expect leading or trailing whitespace.

for (String field : input)
{
    String cleanField = field.trim();
    if (cleanField != 'Id' && !fieldMap.get(cleanField).getDescribe().isUpdateable())
    {
        throw new AuraHandledException('Some Message');
    }
}

A couple notes:

  • Comparing values with == or != is case-insensitive, so no need to call toLowerCase().
  • There is really no need to cache a collection of fields which fail the access check. Just throw your error as soon as you find one.
  • It may make sense to still check if the field is not found and consider that an error, which would look slightly different:

    for (String field : input)
    {
        String cleanField = field.trim();
        SObjectField token == fieldMap.get(cleanField);
        if (cleanField != 'Id' && (token == null || !token.getDescribe().isUpdateable()))
        {
            throw new AuraHandledException('Some Message');
        }
    }
    
  • It may make sense to alter your exception message to include the SObjectType from your input, as that would have made it easier to catch your error. Obviously you would have to update your format to accomodate.

    String label = describe.getLabel();
    String exceptionMessage = String.format(myNewFormatString, new List<String> {
        label, String.join(...)
    });
    
  • Thanks @Adrian, you make good points. I cache the fields so I can display them so we know which fields the user doesn't have access to. – Swisher Sweet Apr 18 '18 at 22:31
  • @SwisherSweet Ah, fair enough. – Adrian Larson Apr 18 '18 at 22:32
1

As it turns out, I somehow switched Contact for Account in the calling code, so the field really wasn't there afterall. It should be checking field permissions on the Account object.

Here's the code that was calling the checkWritePermissions before I corrected it:

// keep these fields up-to-date as more updatable fields are added to form
String[] updatebleFields = new String[] { 'Phone', 'Fax', 'Primary_Contact__c', 
    'Primary_Email__c', 'ShippingStreet', 'ShippingCity', 'ShippingState', 'ShippingPostalCode' };        
// check permissions
checkWritePermissions(Schema.SObjectType.Contact, updatebleFields);         

Here's the correct version:

// keep these fields up-to-date as more updatable fields are added to form
String[] updatebleFields = new String[] { 'Phone', 'Fax', 'Primary_Contact__c', 
    'Primary_Email__c', 'ShippingStreet', 'ShippingCity', 'ShippingState', 'ShippingPostalCode' };        
// check permissions
checkWritePermissions(Schema.SObjectType.Account, updatebleFields);         

Not sure how that got swapped, as it was working before. But, after changing to the correct SObjectType, the error went away and my tests pass.

  • 1
    FYI you can save some keystrokes by dropping Schema and just typing SObjectType.Account. – Adrian Larson Apr 18 '18 at 22:31

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.