22

I am working on a class that dynamically created FieldPermissions records for a permission set. This is working fine, except when it comes to standard compound address fields. When you attempt to create a FieldPermissions record for Contact.MailingStreet using the following code

PermissionSet perm = new PermissionSet(Name='Test_Perm_Set',Label='test Perm Set');
insert perm;
insert new FieldPermissions(SobjectType='Contact',PermissionsRead=true, Field='Contact.MailingStreet', ParentId=Perm.Id);

it errors out. Inexplicably, the error reads

FATAL_ERROR System.DmlException: Insert failed. First exception on row 0; first error: INVALID_OR_NULL_FOR_RESTRICTED_PICKLIST, Field Name: bad value for restricted picklist field: Contact.MailingCity: [Field]

Clearly not expected behavior, right there!

Even weirder, the FieldPermissions docs identify that a field will describe itself as not permissionable in it's describe result if you cannot create a FieldPermissions for it. And yet...

System.debug(Contact.SObjectType.getDescribe().Fields.getMap().get('MailingStreet').getDescribe().isPermissionable());

Returns true.

Is this a platform bug that these fields are responding as permissionable? Is there some combination of describe flags that would let me know that they aren't permissionable?

EDIT: To answer a question from twitter, yes, there is no error when creating a permission for the Contact.MailingAddress field. However, fieldsets contain the component fields instead of the compound field, so I'm looking for some logic other than hardcoded replaces to know to deal with the compound field (or at least not error on the components).

