Sunday, 30 May 2021

Invocable Methods in Apex

 The Invocable method will allow an Apex method to be executed as an invocable action.


Note :


Use the InvocableVariable annotation to identify variables used by invocable methods in custom classes.


The InvocableVariable annotation identifies a class variable used as an input or output parameter for an InvocableMethod method’s invocable action. 

If you create your own custom class to use as the input or output to an invocable method, you can annotate individual class member variables to make 

them available to the method.


ex :


public with sharing class InvocableApexExample {



  @InvocableMethod(label='Example Invocable Method' description='Example Invocable Method')

    public static List<ContactSearchResult> findRelatedContacts(List<ContactSearchRequest> inputParams) {

        List<SObject> inputs = inputParams[0].inputs;

        String firstRecordId = inputs[0].Id;

        String sObjectType = inputs[0].Id.getSobjectType().getDescribe().getName();

         String queryString = '';

        switch on sObjectType.toLowerCase() {

            when 'account' {

                queryString = 'SELECT Id, Name FROM Contact WHERE accountId = :firstRecordId WITH SECURITY_ENFORCED LIMIT 1';

            }

            when 'task' {

                Task task = (Task) inputs[0];

                String whoId = task.WhoId;

                queryString = 'SELECT Id, Name FROM Contact WHERE Id = :whoId WITH SECURITY_ENFORCED LIMIT 1';

            }

            when else {

                throw new InvocableMethodRecipesException(

                    'Unknown object type passed. This method only supports Account and Task.'

                );

            }

        }

        

         Contact contact = Database.query(queryString);

         List<ContactSearchResult> results = new List<ContactSearchResult>();

         results.add(new ContactSearchResult(contact));

        

        return results;

    }

  

  

  // This class Represents Input to the InvocableMethod 

 

 Public class ContactSearchRequest { 

    @InvocableVariable(label='Generic SObject records - Input' description='Input variable for generic Sobjects' required=true)

    Public List<SObject> inputs; 

 }

 

 // This class Represents output from the InvocableMethod

 

  public class ContactSearchResult {

        @InvocableVariable(label='Generic SObject record - Output' description='Output variable for generic SObject' required=true)        

        public SObject output;       

        public ContactSearchResult(SObject toOutput) {

            this.output = toOutput;

        }

    }

    

    // Internal custom exception class

 

   public class InvocableMethodRecipesException extends Exception {

   

     }

 

}



Implementation Notes :


1.The invocable method must be static and public or global, and its class must be an outer class.

2.Only one method in a class can have the InvocableMethod annotation.

3.Other annotations can’t be used with the InvocableMethod annotation.


Inputs and Outputs :


There can be at most one input parameter and its data type must be one of the following:


1.A list of a primitive data type or a list of lists of a primitive data type – the generic Object type is not supported.

2.A list of an sObject type or a list of lists of an sObject type.

3.A list of the generic sObject type (List<sObject>) or a list of lists of the generic sObject type (List<List<sObject>>).

4.A list of a user-defined type, containing variables of the supported types above or user-defined Apex types, with the InvocableVariable annotation. 

Create a custom global or public Apex class to implement your data type, and make sure that your class contains at least one member variable 

with the invocable variable annotation.


If the return type is not Null, the data type returned by the method must be one of the following:


1.A list of a primitive data type or a list of lists of a primitive data type – the generic Object type is not supported.

2.A list of an sObject type or a list of lists of an sObject type.

3.A list of the generic sObject type (List<sObject>) or a list of lists of the generic sObject type (List<List<sObject>>).

4.A list of a user-defined type, containing variables of the supported types above or user-defined Apex types, with the InvocableVariable annotation. 

Create a custom global or public Apex class to implement your data type, and make sure that your class contains at least one member variable 

with the invocable variable annotation.


Managed Packages :


1.You can use invocable methods in packages, but once you add an invocable method you can’t remove it from later versions of the package.

2.Public invocable methods can be referred to by flows and processes within the managed package.

3.Global invocable methods can be referred to anywhere in the subscriber org. Only global invocable methods appear in Flow Builder 

and Process Builder in the subscriber org.

Field History Tracking records in Salesforce

 If field history tracking is enabled for an object, the changes are stored in history table for that particular object.


The API name of these tables follows a simple convention so should be easy to find. For standard objects, 

the name of the history table follows the format: 'ObjectNameHistory' so for the Account object the history table is AccountHistory.


For custom objects, the name of the convention simply replaces the 'c' on the end of the API name with 'History'. 

So, for a custom object call My_Custom_Object__c the history table is called My_Custom_Object__History.


ex :


SELECT AccountId, OldValue, NewValue, IsDeleted, Id, Field, CreatedBy.Name From AccountHistory WHERE AccountId=:accountId ORDER BY CreatedDate DESC


SELECT Id, Name, (Select OldValue, NewValue From Histories) FROM My_Custom_Object__c


SELECT OldValue, NewValue, Parent.Id, Parent.name, Parent.customfield__c FROM My_Custom_Object__history



SELECT Name, customfield__c, (SELECT ParentId, OldValue, NewValue,Field FROM Histories ) FROM Custom_Object__c


Note : The child history relationship name is called "histories".


SELECT ID, Subject, Type, (SELECT Field, OldValue, NewValue FROM Histories) FROM Case



Field History Tracking in Salesforce :


Out of the box Salesforce can track up to 20 fields per object using field history and retains that information for 18 months. 

The benefit of this is displayed on the record in a related list and is even reportable.



Field history data is retained for up to 18 months through your org, and up to 24 months via the API. 

Field history tracking data doesn't count against your Salesforce org's data storage limits.

You can track the field history of custom objects and the following standard objects.


Limitations Of Using Field History Tracking


1.You can only track 20 fields per object.

2.For some fields like Text Area (Long), Text Area (Rich), and Picklist (Multi-Select) and Changes to fields with more than 255 characters are tracked as edited,

 and their old and new values are not saved. 

3.If a trigger causes changes to objects for which the user does not have permission to edit, the change is not tracked. 

 Field history takes into account current user permissions.

 

 


Field Audit Trail : 


Field Audit Trail uses a similar experience but extends those limits to track up to 60 fields and will retain as much as ten years.


Field Audit Trail is a separate add-on feature.It also comes under the Salesforce Shield Product.


Retain Field History for upto 10 years with Field Audit Trail.


Salesforc Shield :


To protect the application data and maintain the customer trust, Salesforce Introduced Salesforce Shield. Salesforce Shield offers a powerful set of tools 

for any customer who needs an extra level of control to meet internal or regulatory compliance requirements.


Salesforce Shield is made up of three components :


1.Platform Encryption

2.Event Monitoring

3.Field Audit Trail


Platform Encryption :


Platform Encryption allows you to encrypt fields, files, and attachments stored in the Salesforce1 Platform. In contrast to Classic Encryption, 

which uses a custom field type in the Salesforce data model, Platform Encryption allows you to encrypt several standard fields and 

custom field types using metadata while preserving functionality necessary to perform common business tasks in Salesforce.


Note : Natively Encrypt sensitive data


Event Monitoring:


Event monitoring lets you to easily see what data users are accessing, from what IP address, and the actions done to that data. 

