[SalesForce] How to update field-level-security using Metadata-API via Apex (using MetadataService.cls)

I'm trying to grant FLS access using MetadataService.cls (found here https://github.com/financialforcedev/apex-mdapi)

Assume there is a field Test__c on Account with no FLS permissions granted to anyone. Goal is to make it accessible for the "System Administrator" profile.

First you have to use Admin instead of "System Administrator" as described here: How to access the "System Administrator" profile using Metadata-API via Apex (using MetadataService.cls)

Now, following the examples here https://github.com/financialforcedev/apex-mdapi/blob/master/apex-mdapi/src/classes/MetadataServiceExamples.cls I thought this should work:

MetadataService.MetadataPort service = new MetadataService.MetadataPort();
service.SessionHeader = new MetadataService.SessionHeader_element();
service.SessionHeader.sessionId = UserInfo.getSessionId();
MetadataService.Profile profile = (MetadataService.Profile) service.readMetadata('Profile', new String[] { 'Admin' }).getRecords()[0];              
MetadataService.ProfileFieldLevelSecurity fieldSec = new MetadataService.ProfileFieldLevelSecurity();
fieldSec.field='Account.Test__c';
fieldSec.editable=true;
profile.FieldPermissions = new MetadataService.ProfileFieldLevelSecurity[] {fieldSec} ;
service.upsertMetadata( new MetadataService.Metadata[] { profile })

But as an unfortunate, it fails with strange errors (as result values of the last line-of-code above) like:

[ {
   "success_type_info" : [ "success", "http://soap.sforce.com/2006/04/metadata", null, "1", "1", "false" ],
   "success" : false,
   "fullName_type_info" : [ "fullName", "http://soap.sforce.com/2006/04/metadata", null, "1", "1", "false" ],
   "fullName" : "adm 2",
   "field_order_type_info" : [ "created", "errors", "fullName", "success" ],
   "errors_type_info" : [ "errors", "http://soap.sforce.com/2006/04/metadata", null, "0", "-1", "false" ],
   "errors" : [ {
      "statusCode_type_info" : [ "statusCode", "http://soap.sforce.com/2006/04/metadata", null, "1", "1", "false" ],
      "statusCode" : "FIELD_INTEGRITY_EXCEPTION",
      "message_type_info" : [ "message", "http://soap.sforce.com/2006/04/metadata", null, "1", "1", "false" ],
      "message" : "You can't edit tab settings for AddOnLicense, as it's not a valid tab.",
      "fields_type_info" : [ "fields", "http://soap.sforce.com/2006/04/metadata", null, "0", "-1", "false" ],
      "fields" : null,
      "field_order_type_info" : [ "fields", "message", "statusCode" ],
      "apex_schema_type_info" : [ "http://soap.sforce.com/2006/04/metadata", "true", "false" ]
   } ],
   "created_type_info" : [ "created", "http://soap.sforce.com/2006/04/metadata", null, "1", "1", "false" ],
   "created" : false,
   "apex_schema_type_info" : [ "http://soap.sforce.com/2006/04/metadata", "true", "false" ]
} ] 

How this can be modified to update the FLS successfully?

Best Answer

Looking at the error message

You can't edit tab settings for AddOnLicense, as it's not a valid tab.

I found that the issue is the content of profile.tabVisibilities. I could not figure out exactly why the error is happening, but I found a workaround simply omitting the entire profile.tabVisibilities by setting it to an empty list like this:

MetadataService.MetadataPort service = new MetadataService.MetadataPort();
service.SessionHeader = new MetadataService.SessionHeader_element();
service.SessionHeader.sessionId = UserInfo.getSessionId();
MetadataService.Profile profile = (MetadataService.Profile) service.readMetadata('Profile', new String[] { 'Admin' }).getRecords()[0];              
MetadataService.ProfileTabVisibility[] tvs = new MetadataService.ProfileTabVisibility[]{}; 
profile.tabVisibilities = tvs;
MetadataService.ProfileFieldLevelSecurity fieldSec = new MetadataService.ProfileFieldLevelSecurity();
fieldSec.field='Account.Test__c';
fieldSec.editable=true;
profile.FieldPermissions = new MetadataService.ProfileFieldLevelSecurity[] {fieldSec} ;
service.upsertMetadata( new MetadataService.Metadata[] { profile });

Now the result of the last line looks way better:

[ {
   "success_type_info" : [ "success", "http://soap.sforce.com/2006/04/metadata", null, "1", "1", "false" ],
   "success" : true,
   "fullName_type_info" : [ "fullName", "http://soap.sforce.com/2006/04/metadata", null, "1", "1", "false" ],
   "fullName" : "adm 2",
   "field_order_type_info" : [ "created", "errors", "fullName", "success" ],
   "errors_type_info" : [ "errors", "http://soap.sforce.com/2006/04/metadata", null, "0", "-1", "false" ],
   "errors" : null,
   "created_type_info" : [ "created", "http://soap.sforce.com/2006/04/metadata", null, "1", "1", "false" ],
   "created" : false,
   "apex_schema_type_info" : [ "http://soap.sforce.com/2006/04/metadata", "true", "false" ]
} ]

I verified that the tab visibility is not messed-up for the Admin profile at least. IMO it would be nice if it was possible to just readMetadata() some stuff, make changes and upsertMetadata() it again - but it seems that some elements (here profile.tabVisibilities) are not update-able and need to be removed.

Related Topic