Wednesday, 23 December 2020

JSON request body for creating an Account and Contact with Composite Graph API

 Hi,

Here we are going to learn 

How to do two composite graph requests each create an Account and then create related records:

Endpoint Url:

  1. <instance.salesforce.com(baseurl)>/services/data/vXX.X/composite/graph

Method Type: POST

Request Body:

{
    "graphs" : [
        {
            "graphId" : "1",
            "compositeRequest" : [
                {
                    "url" : "/services/data/v50.0/sobjects/Account/",
                    "body" : {
                        "name" : "Cloudy Consulting"
                    },
                    "method" : "POST",
                    "referenceId" : "reference_id_account_1"
                },
                {
                    "url" : "/services/data/v50.0/sobjects/Contact/",
                    "body" : {
                        "FirstName" : "Nellie",
                        "LastName" : "Cashman",
                        "AccountId" : "@{reference_id_account_1.id}"
                    },
                    "method" : "POST",
                    "referenceId" : "reference_id_contact_1"
                },
                {
                    "url" : "/services/data/v50.0/sobjects/Opportunity/",
                    "body" : {
                        "CloseDate" : "2024-05-22",
                        "StageName" : "Prospecting",
                        "Name" : "Opportunity 1",
                        "AccountId" : "@{reference_id_account_1.id}"
                    },
                    "method" : "POST",
                    "referenceId" : "reference_id_opportunity_1"
                }
            ]
        },
        {
            "graphId" : "2",
            "compositeRequest" : [
                {
                    "url" : "/services/data/v50.0/sobjects/Account/",
                    "body" : {
                        "name" : "Easy Spaces"
                    },
                    "method" : "POST",
                    "referenceId" : "reference_id_account_2"
                },
                {
                    "url" : "/services/data/v50.0/sobjects/Contact/",
                    "body" : {
                        "FirstName" : "Charlie",
                        "LastName" : "Dawson",
                        "AccountId" : "@{reference_id_account_2.id}"
                    },
                    "method" : "POST",
                    "referenceId" : "reference_id_contact_2"
                }
            ]
        }
    ]
}


Response:

{ "graphs" : [ { "graphId" : "1", "graphResponse" : { "compositeResponse" : [ { "body" : { "id" : "001R00000064wc7IAA", "success" : true, "errors" : [ ] }, "httpHeaders" : { "Location" : "/services/data/v50.0/sobjects/Account/001R00000064wc7IAA" }, "httpStatusCode" : 201, "referenceId" : "reference_id_account_1" }, { "body" : { "id" : "003R000000DDMlTIAX", "success" : true, "errors" : [ ] }, "httpHeaders" : { "Location" : "/services/data/v50.0/sobjects/Contact/003R000000DDMlTIAX" }, "httpStatusCode" : 201, "referenceId" : "reference_id_contact_1" }, { "body" : { "id" : "006R0000003FPYxIAO", "success" : true, "errors" : [ ] }, "httpHeaders" : { "Location" : "/services/data/v50.0/sobjects/Opportunity/006R0000003FPYxIAO" }, "httpStatusCode" : 201, "referenceId" : "reference_id_opportunity_1" } ] }, "isSuccessful" : true }, { "graphId" : "2", "graphResponse" : { "compositeResponse" : [ { "body" : { "id" : "001R00000064wc8IAA", "success" : true, "errors" : [ ] }, "httpHeaders" : { "Location" : "/services/data/v50.0/sobjects/Account/001R00000064wc8IAA" }, "httpStatusCode" : 201, "referenceId" : "reference_id_account_2" }, { "body" : { "id" : "003R000000DDMlUIAX", "success" : true, "errors" : [ ] }, "httpHeaders" : { "Location" : "/services/data/v50.0/sobjects/Contact/003R000000DDMlUIAX" }, "httpStatusCode" : 201, "referenceId" : "reference_id_contact_2" } ] }, "isSuccessful" : true } ] }

Reference:

Composite Graph API

 Hi,

Here we are going to learn what is Composite Graph API and how it works.

What is Composite Graph API:

  • It gives us the ability to execute multiple API requests in a single API call.
  • We can perform CRUD operations on a large number of related sObjects in a single API call, eliminating the need to orchestrate those relationships in custom code or perform multiple round trips to the server.
  • This increases the subrequest limit from 25 to 500, and also provides a number of optimizations that ensure records are processed efficiently and operations are rolled back if any steps are not completed.
  • Grouping collections of sObjects into graphs lets us do more with a single API call.

  • We can construct multiple graphs of related sObjects to insert multiple records within 
  • the same transaction. 
  • We can also access current records and then add records to them using reference ids.

How to Organize Subrequests Within a Graph:

  • The body of a Composite Graph API request consists of a number of graphs, each of which may contain multiple composite subrequests. 
  • Think of each graph as its own grouping of related sObject records.