For Example API calls, user logins, users who are running reports, exporting reports, downloading files, and etc.


Note : Detailed data & Monitoring


Field Audit Trail :


It is used to track information about who, when and what data has changed. Current data retention period is 18 months and we can track up to 20 fields only,

but by upgrading to Field Audit Trail with History tracking, we can track up to 60 fields and can retain data for 10 years.


Note : Prevents Data loss


Different AJAX action tags in visualforce page

 1.apex:actionFunction

2.apex:actionPoller

3.apex:actionRegion

4.apex:actionStatus

5.apex:actionSupport


ActionRegion :


ActionRegion tells to Force.com server which components shoud be processed.

Whatever inside an ActionRegion is processed by a server when AJAX request is generated

on event such as "KeyPress" or "onClick" etc.


Note : 


Sigifies which components should be processed by the server when an AJAX request is generated.


ActionRegion defines the source components that needs to be processed at the server end when the request is sent. 

(or)


ActionRegion tag defines which componnets of VF page should be processed by Force.com server.


By Components  means, all the visualforce tags like inputField, inputText, outputPanels etc.


 ex :

 

 <apex:pageBlock title="Page block" mode="edit" id="infoblock">


        <apex:pageBlockSection title="Information" columns="1">

          <apex:actionRegion> 

               <apex:inputField value="{!myCustomObject.Opportunity__c}">              

                  <apex:actionSupport event="onchange" action="{!changeOpportunity}" rerender="additionalinfo"/>              

               </apex:inputField>

          </apex:actionRegion>    


          <apex:inputField value="{!myCustomObject.MyRequiredField__c}" required="true" /> 


        </apex:pageBlockSection>



        <apex:pageBlockSection title="Additional Info" columns="1" id="additionalinfo">         

                <apex:inputField value="{!myCustomObject.Field1__c}" />

                <apex:inputField value="{!myCustomObject.Field3__c}" />

                <apex:inputField value="{!myCustomObject.Field3__c}" />

                <apex:inputField value="{!myCustomObject.Field4__c}" />         

        </apex:pageBlockSection>


 </apex:pageBlock>

 

ActionSupport :


ActionSupport adds AJAX support to another component, allowing the component to be refreshed asynchronously by the server 

when a particular event occurs, such as a button click or mouseover.


ex 1: 


Used when we want to perform an action on a perticular eventof any control like onchange of any text box or picklist.


<apex:inputText value="{!dummyString}" >

<apex:actionSupport event="onchange" action="{!actionSupportTest}" reRender="pgBlock, pbSection" />

</apex:inputText> 


ex 2 :

 

 <apex:page controller="exampleCon">

<apex:form>

<apex:outputpanel id="counter">

<apex:outputText value="Click Me!: {!count}"/>

<apex:actionSupport event="onclick"

action="{!incrementCounter}"

rerender="counter" status="counterStatus"/>

</apex:outputpanel>

<apex:actionStatus id="counterStatus"

startText=" (incrementing...)"

stopText=" (done)"/>

</apex:form>

</apex:page>


public class exampleCon {

Integer count = 0;

public PageReference incrementCounter() {

count++;

return null;

}

public Integer getCount() {

return count;

}

}



ActionStatus :


ActionStatus is usually used to show the status of an AJAX Process to which it is related to.


ex:

ActionStatus showing text during the update the page and showing a loading gif.


<apex:page controller="AccountFiltctr">

  <apex:actionStatus id="ProgStatusId" rendered="{!actionstatusrendered}">

             <apex:facet name="start">

             <div class="waitingSearchDiv" id="el_loading" style="background-color: #fbfbfb;height: 100%;opacity:0.65;width:100%;"> 

             <div class="waitingHolder" style="top: 74.2px; width: 91px;">

             <img class="waitingImage" src="/img/loading.gif" title="Please Wait..." />

             <span class="waitingDescription">Please Wait...</span>

             </div>

             </div>

             </apex:facet>                           


           </apex:actionStatus>

<apex:form id="frm">

    <apex:commandButton value="Test" status="ProgStatusId" reRender="pb1"/>

</apex:form>

</apex:page>


ActionFunction :


ActionFunction is a component, which provides support for invoking controller action 

methods directly from javascript code with the use of AJAX request.


ex :


<apex:page controller="MyController">

    <apex:form >

        <apex:actionFunction name="getAccountMappedFunction" action="{!getAccount}" oncomplete="doOnComplete('{!MyAccount.Name}');">

            <apex:param name="accountName" value="GenePoint"/>

        </apex:actionFunction>

    </apex:form>


    <script >

        getAccountMappedFunction();


        function doOnComplete(name)

        {

            alert(name);

        }

    </script >

</apex:page>


public with sharing class MyController

{

    public Account MyAccount {get; set;}


    public PageReference getAccount()

    {

        String accountName = Apexpages.currentPage().getParameters().get('accountName');

        MyAccount = [SELECT Name FROM Account WHERE Name = :accountName];

        return null;

    }

}


Note :


Allows for controller methods to be called directly from javascript.

Must be encapsulated in <apex:form> tags.


ActionPoller :


ActionPoller sends the AJAX request to the server periodically according to specified time interval.


Note :

A timer that sends an AJAX request to the server according to a time interval that you specify. 

Each request can result in a full or partial page update.




ex :


<apex:page controller="exampleCon" sidebar="false">

 <apex:form >

        <apex:outputText value="Watch the changing fruit: {!count} : {!currentFruit}" id="counter"/>

        <apex:actionPoller action="{!incrementCounter}" reRender="counter" interval="6"/>

    </apex:form>

</apex:page>


public class exampleCon {


private List<String> fruitList = new List<String>();

    

    Integer count = 0;

    

    // Fruit name to be displayed

    public String currentFruit {

        get{

            currentFruit = fruitList[count];

            return currentFruit; 

        } 

        set;

    }

    

    // Increment the counter value

    public PageReference incrementCounter() {

        if(count< 6) {

        count++;    

        } else {

            count = 0; 

        }

        

        return null;

    }

    

    

    public Integer getCount() {

        return count;

    }

    

    // Constructor define the list of fruits

    public exampleCon () {

        fruitList.add('Mango');

        fruitList.add('Apple');

        fruitList.add('Pineapple');

        fruitList.add('Banana');

        fruitList.add('Orange');

        fruitList.add('Grape');

        fruitList.add('Peach');

    }


}


Inbound Email Services in Salesforce

 Email Services are automated processes that use Apex classes to process the contents,

headers and attachments of inbound emails.


ex :

You can create an email service that automatically creates contact records based on contact information in messages.

Each eamil service can have one or more email service addresses that can receive messages for processing.


ex :


