20 May 2014

That which doesn't limit us makes us stronger

Recently, while writing apex code, I ran up against a number of different limits.

I understand why there needs to be limits with apex. It's a shared development environment and I wouldn't want another organization's code to ever affect my code or organization. That's like protecting me from a neighbor splicing into my cable in order to download movies on my bandwidth.

What helps me manage my limits is when I surface them in the apex code I'm writing.

While playing around with limits, I came up with a quick way to view all of my apex limits and see where I am against those limits.

If you're new to apex limits, try the following:
  1. log into workbench and click on utilities > Apex Execute
  2. copy-paste the following and click Execute:
System.debug('CPU timeout limit: '+Limits.getLimitCPUTime()); 
System.debug('Number of aggregate queries that have been processed with any SOQL query statement: '+Limits.getAggregateQueries()); 
System.debug('Number of aggregate queries that can be processed with SOQL query statements: '+Limits.getLimitAggregateQueries() ); 
System.debug('Number of Web service statements that have been processed: '+Limits.getCallouts()); 
System.debug('total number of Web service statements that can be processed: '+Limits.getLimitCallouts()); 
System.debug('number of child relationship objects that have been returned.: '+Limits.getChildRelationshipsDescribes()); 
System.debug('total number of child relationship objects that can be returned.: '+Limits.getLimitChildRelationshipsDescribes()); 
System.debug('CPU time (in milliseconds) accumulated on the Salesforce servers in the current transaction: '+Limits.getCpuTime() ); 
System.debug('time limit (in milliseconds) of CPU usage in the current transaction.: '+Limits.getLimitCpuTime()); 
System.debug('number of records that have been processed with any statement that counts against DML limits, such as DML statements, the Database.emptyRecycleBin method, and other methods.: '+Limits.getDMLRows()); 
System.debug('total number of records that can be processed with any statement that counts against DML limits, such as DML statements, the database.EmptyRecycleBin method, and other methods.: '+Limits.getLimitDMLRows()); 
System.debug('number of DML statements (such as insert, update or the database.EmptyRecycleBin method) that have been called.: '+Limits.getDMLStatements()); 
System.debug('total number of DML statements or the database.EmptyRecycleBin methods that can be called.: '+Limits.getLimitDMLStatements());System.debug('number of email invocations (such as sendEmail) that have been called.: '+Limits.getEmailInvocations()); 
System.debug('total number of email invocation (such as sendEmail) that can be called.: '+Limits.getLimitEmailInvocations()); 
System.debug('number of field describe calls that have been made: '+Limits.getFieldsDescribes()); 
System.debug('total number of field describe calls that can be made: '+Limits.getLimitFieldsDescribes()); 
System.debug('number of field set describe calls that have been made: '+Limits.getFieldSetsDescribes()); 
System.debug('total number of field set describe calls that can be made: '+Limits.getLimitFieldSetsDescribes()); 
System.debug('number of methods with the future annotation that have been executed (not necessarily completed): '+Limits.getFutureCalls()); 
System.debug('total number of methods with the future annotation that can be executed (not necessarily completed): '+Limits.getLimitFutureCalls()); 
System.debug('approximate amount of memory (in bytes) that has been used for the heap: '+Limits.getHeapSize()); 
System.debug('total amount of memory (in bytes) that can be used for the heap: '+Limits.getLimitHeapSize()); 
System.debug('number of SOQL queries that have been issued: '+Limits.getQueries()); 
System.debug('total number of SOQL queries that can be issued: '+Limits.getLimitQueries()); 
System.debug('number of PicklistEntry objects that have been returned: '+Limits.getPicklistDescribes()); 
System.debug('total number of PicklistEntry objects that can be returned: '+Limits.getLimitPicklistDescribes()); 
System.debug('number of records that have been returned by the Database.getQueryLocator method: '+Limits.getQueryLocatorRows()); 
System.debug('total number of records that have been returned by the Database.getQueryLocator method: '+Limits.getLimitQueryLocatorRows()); 
System.debug('number of records that have been returned by issuing SOQL queries: '+Limits.getQueryRows()); 
System.debug('total number of records that can be returned by issuing SOQL queries: '+Limits.getLimitQueryRows()); 
System.debug('number of RecordTypeInfo objects that have been returned: '+Limits.getRecordTypesDescribes()); 
System.debug('total number of RecordTypeInfo objects that can be returned: '+Limits.getLimitRecordTypesDescribes()); 
System.debug(' number of SOSL queries that have been issued: '+Limits.getSoslQueries()); 
System.debug(' total number of SOSL queries that can be issued: '+Limits.getLimitSoslQueries());
I know that's a lot of system.debug statements, but it turns out there are a lot of limits and a lot of methods to pull out what your limit is versus where you are against your limit.