Sample:

{
  "graphs": [
    {
      "graphId": "graphId",
      "compositeRequest": [
        compositeSubrequest1, 
        compositeSubrequest2, 
        ...]
    },
    {
      "graphId": "graphId2",
      "compositeRequest": [
        compositeSubrequest3, 
        compositeSubrequest4, 
        ...]
    }
  ]
}

Example:
{
  "graphs": [
    {
      "graphId": "graph1",
      "compositeRequest": [
        {
          "method": "PATCH",
          "url": "/services/data/v50.0/sobjects/Account/ExternalAcctId__c/ID12345",
          "referenceId": "newAccount",
          "body": {
            "Name": "Trailblazers",
            "Website": "TrailblazerOutfiters.com"
          }
        },
        {
          "method": "POST",
          "url": "/services/data/v50.0/sobjects/Order__c",
          "referenceId": "newOrder1",
          "body": {
            "Account__c": "@{newAccount.id}"
          }
        },
        {
          "method": "POST",
          "url": "/services/data/v50.0/sobjects/OrderItem__c",
          "referenceId": "newProduct1",
          "body": {
            "Order__c": "@{newOrder1.id}",
            "Product__c": {
              "External_Id__c": "EB1213"
            },
            "Qty_L__c": "1",
            "Price__c": "500"
          }
        }
      ]
    }
  ]
}

Here
  • Each subrequest contains a referenceId that can be used to relate records that follow the subrequest.
  • In the example above, the Account record is upserted and the referenceId is set to newAccount.
  • In the subrequest that follows, an order record is inserted and the Account__c field value is set to @{newAccount.Id}
  • which references the account that was just inserted.
  • Then that order record's referenceId (newOrder1) is used for the Order__c field value (@{newOrder1.id}).
curl Request:

curl --request POST \
--header "Authorization: Bearer token" \
--header "Content-Type: application/json" \
--data @data.json \
instance.salesforce.com/services/data/vXX.X/composite/graph

Sample HTTP Request:

String baseURL = URL.getSalesforceBaseUrl().toExternalForm(); 
String compositeGraphURL = baseURL + '/services/data/v50.0/composite/graph/';  
  
HttpRequest request = new HttpRequest();  
request.setMethod('POST');   
request.setHeader('Authorization', 'OAuth ' + UserInfo.getSessionId());        
request.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionID()); 
request.setHeader('Content-Type', 'application/json');
 //Here we are getting data in JSON format from static resource

StaticResource resource = [
  SELECT Id, Body 
  FROM StaticResource 
  WHERE Name = 'graphs' 
  LIMIT 1
];
String body = resource.Body.toString();
  
request.setBody(body);  
request.setEndpoint(compositeGraphURL);  
String prettyResponse = '';
try {  
  Http http = new Http();   
  HttpResponse response = http.send(request);  
  if (response.getStatusCode() == 200 ) {  
    prettyResponse = JSON.serializePretty( JSON.deserializeUntyped(response.getBody()) );  
  } else {  
    System.debug(' response ' + response.getBody() );  
    throw new CalloutException( response.getBody() );  
  }   
} catch( System.Exception e) {  
  System.debug('ERROR: '+ e);  
  throw e;  
}  
System.debug('Response: ' + prettyResponse );




Monday, 21 December 2020

Removing inaccessible relationship fields from the query result with Security.stripInaccessible

 Hi,

Let's see the following code how it handles when user doesn’t have permission to insert the Account__c field, which is a lookup from MyCustomObject__c to Account.

// Account__c is a lookup from MyCustomObject__c to Account

@isTest

   public class TestCustomObjectLookupStripped {

      @isTest static void caseCustomObjectStripped() {

         Account a = new Account(Name='foo');

         insert a;

         List<MyCustomObject__c> records = new List<MyCustomObject__c>{

            new MyCustomObject__c(Name='Custom0', Account__c=a.id)

         };

         insert records;

         records = [SELECT Id, Account__c FROM MyCustomObject__c];

         SObjectAccessDecision securityDecision = Security.stripInaccessible

                                                  (AccessType.READABLE, records);

         

         // Verify stripped records

         System.assertEquals(1, securityDecision.getRecords().size());

         for (SObject strippedRecord : securityDecision.getRecords()) {

             System.debug('Id should be set as Id fields are ignored: ' + 

                           strippedRecord.isSet('Id')); // prints true

             System.debug('Lookup field FLS is not READABLE to running user, 

                           should not be set: ' +

                           strippedRecord.isSet('Account__c')); // prints false

         }

      }

   }


Reference:

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_with_security_stripInaccessible.htm

How to sanitize sObjects that have been deserialized from an untrusted source with Security.stripInaccessible

 Hi ,

Let's assume we recieved  a json string with "Name" and "Annual Revenue" to update on Account.