global class CreateTaskEmailExample implements Messaging.InboundEmailHandler {

 

  global Messaging.InboundEmailResult  handleInboundEmail(Messaging.inboundEmail email, Messaging.InboundEnvelope env) {

  

  // Create an InboundEmailResult object for returning the result of the 

    // Apex Email Service

    Messaging.InboundEmailResult result = new Messaging.InboundEmailResult();

  

    String myPlainText= '';

    

    // Store the email plain text into the local variable 

    myPlainText = email.plainTextBody;

   

    // Create a new Task object 

    Task[] newTask = new Task[0];

   

    // Try to look up any contacts based on the from email address.

    // If there are more than one contacts with the same email address,

    // an exception will be thrown and the catch statement block will be executed.

    try 

    {

      Contact vCon = [SELECT Id, Name, Email FROM Contact WHERE Email = :email.fromAddress   LIMIT 1];

      

      // Add a new Task to the contact record we just found above.

      newTask.add(new Task(Description =  myPlainText,

           Priority = 'Normal',

           Status = 'Inbound Email',

           Subject = email.subject,

           IsReminderSet = true,

           ReminderDateTime = System.now()+1,

           WhoId =  vCon.Id));

     

     // Insert the new Task 

     insert newTask;    

     

     System.debug('New Task Object: ' + vCon.name+vCon.id);   

    }

    // If an exception occurs when the query accesses 

    // the contact record, a QueryException is thrown.

    // The exception is written to the Apex debug log.

   catch (QueryException e) {

       System.debug('Query Issue: ' + e);

   }

   

   // Set the result to true. No need to send an email back to the user 

   // with an error message

   result.success = true;

   

   // Return the result for the Apex Email Service

   return result;

   }

   

}


ex 2 :


global class ProcessJobApplicantEmail implements Messaging.InboundEmailHandler {


  global Messaging.InboundEmailResult handleInboundEmail(Messaging.InboundEmail email,

    Messaging.InboundEnvelope envelope) {


    Messaging.InboundEmailResult result = new Messaging.InboundEmailresult();


    Contact contact = new Contact();

    contact.FirstName = email.fromname.substring(0,email.fromname.indexOf(' '));

    contact.LastName = email.fromname.substring(email.fromname.indexOf(' '));

    contact.Email = envelope.fromAddress;

    insert contact;


    System.debug('====> Created contact '+contact.Id);


    if (email.binaryAttachments != null && email.binaryAttachments.size() > 0) {

      for (integer i = 0 ; i < email.binaryAttachments.size() ; i++) {

        Attachment attachment = new Attachment();

        // attach to the newly created contact record

        attachment.ParentId = contact.Id;

        attachment.Name = email.binaryAttachments[i].filename;

        attachment.Body = email.binaryAttachments[i].body;

        insert attachment;

      }

    }


    return result;


  }


}


Saturday, 29 May 2021

Exceptions that Can’t be Caught in apex

 These below exceptions are uncatchable, catch blocks, as well as finally blocks if any,

aren't executed.


1.System.LimitException

2.System.assert

3.license exceptions


Throws runtime exceptions if a governor limit has been exceeded, such as when the maximum number of SOQL queries issued has been exceeded.

Other examples are exceptions thrown when assertion statements fail (through System.assertmethods) or license exceptions.


Friday, 28 May 2021

Salesforce Debug Logs

 Salesforce provides debug logs to track the events occurring in a Salesforce organization. 

It contains information about the transactions such as whether it was successful and how much time it took to complete the transaction. 

Debug logs are generated based on the ‘User Trace Flag’ in Debug Logs. Debug logs are generated by the system when a user performs a transaction.


1.Debug logs are used to track the events information that occurs in your org.

2.Debugs log, will only be generated when you have an active flag turned on for the logged in user.


Debug Log Category :


Information is logged based on the category called Log Category. Below are the different log categories.


Database     : Includes information about database activity, including every data manipulation language (DML) statement or inline SOQL or SOSL query.

Workflow     : Includes information for workflow rules, flows, and processes, such as the rule name and the actions taken.

Validation     : Includes information about validation rules, such as the name of the rule and whether the rule evaluated true or false.

Callout         : Includes the request-response XML that the server is sending and receiving from an external web service. Useful when debugging issues related to using Lightning Platform web service API calls or troubleshooting user access to external objects via Salesforce Connect.

Apex Code     : Includes information about Apex code. Can include information such as log messages generated by DML statements, inline SOQL or SOSL queries, the start and completion of any triggers, and the start and completion of any test method.

Apex Profiling  : Includes cumulative profiling information, such as the limits for your namespace and the number of emails sent.

Visualforce     : Includes information about Visualforce events, including serialization and deserialization of the view state or the evaluation of a formula field in a Visualforce page.

System         : Includes information about calls to all system methods such as the System.debug method.


What is a Trace Flag?


Trace flag are used to filter the log. Each trace flag includes a debug level, start time, end time, and log type. 

The trace flag’s log type specifies the entity you’re tracing


Salesforce Debug Log Levels :


you can also specify one of the following logging levels.


1.NONE

2.ERROR

3.WARN

4.INFO

5.DEBUG

6.FINE

7.FINER

8.FINEST


Log level determines the amount of information logged when an event is triggered. There are different log levels for each log category. 

The levels are listed below from lowest to highest.



Error, Warn, Info   : Includes error, warning, and information messages.

Debug                   : Includes lower-level messages, and messages generated by calls to the System.debug method.

Fine, Finer           : Includes log messages generated by calls to the System.debug method, every DML statement or inline SOQL or SOSL query, and the entrance and exit of every user-defined method. In addition, the end of the debug log contains overall profiling information for the portions of the request that used the most resources. These resources include SOQL and SOSL statements, DML operations, and Apex method invocations.

Finest               : Includes all messages generated by the Fine or Finer log levels, as well additional information on Apex scripts, including the following.


what do you do when your debug session reaches 2MB?


There are only a few options


1.Reduce the log level details for the user you're debugging.

2.Trace flag the noisiest classes into submission.


Trace Flags :


In order to generate debug logs, we need to set trace flags.

Trace Flags can be set for Apex Classes ,Triggers and Users.


Limitations of a Debug Log :


1.Each debug log must be 5 MB or smaller. Debug logs that are larger than 5 MB are reduced in size by removing older log lines, such as log lines for earlier System.debug statements. The log lines can be removed from any location, not just the start of the debug log.

2.System debug logs are retained for 24 hours. Monitoring debug logs are retained for seven days.

3.Trace flag will be disabled when a debug log more than 250 MB is generated in a 15-minute window. 

An email will be sent to the user who last modified the trace flag so that, they can re-enable it.

4.When your org accumulates more than 250 MB of debug logs, we prevent users in the org from adding or editing trace flags. 

To add or edit trace flags so that you can generate more logs after you reach the limit, delete some debug logs.

Thursday, 27 May 2021

SOQL Performance Tips and Tricks

SOQL is a tool that lets you access records in your Salesforce database.

when you don't write good SOQL queries you're going to hit the governor limit of non selective query.


Query Error : 'System.QueryException : Non-selective query against large object type' 


System.QueryException :


Non-selective query against large object type(more than 100000 rows).

Consider an indexed filter or contact salesforce.com about custom indexing.

Even if a field is indexed a filter might still not be selective when :

1.The filter value includes null(for instance binding with that list that contains null).

2.Data skew exists whereby the number of matching rows is very large

(for instance,filtering for a particular foreign key value that occurs many times ).


To avoid this error,ensure that the SOQL query is selective.


What is a selective query?


A selective query is a query that leverages indexes in filters to avoid full table scans 

and to reduce the number of records in your result set below the selectivity threshold.


Selective SOQL Query Criteria :


1.A query is selective when one of the query filters is on an indexed field.