6
  • As I understand your question, the issue is that you're creating the permission set from the start as a child of another permission set as opposed to creating it as a stand-alone permission set, then adding it to a combined permission set for a fieldset. Is that correct? As in, if you created them separately, then added them to the new fieldset perm set at the end of the class, perhaps you wouldn't have this issue?
    – crmprogdev
    Commented Nov 1, 2015 at 14:47
  • Nope, not the issue. There's no child permission set here, it's standalone. The issue is that you can't create FieldPermissions records for the Street/City/State/PostalCode/Country fields (on any standard address field), you have to create the FieldPermissions records on the top-level address field. Which is not terrible, but there's no indication in the describe call that this will fail (in fact, it explicitly returns that it won't fail!), so I have to hardcode address related exemptions. Commented Nov 1, 2015 at 16:50
  • ParentId is the lookup to PermissionSet on FieldPermissions. It doesn't actually mean there's a hierarchy at play. Commented Nov 1, 2015 at 16:51
  • Are you able to create the permission set for the composite parent field? So create it for OtherAddress instead of OtherStreet, OtherCity, OtherState, OtherCountry, etc.
    – pchittum
    Commented Dec 22, 2015 at 11:33
  • Peter, I am able to do that (and am currently doing that, with the help of a Map<String,String> to map the components to the parent field, but it's a bit of a hack! Commented Dec 22, 2015 at 17:32

4 Answers 4

7

There is an easy way to determine if field is a real field or a part of compound field.

There are two tables in Salesforce which correspond to compound fields and its components.

If a field can be found in FieldDefinition table, it is a real (permissionable) field. If a field cannot be found in FieldDefinition table, but can be found in EntityParticle table, it is a part of compound field and not a separate independent field.

So, you can easily detect if field is part of compound field by making a query.

SELECT BusinessOwnerId,BusinessStatus,ControllingFieldDefinitionId,DataType,Description,DeveloperName,DurableId,EntityDefinitionId,ExtraTypeInfo,Id,IsApiFilterable,IsApiGroupable,IsApiSortable,IsCalculated,IsCompactLayoutable,IsCompound,IsFieldHistoryTracked,IsHighScaleNumber,IsHtmlFormatted,IsIndexed,IsListFilterable,IsListSortable,IsListVisible,IsNameField,IsNillable,IsPolymorphicForeignKey,IsSearchPrefilterable,IsWorkflowFilterable,Label,LastModifiedById,LastModifiedDate,Length,MasterLabel,NamespacePrefix,Precision,PublisherId,QualifiedApiName,ReferenceTargetField,ReferenceTo,RelationshipName,RunningUserFieldAccessId,Scale,SecurityClassification,ServiceDataTypeId,ValueTypeId FROM FieldDefinition where DurableId = 'Contact.MailingStreet'

or, simply put,

SELECT DeveloperName,DurableId,EntityDefinitionId FROM FieldDefinition where DurableId = 'Contact.MailingStreet'

Since this query returns zero results,

enter image description here

this means that there is no definition of field with name MailingStreet on Contact.

To be sure that there is no such field if you would try to adopt this logic for custom fields, you can change the filter criteria to include QualifiedApiName

SELECT DurableId FROM FieldDefinition where EntityDefinitionId = 'Contact' AND QualifiedApiName = 'MailingStreet'

enter image description here

While for real field like Mailing Address, the similar query would return result

enter image description here

To find all field parts of compound fields, query EntityParticle table.

SELECT ByteLength,DataType,DefaultValueFormula,DeveloperName,Digits,DurableId,EntityDefinitionId,ExtraTypeInfo,FieldDefinitionId,Id,InlineHelpText,IsApiFilterable,IsApiGroupable,IsApiSortable,IsAutonumber,IsCalculated,IsCaseSensitive,IsCompactLayoutable,IsComponent,IsCompound,IsCreatable,IsDefaultedOnCreate,IsDependentPicklist,IsDeprecatedAndHidden,IsDisplayLocationInDecimal,IsEncrypted,IsFieldHistoryTracked,IsHighScaleNumber,IsHtmlFormatted,IsIdLookup,IsLayoutable,IsListVisible,IsNameField,IsNamePointing,IsNillable,IsPermissionable,IsUnique,IsUpdatable,IsWorkflowFilterable,IsWriteRequiresMasterRead,Label,Length,Mask,MaskType,MasterLabel,Name,NamespacePrefix,Precision,QualifiedApiName,ReferenceTargetField,ReferenceTo,RelationshipName,RelationshipOrder,Scale,ServiceDataTypeId,ValueTypeId FROM EntityParticle where EntityDefinitionId = 'Contact' and IsCompound = false AND FieldDefinitionId IN (
SELECT DurableId FROM 
FieldDefinition
where IsCompound = true)

Since you can find MailingStreet record in these results, you can confirm that it is truly part of compound field. enter image description here

I think even better to check `IsComponent' property alone.

SELECT DataType,DeveloperName,DurableId,EntityDefinitionId,FieldDefinitionId,IsComponent,IsCompound,Label,Name,QualifiedApiName
FROM EntityParticle
where EntityDefinitionId = 'Contact' and IsComponent = true

So this query is simpler and returns the same 23 results.

enter image description here

Even it is possible to execute query to return this record without knowing its DurableId filtering by QualifiedApiName, Name or Label like this:

SELECT DataType,DeveloperName,DurableId,EntityDefinitionId,FieldDefinitionId,IsComponent,IsCompound,Label,Name,QualifiedApiName
FROM EntityParticle
where EntityDefinitionId = 'Contact' AND QualifiedApiName = 'MailingStreet'

enter image description here

Also, another way to find out if field is a component of a compound field without making SOQL queries is to check getCompoundFieldName() method on fieldDescribe result. It returns a name of Compound Field for a component of a compound field and returns null in other cases.

System.debug(LoggingLevel.ERROR, '@@@ v: ' + Test__c.Geolocation__Latitude__s.getDescribe().getCompoundFieldName() );
System.debug(LoggingLevel.ERROR, '@@@ v: ' + Test__c.Geolocation__c.getDescribe().getCompoundFieldName() );
System.debug(LoggingLevel.ERROR, '@@@ v: ' + Contact.MailingStreet.getDescribe().getCompoundFieldName() );
System.debug(LoggingLevel.ERROR, '@@@ v: ' + Contact.MailingAddress.getDescribe().getCompoundFieldName() );

enter image description here

So instead of checking Contact.MailingStreet.getDescribe().isPermissionable() you can check Contact.MailingStreet.getDescribe().isPermissionable() && Contact.MailingStreet.getDescribe().getCompoundFieldName() == null

1

This seems to be some internal issue with Salesforce like the one Salesforce accepts related to compound fields on page: Link to page on SF SOAP API document and reads:

"Address fields aren’t filterable, but the isFilterable() method of the DescribeFieldResult Apex class erroneously returns true for address fields."

If I were you, I would use the following logic: 1. Read a permission set that is owned by a profile, that way it will have all FLS defined. I will retrieve the field names for the contact object like:

select id, (select Field from FieldPerms where SobjectType = 'Contact') from PermissionSet where IsOwnedByProfile = true limit 1

The above outputed:

[{"Field":"Contact.Languages__c"},{"Field":"Contact.Level__c"},{"Field":"Contact.Account"},{"Field":"Contact.AssistantName"},{"Field":"Contact.AssistantPhone"},{"Field":"Contact.Birthdate"},{"Field":"Contact.CleanStatus"},{"Field":"Contact.Department"},{"Field":"Contact.Description"},{"Field":"Contact.Email"},{"Field":"Contact.Fax"},{"Field":"Contact.HomePhone"},{"Field":"Contact.Jigsaw"},{"Field":"Contact.LeadSource"},{"Field":"Contact.MailingAddress"},{"Field":"Contact.MobilePhone"},{"Field":"Contact.OtherAddress"},{"Field":"Contact.OtherPhone"},{"Field":"Contact.Phone"},{"Field":"Contact.ReportsTo"},{"Field":"Contact.Title"}]
  1. Finally, read through this array and then set the fieldpermissions for the new permissionset (ie. insert new FieldPermissions......)

Hope this helps.

-2

Compound fields can never be the part of SOQL query. Hence, you may check for SobjectFieldResult method called-->isQueryByDistance().

This will return true for compound fields so you can avoid such fields.

Thanks...!

2
  • Unfortunately, as you can try by running this in an execute anon block: System.debug(Contact.SObjectType.getDescribe().Fields.getMap().get('MailingStreet').getDescribe().isQueryByDistance());, that field is set to false for the compound address component fields, so this does not solve the problem at all. Commented Jun 5, 2016 at 19:28
  • Hi, the field [ MailingStreet] you mentioned here is of Text Area type. You if replace it with BillingAddress as it is compound field, it will return TRUE.
    – Soury
    Commented Jun 6, 2016 at 7:02
-3

You'll want to use the isPermissionable() method of the DescribeFieldResult class and only add fields to the Permission set for which the result returns true.

public static Set<string> getPermissionableFields(String objectName){
    Set<String> permissionableFields = new Set<String>(); 

    // Get a map of field name and field token
    Map<String, Schema.SObjectField> fMap = Schema.getGlobalDescribe().get(objectName).getDescribe().Fields.getMap();

    if(fMap != null){
        for(Schema.SObjectField ft : fMap.values()){ 
            Schema.DescribeFieldResult fd = ft.getDescribe(); 
            //check field is creatable
            if(fd.isPermissionable()){
                permissionableFields.add(fd.getName().trim().toUpperCase());
            }
        }
    }         
   return permissionableFields;         
}
1
  • Hi Brock, thanks for the answer. Unfortunately, as I state in the question, "MailingStreet" returns true for isPermissionable(), but cannot be added to a permission set. This answer does not solve the question. Commented Jul 21, 2016 at 17:32

You must log in to answer this question.

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