But user doesn't have permisson to update Annual Revenue on Account object. 

Then we can avoid updating Annual Revenue on Account object as shown below.


String jsonInput =

'[' +

'{' +

'"Name": "InGen",' +

'"AnnualRevenue": "100"' +

'},' +

'{' +

'"Name": "Octan"' +

'}' +

']';


List<Account> accounts = (List<Account>)JSON.deserializeStrict(jsonInput, List<Account>.class);

SObjectAccessDecision securityDecision = Security.stripInaccessible(

                                         AccessType.UPDATABLE, accounts);

// Secure update

update securityDecision.getRecords(); // Doesn’t update AnnualRevenue field

System.debug(String.join(securityDecision.getRemovedFields().get('Account'), ', ')); // Prints "AnnualRevenue"

System.debug(String.join(securityDecision.getModifiedIndexes(), ', ')); // Prints "0”



Reference:

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_with_security_stripInaccessible.htm

How to remove inaccessible fields from sObjects before DML operations with Security.stripInaccessible

 Hi,

Let's take a scenario that 

 The user who doesn’t have permission to create Rating for an Account can still create an Account.

Her the method "Security.stripInaccessible" ensures that no Rating is set and doesn’t throw an exception.


Eg:

List<Account> newAccounts = new List<Account>();

Account a = new Account(Name='Acme Corporation');

Account b = new Account(Name='Blaze Comics', Rating=’Warm’);

newAccounts.add(a);

newAccounts.add(b);

SObjectAccessDecision securityDecision = Security.stripInaccessible(

                                         AccessType.CREATABLE, newAccounts);

// No exceptions are thrown and no rating is set

insert securityDecision.getRecords();

System.debug(securityDecision.getRemovedFields().get('Account')); // Prints "Rating"

System.debug(securityDecision.getModifiedIndexes()); // Prints "1"

Reference:
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_with_security_stripInaccessible.htm


How to remove inaccessible fields from the subquery result by using Security.stripInaccessible

 Hi,

Here we are going to learn how to remove inaccessible fields from the subquery:

Scenario:

Remove the "Phone" field on Contact Object that the user doesn’t have read permission.

Code Snippet:

List<Account> accountsWithContacts =

[SELECT Id, Name, Phone,

    (SELECT Id, LastName, Phone FROM Account.Contacts)

FROM Account];  

   // Strip fields that are not readable

   SObjectAccessDecision decision = Security.stripInaccessible(

                                   AccessType.READABLE,

                                   accountsWithContacts);

 // Print stripped records

   for (Integer i = 0; i < accountsWithContacts.size(); i++) 

  {

      System.debug('Insecure record access: '+accountsWithContacts[i]);

      System.debug('Secure record access: '+decision.getRecords()[i]);

   }

// Print modified indexes

   System.debug('Records modified by stripInaccessible: '+decision.getModifiedIndexes());

 // Print removed fields

   System.debug('Fields removed by stripInaccessible: '+decision.getRemovedFields());


Reference:
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_with_security_stripInaccessible.htm

How to remove inaccessible fields from the query result with Security.stripInaccessible

 Hi,

Here we are going to learn how to remove inaccessible fields from  the query:

Let's take a scenario 

A display table for campaign data must always show the BudgetedCost. The ActualCost must be shown only to users who have permission to read that field.

Code Snippet:

SObjectAccessDecision securityDecision = 

         Security.stripInaccessible(AccessType.READABLE,

                 [SELECT Name, BudgetedCost, ActualCost FROM Campaign];

                 );


    // Construct the output table

    if (securityDecision.getRemovedFields().get('Campaign').contains('ActualCost')) {

        for (Campaign c : securityDecision.getRecords()) {

        //System.debug Output: Name, BudgetedCost

        }

    } else {

        for (Campaign c : securityDecision.getRecords()) {

        //System.debug Output: Name, BudgetedCost, ActualCost

        }

}

Reference:

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_with_security_stripInaccessible.htm


Enforce Field- and Object-Level Security with Security.StripInaccessible

 Hi ,

Here we are going to learn about Security.StripInaccessible method.

  • This method mainly used for field- and object-level data protection
  • This allows developers to remove all fields from the records that the running user does not have access to.
  • This makes it easier to allow graceful degradation of application behavior on security violation by omitting fields rather than failing.

How does it work?

  • The field- and object-level data protection is accessed through the Security and SObjectAccessDecision classes.
  • We can use the StripInaccessible method to strip fields that the current user can’t access from query and subquery results.
  • It helps to remove inaccessible fields from sObjects before a DML operation to avoid exceptions.
  • This method helps sanitize sObjects that have been deserialized from an untrusted source.
  • The stripInaccesible method checks the source records for fields that don’t meet the field- and object-level security check for the current user and creates a return list of sObjects. 
  • The return list is identical to the source records, except that fields inaccessible to the current user are removed.
  • The sObjects returned by the getRecords method contain records in the same order as the sObjects in the sourceRecords parameter of the stripInaccessible method. 
  • Fields that aren’t queried are null in the return list, without causing an exception.