2.The query filter reduces the resulting number of rows below a system-defined threshold. 

3.The performance of the SOQL query improves when two or more filters used in the 

WHERE clause meet the mentioned conditions.

4. For a standard index, the threshold is 30 percent of the first million targeted records and 15 percent of all records after that first million. 

In addition, the selectivity threshold for a standard index maxes out at 1 million total targeted records

5.For a custom index,the selectivity threshold is 10% of the first million records and less than 5% of the records 

after the first million records,up to a maximum of 3,33,333 records.

6.In some circumstances, for example with a query filter that is an indexed standard field, 

the threshold can be higher.Also, the selectivity threshold is subject to change.

7.If the filter exceeds the threshold, it won’t be considered for optimization.

8.If the filter doesn’t exceed the threshold, this filter is selective, and the query optimizer will consider it for optimization.


How to know if filter is index?


Primary key present on each object (Id, Name, OwnerId), a foreign key (CreatedById, LastModifiedById, lookup, master-detail relationship), 

and an audit field (CreatedDate, SystemModstamp) are index fields.


If parameter value of index field is null in SOQL, then it won’t be considered as an index field. 

Custom fields will have an index if they have been marked as Unique or External Id. 

Custom index can be enabled by contacting Salesforce.com on non-index fields.


Note :

Salesforce has various rules around when an index will be used or not based on its selectivity.

An indexed field will not be used under the following conditions:

1.Not In (….)

2.Like with wildcards "%"

3.!=

4.Excludes for picklist values

5.Not Like

6.Comparison Operators Paired With Text Fields. Text_Field <, >, <=, >=


Note :


When your filter uses != or NOT which includes using NOT EQUALS/CONTAINS for reports, even if the field is indexed

the Force.com query optimizer can’t use the index to drive the query. For better performance, filter using = or IN,

and the reciprocal values.



ex : 


1.When Index field is used in SOQL properly


Select id, name from Account where Account_Code__c =’ProQuest’


Analysis :


You can observe the cost of SOQL. It is 0.0000970 which is very low and will be best to use.


2.When non Indexed field is used in SOQL


Select id, name from Account where Mobile_Phone__c = ‘00000’


Analysis


You can observe the cost of SOQL. It is 0.84 which is much higher. 

Although there are only 50 records found which have this phone no. It is not ideal to use this SOQL.



3.Passing null value in parameters


select id from Opportunity where Booking_External_Id__c IN (‘B10098’,null)


Analysis


This is the common problem what developers do. 

Although index field is used but it can’t optimize SOQL because null is present in a parameter. 

Query optimizer will do Table scan instead of applying index



Advantage of Selective Filters :


1.Reduces the number of records in your result set.

2.Leverages indexes.

3.Avoids full table scans.


Note :

1.Selectivity thresholds determine if an index is considered.

2.Not Equals filters will not leverage indexes.

3.Be careful filtering on Null.

4.And conditions involve an INTERSECTION of indexes.

5.OR conditions involve an ADDITION of indexes.

6.ORDER BY with a LIMIT on an index can make up for non-selective filters.


Note :

1.Query Performance improves with indexes.

2.Use selective filters to reduces result set.

3.Query Optimizer chooses the best table/index to drive a query 

4.Skinny Tables may help when indexing is exhausted.


Skinny Table :


1.Contact Salesforce to enable it.

2.Can contain only 100 fields.

3.Change in field type used in Skinny table would make it invalid.

4.Skinny Table is not copied to sandbox after refresh (Except Full Copy Sandbox).

5.Useful for read operations.

6.Cannot contain fields from other object/Parent.



Selective Query Definition :


Selectivity is determined by the index state of the query filter conditions 

and the number of records the filter returns (Selectivity) versus the object total records.

The thresholds below show the difference between the selectivity calculation for a standard index versus a custom index.


Selectivity Thresholds :


Standard Index - 30% (first 1M) then 15%

Custom Index   - 10% (first 1m) then 5%


Unary filter :


ex : SELECT Name FROM Account where IndexedField__c='ABC'


With a custom index on IndexedField__c the filter must return <10% of the total records

in the object to be considered selective - up to the first 1 million records from that point the threshold drops to 5%.


Multiple filters AND (exclusively):


ex : SELECT Name FROM Account WHERE IndexedField__c='ABC' AND SecondIndexedField__c='123'


The Query Optimiser will set the leading operation on the basis of lowest cost.

if no filters are selective a table  scan is performed.


If all filters have an index then a Composite Index Join optimization can be applied.


In this case each filter must be less than 2x(two-times) the selectivity threshold.

All filters combined must be less than selectivity threshold.


If all filter fields are standard then use the standard index selectivity threshold-otherwise use custom index selectivity threshold.


Multiple filters OR ( at least one) :


ex : SELECT Name FROM Account WHERE IndexedField__c='ABC' OR SecondIndexedField__c='123'


Selective AND filter indexes cloud be set as the Leading Operation - if none exist,then a table scan occurs

unless all filters have an index then a Composite Index Union optimisation becomes possible.


In this case each filter must be less than selectivity threshold.

All filters combined must be less than selectivity threshold.


If all fields are standard then use the standard index selectivity threshold-otherwise use the custom index selectivity threshold.


Note : When using OR condition, all filters must be indexed and under the 10% threshold.


Parent Field Filter :


 ex : SELECT Name FROM Contact WHERE IndexedField__c=’ABC’ and Account.IndexedField__c=’ABC’


Where parent object fields are referenced in a filter, each filter index is individually and the lowest cost option selected as the leading operation.


Note, the parent field is not indexed on the queried object, so Account.Id can incur a table scan on Opportunity 

whereas AccountId may allow the standard index to become the leading operation.


Evaluate the Selectivity :

==========================


In order to evaluate the selectivity of a given query – the following 2 approaches can be applied.


1.REST API Query Resource Feedback Parameter

2.Developer Console Query Plan


Query Resource Feedback Parameter :


The Force.com REST API exposes a Query resource that accepts an explain parameter which can set with a SOQL query, List View Id or Report Id. 

The results show the options considered by the Query Optimiser and the lowest cost option (leading operation) taken. 

A relative cost value of less than 1 indicates a selective query, anything higher indicates non-selective


ex :


/services/data/v52.0/query/?explain=

SELECT+Name+FROM+Merchandise__c+WHERE+CreatedDate+=+TODAY+AND+Price__c+>+10.0


/services/data/v52.0/query/?explain=00Ow0000007lLgO



Query Plan in Salesforce :


The Query Plan tool shows the cost of Salesforce executing a given SOQL query given the database 

statistics know at that time.


The new Query Plan tool in the Developer Console can help speed up SOQL queries done over large volumes. 

Use the Query Plan tool to optimize and speed up queries done over large volumes.


The Query Plan tool will show a list of available plans that our Query Optimizer can utilize for the query provided 

and will be arranged by cost ascending. Each Plan will contain information on Cardinality, Operation Type, Cost, sObject Type, and more. 

Each plan has a “Leading Operation Type”, for example, Field Index or Full Table Scan. 

The plan with the lowest cost is the plan that is used for driving the query execution.


The Query Plan window displays all query operations and the cost of each.


1.Cardinality – The estimated number of records that will be returned by the query.

