apex-enterprise-patterns / fflib-apex-common

Common Apex Library supporting Apex Enterprise Patterns and much more!
BSD 3-Clause "New" or "Revised" License
913 stars 518 forks source link

fflib_QueryFactory.InvalidFieldException when running user is not system admin #308

Closed cropredyHelix closed 1 year ago

cropredyHelix commented 3 years ago

This error

fflib_QueryFactory.InvalidFieldException: Invalid field 'FieldHistoryType' for object 'FieldHistoryArchive'

is thrown in method getFieldPath()

when used in code as:

FieldHistoryArchive[] fhas = 
   FieldHistoryArchivesSelector.newInstance().selectByFieldHistoryTypeAndParentId(Account.SObjectType,parentIds);

Relevant fflib_QueryFactory code is here:

private String getFieldPath(String fieldName){
  if(!fieldName.contains('.')){ //single field
 Schema.SObjectField token = fflib_SObjectDescribe.getDescribe(table).getField(fieldName.toLowerCase());
 if(token == null)
    throw new InvalidFieldException(fieldName,this.table);
 if (enforceFLS)
    fflib_SecurityUtils.checkFieldIsReadable(this.table, token);
 return token.getDescribe().getName();
}

Relevant selector is here:

for a Selector on FieldHistoryArchive, an OOTB object that comes with Shield Field Audit Trail that looks like this:

public inherited sharing class FieldHistoryArchivesSelector extends ApplicationSelector implements IFieldHistoryArchivesSelector {

    public override String getOrderBy() {return 'FieldHistoryType ASC,ParentId ASC, CreatedDate DESC';} // required per doc

    private static final Boolean INCLUDE_FIELDSETS= false; // false is default, if true, class must include getSobjectFieldSetList
    private static final Boolean ENFORCE_CRUD= false; // true is default
    private static final Boolean ENFORCE_FLS= false; // false is default
    private static final Boolean SORT_SELECT_FIELDS= true; // true is default

    public List<Schema.SObjectField> getSObjectFieldList() {    
        return new List<Schema.SObjectField> {
            FieldHistoryArchive.CreatedDate,
            FieldHistoryArchive.FieldHistoryType,
            FieldHistoryArchive.HistoryId,
            FieldHistoryArchive.ParentId
        };
    }

    public FieldHistoryArchivesSelector() {
        super(INCLUDE_FIELDSETS,ENFORCE_CRUD,ENFORCE_FLS,SORT_SELECT_FIELDS);
    }

    public static IFieldHistoryArchivesSelector newInstance()    {
        return (IFieldHistoryArchivesSelector) Application.Selector.newInstance(new FieldHistoryArchive().getSObjectType());
    }

    public Schema.SObjectType getSobjectType() {return new FieldHistoryArchive().getSObjectType();}

    public virtual FieldHistoryArchive[] selectById(set<ID> ids) {
        if (ids.isEmpty()) return new List<FieldHistoryArchive> ();
        fflib_QueryFactory qF = newQueryFactory()
            .setCondition('Id IN :ids');
        return Database.query(qF.toSOQL());
    }

    public FieldHistoryArchive[] selectByFieldHistoryTypeAndParentId(SObjectType sobjType, Set<Id> parentIds) {
        if (parentIds.isEmpty() || sobjType== null) {return new List<FieldHistoryArchive>();}

        String fhType = String.valueOf(sobjType);
                 fflib_QueryFactory qF = newQueryFactory()
            .setCondition('FieldHistoryType = :fhType AND ParentId IN :parentIds');
        FieldHistoryArchive[] results = Database.query(qF.toSOQL()); 
        return results;
    }

Notes:

Analysis

I think this has something to do with certain objects that are otherwise inaccessible to the running user per Describe but are available to the running user where CRUD is not enforced or otherwise not specifiable. FieldHistoryArchive appears to be one of these. So, fflib_QueryFactory can't construct the query from the provided fieldnames. If I'm right, then perhaps the exception could provide more hints as to the range of possible causes:

daveespo commented 1 year ago

There were a bunch of changes to Describe at a platform level in the 2020 time frame that fixed some bugs with how schema data was cached on the platform side -- maybe this issue was caused by that?

But also, with the introduction of #419 , we're no longer recommending anyone uses the legacy homegrown CRUD/FLS enforcement and instead uses the native User Mode support that we added