Note: Previously we were using dynamic apex for identifying access on particular object or field.

How to identify inaccessible fields that were removed:

We can use 'isSet' method.
For example, the return list contains the Contact object and the custom field social_security_number__c is inaccessible to the user. Because this custom field fails the field-level access check, the field is not set and "isSet" returns false.

SObjectAccessDecision securityDecision = Security.stripInaccessible(sourceRecords);
Contact c = securityDecision.getRecords()[0];
System.debug(c.isSet('social_security_number__c')); // prints "false"

Eg:(1)
This example removes fields from the query result that the current user does not have update access to.

SObjectAccessDecision securityDecision = 
       Security.stripInaccessible(AccessType.UPDATABLE,
             [SELECT Name, BudgetedCost, ActualCost FROM Campaign]);

Eg:(2)

This example performs a query and then removes inaccessible fields from the query result.

List<Contact> records = [SELECT Id, Name, Phone, HomePhone FROM Contact];
SObjectAccessDecision securityDecision = Security.stripInaccessible(AccessType.READABLE, records);

Example class before applying "stripInaccessible".


global with sharing class ApexSecurityRest {
    global static Contact doGet() {
        Id recordId = RestContext.request.params.get('id');
        Contact result;
        if (recordId == null) {
           throw new FunctionalException('Id parameter is required');
        }
        if (Schema.SObjectType.Contact.isAccessible()
          && Schema.SObjectType.Contact.fields.Name.isAccessible()
          && Schema.SObjectType.Contact.fields.Top_Secret__c.isAccessible()
        ) {
          List<Contact> results = [SELECT id, Name, Title, Top_Secret__c, Account.Name FROM Contact WHERE Id = :recordId];
          if (!results.isEmpty()) {
             result = results[0];
             if (Schema.sObjectType.Contact.fields.Description.isUpdateable()){
                 result.Description = result.Account?.Name;
                 }
             }
           } else {
             throw new SecurityException('You don\'t have access to all contact fields required to use this API');
           }
           return result;
      }
      public class FunctionalException extends Exception{}
      public class SecurityException extends Exception{}
}

After applying stripInaccessible


global with sharing class ApexSecurityRest {  
    global static Contact doGet() {
        Id recordId = RestContext.request.params.get('id');
        Contact result;
        if (recordId == null) {
           throw new FunctionalException('Id parameter is required');
        }
       
          List<Contact> results = [SELECT id, Name, Title, Top_Secret__c, Account.Name FROM Contact WHERE Id = :recordId];
          SObjectAccessDecision securityDecision = Security.stripInaccessible(AccessType.READABLE, results);
          if (!results.isEmpty()) {
             result = results[0];
             if (Schema.sObjectType.Contact.fields.Description.isUpdateable()){
                 result.Description = result.Account?.Name;
                }
            }
           
           return result;
      }
      public class FunctionalException extends Exception{}
      public class SecurityException extends Exception{}
}




Considerations:


References:
------------------



Sunday, 20 December 2020

Safe Navigation Operator

Hi,

Here we learn how can we use Safe Navigation Operator:

We Use the safe navigation operator (?.) to replace explicit, sequential checks for null references.

This operator short-circuits expressions that attempt to operate on a null value and returns null instead of throwing a NullPointerException.

If the left-hand-side of the chain expression evaluates to null, the right-hand-side is not evaluated. Use the safe navigation operator (?.) in method, variable, and property chaining. The part of the expression that is not evaluated can include variable references, method references, or array expressions.

This example first evaluates a, and returns null if a is null. Otherwise, the return value is a.b.

a?.b // Evaluates to: a == null ? null : a.b

This example indicates that the type of the expression is the same whether the safe navigation operator is used in the expression or not.

Integer x = anObject?.anIntegerField; // The expression is of type Integer because the field is of type Integer

This example shows a single statement replacing a block of code that checks for nulls.

// Previous code checking for nulls

String profileUrl = null;

if (user.getProfileUrl() != null) {

   profileUrl = user.getProfileUrl().toExternalForm();

}

// New code using the safe navigation operator
String profileUrl = user.getProfileUrl()?.toExternalForm();

This example shows a single-row SOQL query using the safe navigation operator.

// Previous code checking for nulls

results = [SELECT Name FROM Account WHERE Id = :accId];

if (results.size() == 0) { // Account was deleted

    return null;

}

return results[0].Name;


// New code using the safe navigation operator

return [SELECT Name FROM Account WHERE Id = :accId]?.Name;



References:

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/langCon_apex_SafeNavigationOperator.htm