2.Fields – The indexed field(s) used by the Query Optimizer. If the leading operation type is Index, the fields value is Index. 

Otherwise, the fields value is null.

3.Leading Operation Type – The primary operation type that Salesforce will use to optimize the query.

 a.Index – The query will use an index on the queried object.

 b.Sharing – The query will use an index based on the sharing rules associated with the user who is executing the query. 

  If there are sharing rules that limit which records that user can access, Salesforce can use those rules to optimize the query.

 c.TableScan – The query will scan all records for the queried object.

 d.Other – The query will use optimizations internal to Salesforce.

4.Cost – The cost of the query compared to the Force.com Query Optimizer’s selectivity threshold. 

Values above 1 mean that the query won’t be selective.

5.SObject Cardinality – The approximate record count for the queried object.

6.Sobject Type - The name of the query object.


How Query plan calculates cost?

Each plan has its own cost value. The cost value is derived from the latest gathered database (DB) statistics on the table and values. 

The plan with the lowest cost will be the plan used. If the Cost is above 1, it means that the query won’t be selective. 

There are below reasons for not showing index fields in Query plan.


1.index will never be used when comparisons are being done with an operator like “NOT EQUAL TO”

2.index will never be used when comparisons are being done with a null value like “Name = ””

3.Leading ‘%’ wildcards are inefficient operators that also make filter conditions non-selective

4.When using an OR comparison, all filters must be indexed for optimized results.


Note :

Don't forget about deleted records in your recycle bin.


Did you know that deleted records can effect your query performance.


2 ways to solve this problem :


1.Add isDeleted = false to your queries.This field is available on all standard and custom objects.

2.Empty the recycle bin.


Note :


MAX # of Master-Detail Relationship Fields, per Object (Standard or Custom) -- 2

MAX # of total Relationship Fields (Master-Detail + Lookup), per Object (Standard or Custom) -- 40


Relationship Query :


1.Child to parent


SELECT Id,Name,Broker__c,Broker__r.Id,Broker__r.Region__c FROM Property__c WHERE Status__c='Available'


2.Parent to Child 


SELECT Id,Name,Region__c,(SELECT Id,Name,Status__c FROM Properties__r) FROM Broker__c WHERE Region__c='North'


Querying for Intersection :


SELECT Id,Name,Broker__r.Name,Broker__r.Email__c FROM Property__c WHERE Broker__r.Region__c='North'


Aggregate Queries :


SELECT City__c,COUNT(Id) houseCount,MAX(Price__c),AVG(Price__c) avgValue FROM Property__c GROUP BY City__c


Wednesday, 26 May 2021

Force.com Query Optimizer (Lightning Platform Query Optimizer)

 Force.com Query Optimizer is the primary command center in Force.com Platform that is responsible for executing the SOQL/Filter statements and returning the data back to the code/Front-end.

The Force.com query optimizer tool optimizes the SOQL used to fetch data by inspecting SOQL,Reports and List Views.

The Force.com query optimizer is an engine that sits between your SOQL ,reports and list views and the database itself.

Because of salesforce.com's multitenancy, the optimizer gathers its own statistics instead of relying on the underlying database statistics.

Pre-computed Statistics contains the following information's.

1.Row Count

2.User Visibility

3.Custom index

4.Owner row Count


First, Force.com executes “pre-queries” that consider the multitenant-aware statistics. Then, using the results returned by the pre-queries, the service builds an optimal underlying database query for execution in the specific setting. Using both these statistics and pre-queries, the optimizer generates the most optimized SQL to fetch your data. It looks at each filter in your WHERE clause to determine which index, if any, should drive your query.

To determine if an index should be used to drive a query, the Force.com query optimizer checks the number of records targeted by the filter against selectivity thresholds.

Lightning Platform Query Optimizer :

When a developer invokes SOQL, it’s the responsibility of a technology called the query optimizer to translate it into SQL. The query optimizer dynamically adapts its query to select and join tables in the most efficient way possible depending on the filters, fields, and other properties involved in the query. It also enforces sharing and record access rules to guarantee data privacy. Finally, it ensures the underlying SQL query only retrieves records that belong to the customer environment that issued the SOQL request.

To ensure that your queries are selective, avoid some common pitfalls.

1.Understand your schema and have proper indexes created.
2.Apply as many filters as possible to reduce the result set.
3.Minimize the amount of records in the Recycle Bin.
4.Remember that NOT operations and LIKE conditions with a leading % wildcard do not use indexes, and complex joins might perform better as separate queries.

Saturday, 22 May 2021

Debugging Jest Tests with Chrome Developer Tools

 1.Debugging Jest Tests with the Salesforce Extensions.

2.Debugging Jest Tests with Chrome Developer Tools.

3.Debugging Jest Tests with VSCode debugger advanced configuration.


To debug Jest unit test, we need to modify package.json.


{

    "scripts": {

        "test:unit": "sfdx-lwc-jest",

        "test:unit:watch": "sfdx-lwc-jest --watch",

        "test:unit:debug": "sfdx-lwc-jest --debug",

        "test:unit:coverage": "sfdx-lwc-jest --coverage"

    }

}


Note :

"test:unit" runs all your tests.

"test:unit:watch" and "test:unit:debug" run jest in watch and debug mode.

"test:unit:coverage" collect coverage and display in output


Run Tests on the Command Line :


1.npm run test:unit

2.npm run test:unit:debug -- helloConditionalRendering



Debug Mode In browser


Debug mode lets you easily debug your jest tests.


1.Put a debugger; into your code.

2.Open chrome://inspect

3.Run sfdx-lwc-jest with the --debug flag.


Pass other parameters to jest after the -- flag.


ex: sfdx-lwc-jest --debug -- --no-cache


Tuesday, 18 May 2021

Working with Picklists in Apex and Lightning Web Components

 Working with Picklists in Apex and Lightning Web Components 


1.Use dynamic Apex when working with records with no record type.

2.Use UI API callouts when working with record types in Apex.

3.Use the UI API getPicklistValues wire adapter when working with Lightning Web Components.

4.Use an Apex utility class like PicklistUtils to avoid code duplication.




--> Using Picklist in Apex 


ex : 


global with sharing class PicklistUtils {

    // Cache for dynamic Apex calls

    private final static Map<String, List<Schema.PicklistEntry>> apexCache = new Map<String, List<Schema.PicklistEntry>>();

    

     public static List<Schema.PicklistEntry> getPicklistValues(sObjectType sObjectType,Schema.sObjectField field ) {

        // Try to get entries from cache

        String cacheKey =

            String.valueOf(sObjectType) +

            '.' +

            String.valueOf(field);

            

            

        List<Schema.PicklistEntry> entries = apexCache.get(cacheKey);

        

        if (entries == null) {

            // Get picklist values

            entries = field.getDescribe().getPicklistValues();

            // Exclude inactive picklist values

            entries = getActivePicklistEntries(entries);

            // Cache entries

            apexCache.put(cacheKey, entries);

        }

        return entries;

    }

    

     private static List<Schema.PicklistEntry> getActivePicklistEntries(

        List<Schema.PicklistEntry> entries

    ) {

        List<Schema.PicklistEntry> activeEntries = new List<Schema.PicklistEntry>();

        for (Schema.PicklistEntry entry : entries) {

            if (entry.isActive()) {

                activeEntries.add(entry);

            }

        }

        return activeEntries;

    }


    global class PicklistEntries {

        global PicklistEntry defaultValue;

        global List<PicklistEntry> values;

    }


    global class PicklistEntry {

        global String label;

        global String value;

    }

    

 }