This is a great way to see all of the various apex limits that are tracked in one place.

Taking this example one step further, I created a new Apex Class called 'LimitsTester' with a single method:

public class LimitsTester{
  public static void tooManyQueries() {
    for (Integer i = 0; i < 200; i++) {
      List<Account> a = [select id from Account Limit 1];
      System.debug('How many queries: '+Limits.getQueries());
      System.debug('Out of how many possible: '+Limits.getLimitQueries());
    }
  }
}
When I execute this apex, it quickly returns an exception that enables me to track when the limit was reached.


All of this is documented but it really helps to surface it early in the development cycle.

12 May 2014

How to get a list of permissions for your organization

I see this question on discussion boards and forums often:

"why doesn't salesforce publish a list of permissions available in my organization?"

The simple answer is because permissions are far more dynamic than what a static list defined in the documentation can enumerate.

User permissions like 'New Report Builder' are dependent on specific organization configurations called organization permissions and organization preferences.

Organization permissions are controlled by salesforce support and organization preferences are controlled by organization administrators. That way, features can be enabled selectively in an organization based on paying for it or requesting it from support but administrators often have the ultimate control to determine whether or not to actually use that feature by going into setup and enabling the preference. This makes introducing new functionality dependent on the administrator's release schedule rather than salesforce.com's upgrade schedule.

All this means is that permissions are dynamic based on what the organization has enabled. By going to a profile or permission set in the setup user interface,  you can see what permissions are assignable to users. However, you can also use the API to generate a list of permissions on the profile or permission sets. It won't include 100% of permissions, but it will include most of them.

On the profile and permissionset subjects is a series of fields that are prefixed with the word, 'Permissions.' For instance, 'PermissionsCustomizeApplication' is the 'Customize Application' permission found on your profile or permission sets.

Some permissions are fairly straight forward like PermissionsModifyAllData. Others take some thought like PermissionsInstallMultiforce which actually grants the access to download AppExchange Packages.

Using a tool like workbench can help you understand which permissions are available in your organization. Just go to Info > Standard & Custom Objects > Choose 'Profile' or 'Permission Set'.

We've heard that administrators and developers want more of these user permissions exposed to the API. With the Spring '14 release, we added the following permissions to version 30.0 of the API:

  • PermissionsAllowEmailIC
  • PermissionsAssignTopics
  • PermissionsChatterInternalUser
  • PermissionsChatterInviteExternalUsers
  • PermissionsChatterOwnGroups
  • PermissionsContentAdministrator
  • PermissionsContentHubUser
  • PermissionsCreateTopics
  • PermissionsCreateWorkspaces
  • PermissionsDeleteTopics
  • PermissionsEditTopics
  • PermissionsEnforceMutualAuthentication
  • PermissionsForceTwoFactor
  • PermissionsHasFileSync
  • PermissionsIdentityEnabled
  • PermissionsIsSsoEnabled
  • PermissionsManageContentPermissions
  • PermissionsManageContentProperties
  • PermissionsManageContentTypes
  • PermissionsManageNetworks
  • PermissionsManageRealm
  • PermissionsModerateChatter
  • PermissionsModerateNetworkFeeds
  • PermissionsModerateNetworkFiles
  • PermissionsTwoFactorApi
  • PermissionsViewContent
  • PermissionsViewGlobalHeader

There are more to add. Until then, continue to use tools like workbench or describeSObject('Profile') in your code to easily find out what permissions are available to you in your organization.