https://trailhead.salesforce.com/en/content/learn/modules/platform-developer-i-certification-maintenance-winter-21/get-handson-with-field-and-objectlevel-security-and-safe-navigation-operator


Friday, 18 December 2020

How to retrieve Lightning experience Theme and branding

 Hi,

Here we are going to learn how can we include Lightning Experience theme and Branding set in package.xml for retrieving and deploying the same.

<types>
        <members>"themeName"</members>
        <name>LightningExperienceTheme</name>
    </types>
    <types>
        <members>"BrandingSetName"</members>
        <name>BrandingSet</name>
    </types>

Reference:

https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_brandingset.htm



Wednesday, 16 December 2020

Convert Source to Metadata Format and Deploy

 Hi ,

Here we are going learn how we can Convert Source format to Metadata Format and Deploy with the help of SFDX commands.

We can do this via command prompt or vs code terminal after opening the project.

Let's look into following simple steps:

  1. Create a folder to put the converted files called "mdapioutput" (any name can be given for Folder or directory name)
    • mkdir mdapioutput
  2. Run the convert command
    • sfdx force:source:convert -d mdapioutput/
  3. Deploy it to your testing environment or production 
    • sfdx force:mdapi:deploy -d mdapioutput/ 
  4. Assign permission set if there is any
    • sfdx force:user:permset:assign --permsetname <permset_name> --targetusername <username/alias>



Reference:

Sunday, 13 December 2020

Check User Permissions for Lightning Web Components

 Hi,

When we develop Lightning web components we can customize a component’s behavior based on whether the current user has specific permission. 

To check whether a user has a permission, import Salesforce permissions from the @salesforce/userPermission and @salesforce/customPermission scoped modules and evaluate whether it’s true or undefined. Then if the user has the permission, the component can take a specific action.

Custom permissions can include a namespace. Orgs use namespaces as unique identifiers for their own customization and packages. If the custom permission was installed from a managed package, prepend the namespace followed by __ to the permission name.

Standard Permission Example:

import hasPermission from '@salesforce/userPermission/StandardPermissionName';

Custom Permission Examples: 

import hasPermission from '@salesforce/customPermission/CustomPermissionName';

import hasPermission from '@salesforce/customPermission/namespace__CustomPermissionName';

The name of the static reference is your choice. These examples chose the format hasPermission to indicate that the reference contains a Boolean.

Reference:

https://trailhead.salesforce.com/content/learn/modules/platform-developer-i-certification-maintenance-winter-21/learn-whats-new-in-lightning-web-components-and-visualforce

Tuesday, 17 November 2020

How to prepare test data for Content Document Link (File)?

 Hi,

Here we are going to learn how can we prepare test data for Content Document.

Let's take a scenario that when a File is inserted under an Account record then "Count of files" should be calculated. (Solution: Apex Trigger on ContentDocumentLink)

Then the following test class will be useful for testing the above scenario.

To insert Content Document Link object record we have to insert ContentVersion first then retrieve the 

ContentDocument and then insert ContentDocumentLink object record.

@isTest

private class ContentDocumentLinkActionsTest {     

    testmethod static void insertContentDocumentTest(){       

        Account actObj = new Account();

actObj.Name = 'Salesforce Techbook';

insert actObj;

        //start insert a filer under Account 

ContentVersion content=new ContentVersion();

content.Title='Header_Picture1';

content.PathOnClient='/' + content.Title + '.jpg';

Blob bodyBlob=Blob.valueOf('Unit Test ContentVersion Body');

content.VersionData=bodyBlob;

content.origin = 'H';

insert content;

List<ContentDocument> documentsObj = [SELECT Id, Title, LatestPublishedVersionId FROM ContentDocument

  where LatestPublishedVersionId=:content.Id];

ContentDocumentLink contentlink=new ContentDocumentLink();

contentlink.LinkedEntityId=actObj.id;

contentlink.contentdocumentid=[select contentdocumentid from contentversion where id =: content.id].contentdocumentid;

contentlink.ShareType = 'V';

Test.startTest();

insert contentlink;       

        Test.stopTest();

    }

}



What is Granular locking?

 Hi,

Here let's see what is  Granular Locking Feature and its advantages.

Granular locking can help customers who experience locking issues("org" locks or lock errors). Without this feature, our code locks the entire table that keeps track of people’s membership in various groups. 

This means that any two administrative operations that change roles, public groups, or territories will conflict if they happen simultaneously. With granular locking, the code first analyses each operation and locks only the portions of the table touched by each. 

This makes it much less likely that any two group membership operations will conflict, and so customers who are using this feature will experience many fewer locks. In most cases, customers will find that with granular locking they can perform manual administration while automated update processes are running, and introduce some degree of multi-threading into integration code and other automated processes.