it has three important limitations 


1.It relies on hard-coded object and field names.

2.It returns values that include inactive picklist values.

3.It doesn't handle values for specific record types.


--> Generic Helper method


global with sharing class PicklistUtils {

    // Cache for dynamic Apex calls

    private final static Map<String, List<Schema.PicklistEntry>> apexCache = new Map<String, List<Schema.PicklistEntry>>();

   

   

   global static List<Schema.PicklistEntry> getPicklistValues(String objectName,String fieldName) {

   

        // Try to get entries from cache

        String cacheKey = objectName + '.' + fieldName;

        List<Schema.PicklistEntry> entries = apexCache.get(cacheKey);

        if (entries == null) {

            // Use dynamic Apex to get object description

            // Schema.describeSObjects throws System.InvalidParameterValueException if objectName is invalid

            Schema.DescribeSobjectResult objDescription = Schema.describeSObjects(

                new List<String>{ objectName }

            )[0];

            // Retrieve field map and check that field exists

            Schema.SObjectField field = objDescription.fields.getMap()

                .get(fieldName);

            if (field == null) {

                InvalidParameterValueException e = new InvalidParameterValueException(

                    'fieldName',

                    fieldName

                );

                e.setMessage(

                    'Could not find field ' +

                    fieldName +

                    ' on object ' +

                    objectName

                );

                throw e;

            }

            // Get picklist values

            entries = field.getDescribe().getPickListValues();

            // Exclude inactive picklist values

            entries = getActivePicklistEntries(entries);

            // Cache entries

            apexCache.put(cacheKey, entries);

        }


        return entries;

    }

    

     private static List<Schema.PicklistEntry> getActivePicklistEntries(

        List<Schema.PicklistEntry> entries

    ) {

        List<Schema.PicklistEntry> activeEntries = new List<Schema.PicklistEntry>();

        for (Schema.PicklistEntry entry : entries) {

            if (entry.isActive()) {

                activeEntries.add(entry);

            }

        }

        return activeEntries;

    }


    global class PicklistEntries {

        global PicklistEntry defaultValue;

        global List<PicklistEntry> values;

    }


    global class PicklistEntry {

        global String label;

        global String value;

    }

   

   

   }


it has one important limitations :


1.It doesn't handle values for specific record types.


--> UI API endpoint


if you need to work with picklist values for specific record types, you'll need 

to do a callout to this UI API endpoint.




ex :


global with sharing class PicklistUtils {


 // Cache for UI API calls

    private final static Map<String, PicklistEntries> uiApiCache = new Map<String, PicklistEntries>();



   global static PicklistEntries getPicklistValues(String objectName,Id recordTypeId,String fieldName) {


        String requestUrl =

            URL.getSalesforceBaseUrl().toExternalForm() +

            '/services/data/v51.0/ui-api/object-info/' +

            objectName +

            '/picklist-values/' +

            recordTypeId +

            '/' +

            fieldName;


        // Try to get entries from cache

        PicklistEntries entries = uiApiCache.get(requestUrl);

        if (entries == null) {

            // Prepare UI API request

            HttpRequest request = new HttpRequest();

            request.setMethod('GET');

            request.setHeader(

                'Authorization',

                'Bearer ' + UserInfo.getSessionId()

            );

            request.setEndpoint(requestUrl);

            // Call UI API

            try {

                HttpResponse httpResponse = new Http().send(request);

                if (httpResponse.getStatusCode() == 200) {

                    // Parse JSON response into PicklistEntries

                    entries = (PicklistEntries) JSON.deserialize(

                        httpResponse.getBody(),

                        PicklistEntries.class

                    );

                    // Cache entries

                    uiApiCache.put(requestUrl, entries);

                } else {

                    System.debug(

                        'HTTP ' +

                        httpResponse.getStatusCode() +

                        ' while calling UI API, Response ' +

                        httpResponse.getBody()

                    );

                    throw new CalloutException(httpResponse.getBody());

                }

            } catch (System.Exception e) {

                System.debug('ERROR: ' + e);

                throw e;

            }

        }


        return entries;

    }

    

     global class PicklistEntries {

        global PicklistEntry defaultValue;

        global List<PicklistEntry> values;

    }


    global class PicklistEntry {

        global String label;

        global String value;

    }

 }

 

--> Using picklists in Lightning Web Components

 

You can avoid Apex altogether by calling directly the UI API with the 'getPicklistValues' wire adapter

 from the lightning/uiObjectInfoApi module.


The getPicklistValues adapter retrieves all picklist values for a given record type and field name. 

Using the adapter is interesting for performance reasons because it benefits from the Lightning Data Service cache. 

Additionally, this also reduces the amount of Apex code you have to maintain and test (no need for a custom Apex controller). 


ex :

    

    import { LightningElement, wire } from 'lwc';

import { getPicklistValues } from 'lightning/uiObjectInfoApi';

import INDUSTRY_FIELD from '@salesforce/schema/Account.Industry';

 

export default class Example extends LightningElement {

    @wire(getPicklistValues, {

        recordTypeId: '012000000000000AAA', // Default record type Id

        fieldApiName: INDUSTRY_FIELD

    })

    getIndustryPicklistValues({ error, data }) {

        if (data) {

            /*

            Do something with data.defaultValue and data.values[]

            Values are represented as objects like this:

            {

                attributes: null,

                label: "Agriculture",

                validFor: [],

                value: "Agriculture"

            }

            */

        } else if (error) {

            // Handle error

        }

    }

}


Thursday, 13 May 2021

Styling Lightning Web Components

 Composability :


Modern apps are composable and made up of reusable elements where smaller parts are 

combined to create larger parts.


This extends to both functionality as well as styling.


Encapsulation :


1.Shadow DOM

Ability for styles to be self-contained to just the reusable element.


2.CSS Custom Property(aka css variables)

Supported mechanism to set and override styles on a reusable element from a parent element.


Shadow DOM :


Shadow DOM, a part of the Web Components standard, helps to isolate the internals of a component from the 

rest of the document,including styling.


This protects us from breaking CSS changes,global styles, or any side-effects of cascading.


Note : Your UI won't change depending on the context.


CSS Custom Properties :


The primary way you can customize the CSS inside the Shadow DOM is by relying on CSS Custom Properties (aka CSS Variables)

to change and override property values.


The way you use them is similar to how you would use variables in javascript.


ex :

    :root {

      --myAlign: center;

    }

    

    #container {

      width : 100%;

      height:350px;

      background-color:#0099FF;

      display:flex;

      align-items:var(--myAlign);

      justify-content:var(--myAlign);    

    }

    

Note :


1.With Shadow DOM, you get style encapsulation.

2.With CSS Custom Properties, you have the ability to customize the parts of the UI that have been exposed for customization.


Styling Hooks and LWCs :


Styling hooks offer an easy, supported way of making styling customizations to any web component (such as a Base Component)!