By default, granular locking is enabled, which allows some group maintenance operations to proceed simultaneously if there is no hierarchical or other relationship between the roles or groups involved in the updates. Administrators can adjust their maintenance processes and integration code to take advantage of this limited concurrency to process large-scale updates faster, all while still avoiding locking errors.

How can I tell if I should use granular locking?

     Customers who only occasionally receive an error message indicating that they have encountered a group membership lock are probably not good candidates for this feature. Customers should consider using this feature only if they experience frequent and persistent locking that severely restricts their ability to manage both manual and automated updates at the same time, or severely degrades the throughput of integrations or other automated group maintenance operations.

Advantages:

  • Groups that are in separate hierarchies are now able to be manipulated concurrently
  • Public groups and roles that do not include territories are no longer blocked by territory operations.
  • Users can be added concurrently to territories and public groups.
  • User provisioning can now occur in parallel.
    • Portal user creation requires locks only if new portal roles are being created. 
    • Provisioning new portal users in existing accounts occurs concurrently.
  •  A single-long running process, such as a role delete, blocks only a small subset of operations.

Reference:

https://help.salesforce.com/articleView?id=000325942&type=1&mode=1

https://developer.salesforce.com/docs/atlas.en-us.draes.meta/draes/draes_group_membership_locking.htm

https://developer.salesforce.com/docs/atlas.en-us.210.0.draes.meta/draes/draes_tools_granular_locking.htm

Monday, 26 October 2020

Transaction Control (SavePoint & Rollback)

 Hi,

We are going to learn what is SavePoint &  Rollback along with limitations.

savePoint:

A point in the request that specifies the state of the database at that time. Any DML statement that occurs after the savepoint can be discarded, and the database can be restored to the same condition it was in at the time you generated the savepoint.

The savePoint statement helps to identify a point in a transaction to which you can later roll back.

rollback:

It helps to roll back the transaction based on savePoint.

Eg:

Account a = new Account(Name = 'xxx'); 

insert a;

System.assertEquals(null, [SELECT AccountNumber FROM Account WHERE Id = :a.Id].

                           AccountNumber);

// Create a savepoint while AccountNumber is null

Savepoint sp = Database.setSavepoint();

//Change the account number

a.AccountNumber = '123';

update a;

System.assertEquals('123', [SELECT AccountNumber FROM Account WHERE Id = :a.Id].

                             AccountNumber);

// Rollback to the previous null value

Database.rollback(sp);

System.assertEquals(null, [SELECT AccountNumber FROM Account WHERE Id = :a.Id].

                            AccountNumber);


Limitations apply to generating savepoint variables and rolling back the database:

  • If you set more than one savepoint, then roll back to a savepoint that is not the last savepoint you generated, the later savepoint variables become invalid. For example, if you generated savepoint SP1 first, savepoint SP2 after that, and then you rolled back to SP1, the variable SP2 would no longer be valid. You will receive a runtime error if you try to use it.
  • References to savepoints cannot cross trigger invocations because each trigger invocation is a new trigger context. If you declare a savepoint as a static variable then try to use it across trigger contexts, you will receive a run-time error.
  • Each savepoint you set counts against the governor limit for DML statements.
  • Static variables are not reverted during a rollback. If you try to run the trigger again, the static variables retain the values from the first run.

  • Each rollback counts against the governor limit for DML statements. You will receive a runtime error if you try to rollback the database additional times.
  • The ID on an sObject inserted after setting a savepoint is not cleared after a rollback. Create an sObject to insert after a rollback. Attempting to insert the sObject using the variable created before the rollback fails because the sObject variable has an ID. Updating or upserting the sObject using the same variable also fails because the sObject is not in the database and, thus, cannot be updated.


Reference:

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/langCon_apex_transaction_control.htm

Sunday, 4 October 2020

How to Tune Data Relationships and Updates for Performance

 Hi,

Let's have a look into a few points below to Tune Data Relationships and updates for Performance.

We always need to understand the performance characteristics of the various maintenance operations that we are performing and always test substantial data uploads and changes to object relationships in a sandbox environment so we know what to expect.

Here are some specific suggestions.
  • Use a Public Read Only or Read/Write organization-wide default sharing model for all non-confidential data.
  • To avoid creating implicit shares, configure child objects to be Controlled by Parent wherever this configuration meets security requirements.
  • Configure parent-child relationships with no more than 10,000 children to one parent record.
  • If you are encountering only occasional locking errors, see if the addition of retry logic is sufficient to solve the problem.
  • Sequence operations on parent and child objects by ParentID and ensure that different threads are operating on unique sets of records.
  • Tune your updates for maximum throughput by working with batch sizes, timeout values, the Bulk API, and other performance-optimizing techniques.

Reference:
https://developer.salesforce.com/docs/atlas.en-us.draes.meta/draes/draes_object_relationships_takeaway.htm?search_text=Skew

Saturday, 3 October 2020

Implicit Sharing

 Hi,

Here we are going to learn about Implicit Sharing.

The sharing capabilities of the Lightning Platform include a wide variety of features that administrators can use to explicitly grant access to data for individuals and groups.

In addition to these more familiar functions, there are a number of sharing behaviors that are built into Salesforce applications. This kind of sharing is called implicit because it’s not configured by administrators; it’s defined and maintained by the system to support collaboration among members of sales teams, customer service representatives, and clients or customers.

Let's have a look into the following table which describes the different kinds of implicit sharing built into Salesforce applications and the record access that each kind provides.

Type of SharingProvidesDetails
ParentRead-only access to the parent account for a user with access to a child record
  • Not used when sharing on the child is controlled by its parent
  • Expensive to maintain with many account children
  • When a user loses access to a child, Salesforce needs to check all other children to see if it can delete the implicit parent.
ChildAccess to child records for the owner of the parent account
  • Not used when sharing on the child is controlled by its parent
  • Controlled by child access settings for the account owner’s role
  • Supports account sharing rules that grant child record access
  • Supports account team access based on team settings
  • When a user loses access to the parent, Salesforce needs to remove all the implicit children for that user.
PortalAccess to portal account and all associated contacts for all portal users under that accountShared to the lowest role under the portal account
High Volume1Access to data owned by high volume users associated with a sharing set for users member of the sharing set's access groupAll members of the sharing set access group gain access to every record owned by every high volume user associated with that sharing set
High Volume ParentRead only access to the parent account of records shared through a sharing set's access group for users member of the groupMaintains the ability to see the parent account when users are given access to account children owned by high volume users
To allow portal users to scale into the millions, Community users have a streamlined sharing model that does not rely on roles or groups, and functions similarly to calendar events and activities. Community users are provisioned with the Service Cloud Portal or Authenticated Website licenses.


Reference:
https://developer.salesforce.com/docs/atlas.en-us.draes.meta/draes/draes_object_relationships_implicit_sharing.htm?search_text=Skew

Parent-Child Data Skew

 Hi,

Let's learn about Parent-Child Data Skew.

A common configuration that can lead to poor performance is the association of a large number of child records (10,000 or more) with a single parent account. 

Eg: A customer can have tens or hundreds of thousands of contacts generated by marketing campaigns or purchased from mailing lists—without any association to formal business accounts. If contact is required to have an associated account, what should an administrator do? It might be convenient to park all those unallocated contacts under a single dummy account until their real business value and relationship can be determined.

While this option seems reasonable, this kind of parent-child data skew can cause serious performance problems in the maintenance of implicit sharing.

Problem #1: Losing Access to a Child Record Under a Skewed Account

Assume that we have 300,000 unallocated contacts all under the same account. A user with access to one of these contacts will also have a parent implicit share in the account sharing table that gives him or her access to that account. Now, what happens if that user loses access to the contact?

In order to determine whether to remove his or her sharing to the account, Salesforce needs to scan all of the other 299,999 contacts to ensure that the user doesn’t have access to them either. This practice can become expensive if Salesforce is processing a lot of visibility changes on these highly skewed accounts.

Problem #2: Losing Access to the Skewed Parent Account


Consider the opposite scenario: The user has access to all 300,000 contacts because of his or her access to their parent account. What happens when the user loses access to the account?

This situation is not as problematic because the user must lose access to all the child records. Salesforce can query that list very quickly, but if there are very many child records, it might still take substantial time to delete all the relevant rows from the sharing tables for all the child objects.
Configuring a severe data skew on an account can also cause issues when customers make large-scale changes in sharing or realign sales assignments in Territory Management.

Eg: If the account is part of the source group for a sharing rule, and the administrator recalculates sharing on accounts, the work required to adjust the child entity access for that one account can cause the recalculation to become a long-running transaction or, in extreme cases, to fail altogether. Similar problems can occur when a territory realignment process attempts to evaluate assignment rules for a skewed account.


Reference:
https://developer.salesforce.com/docs/atlas.en-us.draes.meta/draes/draes_object_relationships_parent_child_data_skew.htm?search_text=Skew


Friday, 2 October 2020

Ownership Data Skew

 Hi,

Let's see what is ownership Data Skew.

We have different types of  Data Skews. 

  • Ownership Data Skew
  • Parent-Child Data Skew
Here we are going to discuss "Ownership Data Skew".

What is Ownership Data Skew?
When a single user owns more than 10,000 records of an object, we call that condition ownership data skew


One of the common patterns involves customers concentrating ownership of data so that a single user or queue, or all the members of a single role or public group, owns most or all of the records for a particular object.

Eg:

A customer can assign all of his or her unassigned leads to a dummy user. This practice might seem like a convenient way to park unused data, but it can cause performance issues if those users are moved around the hierarchy, or if they are moved into or out of a role or group that is the source group for a sharing rule. In both cases, Salesforce must adjust a very large number of entries in the sharing tables, which can lead to a long-running recalculation of access rights.