They are nothing more than css custom properties.


ex : --sds-styling-hooks:"awesome";


   


:host {

   --sds-c-card-color-background:#222;

   --sds-c-card-text-color:#fff;

   --sds-c-card-color-border:transparent;

   --sds-c-card-spacing-block:1.5rem;

   --sds-c-card-radius-border:0;

   --sds-c-card-footer-spacing-block-start:1rem;

   --sds-c-card-footer-color-border:transparent;

   --sds-c-card-footer-text-align:left;

   

   --sds-c-badge-color-background:#fff;


  --sds-c-button-color-background:black;

  --sds-c-button-text-color:#111;

  --sds-c-button-spacing-inline-start:1.5rem;

  --sds-c-button-spacing-inline-end:1.5rem;

  --sds-c-button-spacing-block-start:.5rem;

  --sds-c-button-spacing-block-end:.5rem;

  --sds-c-button-radius-border:0;

}


Create Styling Hooks for Lightning Web Components :


This feature allows creating custom styling hooks for custom components.

To define a CSS custom property in a component's style sheet, prefix with --.

To insert the value of the property ,use var.


ex :

 :host {

   --important-color:red;

 }

 .important{

   color:var (--important-color)

 }



share CSS Style in LWC :

=========================


1.share CSS styles among LWC component (summer 20 release)


Create a component that contains a CSS file and configuration file.


custom.css

custom.js-meta.xml


Then import it in the component you want to use the CSS file in.


/**myComponent.css **/

import 'c/custom';

/* Define other style rules for mycomponent here */


2.load css from static resource


import { loadStyle } from 'lightning/platformResourceLoader';


renderedCallback() {

                Promise.all([

                    loadStyle(this, resourcePath + '/custom.css');

                ])

}

Monday, 10 May 2021

Large Data Volumes (LDV) in Salesforce

 The large data volumes (LDV) can lead to slow performance, including slower queries,

slower search and list views and slower sandbox refreshing.


Why is LDV important ?


1. Objects with > 1M records

2. Report & Dashboard Impact

3. SOQL Query Impact

4. List View Loading Imapct

5. Ownership Skews

6. Record Locking

7. Sharing Calculations

8. Data Migration considerations


Salesforce Platform Structure :

================================


Metadata Table : Describe custom objects and fields

Data Table     : containing the physical records in your org.

Virtual Database : A virtualization layer which combines the data and 

metadata to display the underlying records to the User.


Platform takes the SOQL entered by the user which is converted to native SQL under the hood to 

perform necessary table joins and fetch the required information.


How does Search Work ?


1.Record takes 15 minutes to be indexed after creation.

2.Salesforce searches the Index to find records.

3.Found rows are narrowed down using security predicates to form Result Set.

4.Once Result-Set reaches a certain size, additional records are discarded.

5.ResultSet is used to query the main database to fetch records.


Force.com Query Optimizer :


System responsible for generating optimal query execution paths in Salesforce,

and performing table joins to fetch the underlying data.


What is a skinny table ?


Condensed table combining a shortlist of standard and custom fields for faster DB record fetches.


1.Salesforce stores standard and custom field data in separate DB Tables.

2.Skinny table combines standard and custom fields together.

3.Data fetch speed is improved with more rows per fetch.

4.Do not contain soft-deleted records.

5.Best for > 1M records.

6.Immediately updated when master tables are updated.

7.Usable on standard and custom objects.

8.Created by salesforce Support.


Note : More rows returned faster using Skinny Table.


what is an index ?


1.Sorted column,or combination of columns which uniquely identify rows of data.

2.Index contains sorted column and pointers to the rows of data.


ex :

1.Created index on Comp_no field.

2.[SELECT * FROM Table WHERE Comp_no>14]

3.Query uses the Sorted Comp_no(Index) column to quickly identify data rows.

4.Query does not need to do full table scan to fetch rows.


Two types of Index 


1.Standard Index

2.Custom Index


Standard Index :



Salesforce creates standard index on the following fields :


1.RecordTypeId

2.Division

3.CraetedDate

4.LastModifiedDate

5.Name

6.Email

7.FOreign key Relationship [Master-Detail and Lookup)

8.Salesforce Record Id

9.External Id and Unique fields


Custom Index :


Created an index for custom fields frequently used in reports or custom queries.


1.Can be created o formula fields.

2.Cannot be created for 

 multi-picklist

 Currency field

 long text field

 binary fields

3.Cannot be created for columns with NULL values.


Two Column Index : 


Useful when one column is to select the records to display,

and the other column is to sort.


Indexing Pointers :

===================

How does Salesforce Query Optimizer use Indexes?


1.Force.com Query Optimizer determines which index to use.

2.Standard Indexed Fields : If query filter matches < 30 % of first million records then use standard index.

3.Custom Index Fields : Used if filter matches < 10% of total records ,upto max 3,33,333 records.

4.Salesforce determines % of records to be returned when evaluating index use.

5.A query is selective if index can be used -'Please write selective queries'.

6.Performance of SOQL query improves when two or more filters in the where clause meet the above conditions.

7.Salesforce checks for frequently run queries and creates an index if it will improve the query performance.

8.Force.com query optimizer makes decisions to use table scan,indexing etc, and what order to perform joins in to best optimize the execution of the query.


How should we improve performance under Large Volumes?


1.Aim to use indexed fields in the WHERE clause of SOQL queries.

2.Avoid using NULLS in queries as index cannot be used.

3.Only use fields present in skinny table.

4.Use query filters which can highlight < 10% of the data.

5.Avoid using wildcards in queries, such as % as this prevents use of an index.

6.Break complex query into simple singular queries to use indexes.

7.Select ONLY required fields in SELECT statement.


Ownership Skew :


When you have more than 10,000 records for a single object owned by a single owner.


why does this cause problems?


1.Share Table Calculations : when you move a user in the Role Hierarchy ,sharing calculations need to take 

place on large volumes of data to grant and revoke access.

2.Moving users around the hierarchy,causes the sharing rules to be re-calculated for both the user in the hierarchy,

and any users above this user in the role hierarchy.


How can we avoid this ?


Data Migration : Work with client to divide records up across multiple real end-users.

Integration User : Avoid making integration user the ecord owner.

Leverage Lead and Case assignment rules.

If unavoidable : assign records to a user is an isolated role at the top of the Role Hierarchy.


Parenting Skew :

===============

When you have more than 10,000 records for a single object underneath the same parent record.


why does this cause problems ?


Data Migration : Bulk API batch size is 10,000.

Records in parallel batches linked to the same parent will force the parent to be updated potentially causing record locking.


Implicit Shaing : where access to a parent record is driven by access to children.

If you lose access to child record, Salesforce must check every other child record to determine continued access to parent.


How can we avoid this ?


1.Avoid having > 10,000 records of a single object linked to the same parent record.

2.When you have free-hanging contacts that need to be associated to accounts ,distribute these across multiple accounts.

3.Using a Picklist field : when dealing with a small number of Lookup records , use a Picklist field instead.


Sharing Considerations :

========================

1.Set OWDs to Public R/W and public R/O where possible for non-confidential data.

2.Reduce the requirement for a share table.

3.Use 'Controlled by Parent' to avoid additional share tables.