Distributing ownership of records across a greater number of users will decrease the chance of long-running updates occurring..

We can take the same approach when dealing with a large amount of data that is owned by or visible to the users under a single portal account—changing the owner of that account or moving those users in the hierarchy requires the system to recalculate all the sharing and inheritance for all the data under the account.


If we do have a compelling reason for assigning ownership to a small number of users, we can minimize possible performance impacts by not assigning the user(s) to a role.

If the user(s) must have a role to share data, Salesforce recommends that we have to:
  • Place them in a separate role at the top of the hierarchy
  • Not move them out of that top-level role
  • Keep them out of public groups that could be used as the source for sharing rules
Reference:
https://developer.salesforce.com/docs/atlas.en-us.draes.meta/draes/draes_group_membership_data_skew.htm?search_text=Data%20Skew

Thursday, 1 October 2020

Visualforce Standard Controller method addFields(fieldNames)

 Hi ,

When a Visualforce page is loaded, the fields accessible to the page are based on the fields referenced in the Visualforce markup. This method adds a reference to each field specified in "fieldNames" so that the controller can explicitly access those fields as well.

Here "fieldNames" data type is List<String> .

The strings in fieldNames can either be the API name of a field, such as AccountId, or they can be explicit relationships to fields, such as something__r.myField__c.

Usage:

This method should be called before a record has been loaded—typically, it's called by the controller's constructor. If this method is called outside of the constructor, you must use the reset() method before calling addFields().

This method is only for controllers used by dynamicVisualforce bindings.

Sample Example:

public AccountController(ApexPages.StandardController stdController){

            this.controller = stdController;

           List<String> fieldNamesList = new List<String>{Type,Industry};

            stdController.addFields(fieldNamesList); 

}

Reference:

https://developer.salesforce.com/docs/atlas.en-us.pages.meta/pages/apex_ApexPages_StandardController_addFields.htm


ContentDocument and ContentDocumentLink trigger behavior in Classic and Lightning on delete trigger

Hi,

Let's have a look at how the triggers behave written on "ContentDocument and ContentDocumentLink" objects in Salesforce Classic and Salesforce Lightning.

Here we are going to discuss what is going to happen when we write a trigger for the "delete" event on the above objects.

In Classic:

ContentDocument triggers do not fire, as Salesforce only deletes the associated ContentDocumentLink record, not the ContentDocument record.

In Lightning Experience:

both the ContentDocument and related ContentDocumentLink records are deleted, and by design Salesforce only fires the trigger on ContentDocument, not the trigger on ContentDocumentLink.

This is working as designed and can be verified by following below steps:

1. Create two "before delete"  triggers: one on ContentDocument, and the other on ContenDocumentLink objects.

2. Place a "system.debug" statement in each which could be verified in the Debug logs.

3. Now upload 2 files to any Object record under 'Files' related list. Once done, you can observe both the uploaded documents under the 'Files' tab.

4. Execute the below queries in the Developer Console.

SELECT Id, LinkedEntityId, ContentDocumentId FROM ContentDocumentLink WHERE LinkedEntityId=<<Id og the document>>

2 records will be returned

SELECT Id, Title FROM ContentDocument WHERE Id=<<ContentDocumentId from the above query>>

2 rows will be returned

5. Set up the Debug logs

 IN CLASSIC: 

Delete one of the uploaded files, by clicking on the 'Del' link besides the document under 'Files' related list.

OBSERVATION:

In Debug logs you will see that only the ContentDocumentLinkTrigger has got fired and the Debug statement present in that Trigger will get displayed.

The document you have deleted will be available under the 'Files' tab

On executing the above 2 queries you will observe that only 1 row is returned for the 1st query and 2 rows for the second query. i.e. Only the ContentDocumentLink is getting removed.

 IN LIGHTNING:

Open the object record and delete the 2nd uploaded file, by clicking on the 'Del' link besides the document under 'Files' related list.

OBSERVATION:

In Debug logs you will see that only the ContentDocumentTrigger has got fired and the Debug statement present in that Trigger will get displayed.

The document you have deleted will no longer be available under the 'Files' tab

On executing the above 2 queries you will observe that no row is returned for the 1st query and 1 row for the second query (the one related to the 1st document). i.e. Both the ContentDocument and the ContentDocumentLink have got removed.

Note: 

We should remember if we are trying to write tirggers on "delete" event on these objects.

This content is from following Salesforce Link.

Reference:

https://help.salesforce.com/articleView?id=000312746&language=en_US&type=1&mode=1


What’s the difference between Einstein Article Recommendations and Suggested Articles?

How Does Einstein Article Recommendations Work? Einstein Article Recommendations helps support agents resolve customer cases efficiently by ...