Sharing Calculation :


1.Parallel Sharing Rule Re-calculation

2.Deferred Sharing Rule Calculation

3.Granular Locking 


Parallel Sharing Rule Re-calculation :


1.Sharing rules are processed synchronously when Role Hierarchy updates are made.

2.Sharing rules can be processed asynchronously and split into multple execution threads, so a single sharing rule cal

can be executed on parallel threads.

3.For long-running role calculations ask SFDC to enable parallel sharing rule re-calculation.


Deferred Sharing Rule Calculation :


1.Make Role Hierarchy updates, Sharing Rule changes and Group Membership changes up front.

2.Share Table calculations across objects are deferred.

3.Re-enable sharing calculations once amendments are complete.

4.Contact SFDC to enable this functionality.

5.Test the process in a sandbox environment first and verify the results and timings.

6.Negotiate a window with the customer and perform deferred sharing rule recalculation. 


3.Granular Locking :


1.When performing updates to roles and groups, the entire group membership table is locked for data integrity.

2.Multiple updates to the roles/groups may cause locking when done concurrently.

3.System employs additional logic to allow multiple updates to proceed simultaneously if there is no hierarchical

or other relationship between the roles or groups involved.

4.Groups that are in separate hierarchies can be edited concurrently.


Data Load Strategy :

===================


Step 1 : Cinfigure Your Organization for Data Load :


1.Enable parallel sharing rule recalculation and differed sharing rule recalculation.

2.Create Role Hierarchy and Load users.

3.setup OWD of object as public read/write - the one we want to load by setting the no sharing table

needs to be aintained for the object,preventing sharing recalculation from needing to run during data loads.

4.Disable workflows,Triggers,Process builder,validation rules.


step 2 : Prepare the Data Load :


1.dentify the data that you want to load in new org.

2.Extract,Cleanse,Enrich,Transform the data and create record in the staging table.

3.De-duplicate the data.

4.Make sure data is clean, especially foreign key relationship.

5.Perform some initial batch testing in the sandbox.


step 3 : Execute the Data Load :


1.Load parent object before children.Extract parent keys for later loading.

2.User insert and update ahead of upsert -in upsert operation Salesforce internally validates the data.

based on Object's Id or External Id.So upsert takes little bit longer time than insert or upsert.

3.For updates : only send fields whose values have changed.

4.group record by parents id when using Bulk API to prevents lock failure in parallel batches.

5.Use the BULK API when dealing with > 50,000 records.


step 4 : Configure your organization for production 


1.Defer sharing calculations whilst loads are in progress.

2.Change OWD for object from Public Read/Write back to public RO or private after load is complete-

create public Group/Queues.Create your sharing rules.Try this steps in sandbox first.Can ask SFDC to 

enable parallel sharing rules processing.

3.Configure sharing rules-do these one at a time allowing one to complete before moving on.Or use deferred sharing ,

finish off sharing rule creation and let the sharing rule calculation happen on mass afterwards.

4.Enable trigger,workflow and validation rules again.

5.Create roll-up summary fields.

 

Archiving Data :


How and why should we archive data?


1.To keep only current data accessible in salesforce.

2.To improve performance of reports,dashboards and list views.

3.To improve SOQL query performance.

4.Compliance and regulatory purposes.

5.To maintain a data backup.

6.To ensure redundant data does not impact system reporting, analytics or general performance.


Saturday, 8 May 2021

How to save a record that’s been identified as a duplicate in apex

 Here we are going to learn how to save a record that's identified as duplicate with the help of DMLOptions.


Especially it helps to bypass duplicate rules in test class.


Eg:


Save an account record that’s been identified as a duplicate\


Database.DMLOptions dml = new Database.DMLOptions(); 


dml.DuplicateRuleHeader.allowSave = true;


dml.DuplicateRuleHeader.runAsCurrentUser = true;


Account duplicateAccount = new Account(Name='dupe');


Database.SaveResult sr = Database.insert(duplicateAccount, dml);


if (sr.isSuccess()) {


System.debug('Duplicate account has been inserted in Salesforce!');



}

Inherited Sharing keyword in Apex Class

 The 'inherited sharing' keyword on an Apex class, which allows the class to run in the sharing mode of the class that called it.



1.Using Inherited sharing enables you to pass AppExchange Security Review 

and ensure that your privileged Apex code is not used in unexpected or insecure ways.


2. Inherited sharing ensures that the deafult is to run as with sharing.


3.Inherited sharing runs as with sharing when used as a Lightning component controller,

a visualforce controller, an Apex REST service or any other entry point to an Apex transaction.


4. A class declared as inherited sharing runs as without sharing only when explicitly called from an already

established without sharing context.


ex :  


   public without sharing class noSharing {

   

      public void callwithoutsharingclassmethod(){

      

       inheritedSharingClass iobj=new inheritedSharingClass();

       

          List<Contact> conlst=iobj.getAlltheContacts();

      }

   

   }

   

    public inherited sharing class inheritedSharingClass {

     

     public List<Contact> getAlltheContacts(){

       return [SELECT Id,Name FROM Contact];

     }

    

    }

Context Summary :



Friday, 7 May 2021

Salesforce Summer 21 release Notes

 1.Visibility Rules Apply to CREATE,EDIT & CLONE


Hidden Accordion and Tabs Components Now Stay Hidden During Create,edit and clone.


Visibility rules on Accordion and Tabs components in Dynamic Forms-enabled pages are 

no longer ignored during create, edit, and clone. 


For example, let’s say you have a visibility rule on an Accordion component that 

causes it to be hidden while a user views a record page. 

When the user clicks to create, edit, or clone, all fields and field sections in that 

hidden Accordion component now remain hidden in the create, edit, and clone windows.


2.Create a Dynamic Actions Bar for Your App Page (Pilot)


The new Standard Lightning Page Component allows you to organize all of your 

standard and custom global actions in one convenient bar.


3.Set Expirations for Permission (Beta):


Enable Permission Set Group Assignments with Expiration Dates(Beta).The option appears under 'User Management Settings' in Setup.

Then you can Set Expirations for Assignments on Permissions in Permission Sets and Permission Set Groups.


Thursday, 6 May 2021

Salesforce Restriction Rules Summer21 release (Beta)

 Resriction rules let you enhance your security by allowing certain users to access only specified records.

They prevent users from accessing records that can contain sensitive data or information that isn't essential to their work.

Restriction rules are avaialble for custom objects,contracts,tasks and events and can be configured through the Tooling or 

Metadata API.





ex :


<?xml version="1.0" encoding="UTF-8"?>
<RestrictionRule xmlns="http://soap.sforce.com/2006/04/metadata">
    <active>true</active>
    <description>Allows users with a specific profile to see only tasks that they own.</description>
    <enforcementType>Restrict</enforcementType>
    <masterLabel>Tasks You Own</masterLabel>
    <recordFilter>OwnerId = $User.Id</recordFilter>
    <targetEntity>Task</targetEntity>
    <userCriteria>$User.ProfileId = '005xxxxxxxxxxxx'</userCriteria>
    <version>1</version>
</RestrictionRule>


Restriction rules are applied to the following Salesforce features:
List Views
Lookups
Related Lists
Reports
Search
SOQL
SOSL