Sunday 28 March 2021

How to create an unlocked package?

 Hi ,

Here we are going to learn how we can create an unlocked package with simple "sfdx" commands.

Enable Devhub and Unlocked Package, Second Generation package in Developer edition or Production.
Authorize dev hub from Command line or vs code terminal
        sfdx force:auth:web:login -d -a DevHub
It opens a login page then give credentials then you are successfully authorized

Now clone the project from the repository or create a project with all the components in vs code.

Add the following snippet in "sfdx-project.json" file if it is already not there

{

   "packageDirectories": [

      {

         "path": "force-app",

         "default": true

      }

   ],

   "namespace": "",

   "sfdcLoginUrl": "https://login.salesforce.com",

   "sourceApiVersion": "50.0"

}


Now create a package with the help of following command:

sfdx force:package:create --name TestPackage  --description "TestPackage Desc" --packagetype Unlocked --path force-app --nonamespace --targetdevhubusername DevHub

Here DevHub is the alias of our DevHub configured while authorizing the DevHub

 After the creation of the package, it includes different items in sfdx-project.json   file as shown below.

{

    "packageDirectories": [

        {

            "path": "force-app",

            "default": true,

            "includeProfileUserLicenses": true,

            "package": "TestPackage ",

            "versionName": "ver 0.1",

            "versionNumber": "0.1.0.NEXT"

        }

    ],

    "namespace": "",

    "sfdcLoginUrl": "https://login.salesforce.com",

    "sourceApiVersion": "50.0",

    "packageAliases": {

        "TestPackage": "0HoXXXXXXXX"

    }

}

We can change the version number here manually if needed:

{

    "packageDirectories": [

        {

            "path": "force-app",

            "default": true,

            "includeProfileUserLicenses": true,

            "package": "TestPackage ",

            "versionName": "Version 1.0",

            "versionNumber": "1.0.0.NEXT"

        }

    ],

    "namespace": "",

    "sfdcLoginUrl": "https://login.salesforce.com",

    "sourceApiVersion": "50.0",

    "packageAliases": {

        "TestPackage": "0HoXXXXXXXX"

    }

}


Now its time to create a package version with the help of the following command:

sfdx force:package:version:create -p TestPackage -d force-app -k test1234 --wait 10   -v DevHub

or 

if we want to promote this package version then we have add "--codecoverage"  in the command.

sfdx force:package:version:create -p TestPackage -d force-app -k test1234 --wait 10  --codecoverage  -v DevHub

Here -k represents a key or password. If we set up this here then while installation of this package we have to give this password.

--codecoverage:

Calculate and store the code coverage percentage by running the Apex tests included in this package version. 

Before you can promote and release a managed or unlocked package version, the Apex code must meet a minimum 75% code coverage requirement. 

Salesforce doesn’t calculate code coverage for org-dependent unlocked packages or for package versions that specify --skipvalidation.

once the package version is created successfully it generates the package URL.

https://login.salesforce.com/packaging/installPackage.apexp?p0=04txxxxxxxxxxxxxx

we can install this package in our salesforce instance before promoting with the help of the above URL or command but it will beta version.


It also adds a package alias with package id in sfdx-project.json as shown below.

{

    "packageDirectories": [

        {

            "path": "force-app",

            "default": true,

            "includeProfileUserLicenses": true,

            "package": "TestPackage ",

            "versionName": "Version 1.0",

            "versionNumber": "1.0.0.NEXT"

        }

    ],

    "namespace": "",

    "sfdcLoginUrl": "https://login.salesforce.com",

    "sourceApiVersion": "50.0",

    "packageAliases": {

        "TestPackage": "0HoXXXXXXXX",

        "TestPackage@1.0.0-1": "04txxxxxxxxxxx"        

    }

} 


Now promote the package:

sfdx force:package:version:promote -p TestPackage@1.0.0-1 -v DevHub 

When you promote the package then it becomes a release version.

Now install with the help of installation URL above or sfdx command

sfdx force:package:install --wait 10 --publishwait 10 --package TestPackage@1.0.0-1 -k test1234 -r -u targetOrgalias

With this, we have successfully created an unlocked package and promote then install it into our target salesforce org where we want to install it.

Note:

Post-install scripts and uninstallation scripts are not supported in unlocked packages.


Reference:

https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_unlocked_pkg_intro.htm

https://trailhead.salesforce.com/content/learn/modules/unlocked-packages-for-customers


Thursday 25 March 2021

How to override tab name in console application

 Hi,

Here we are going to learn how to override tab name in console application when tab is loaded.

For that we have to create a background utility lightning component and add it into your application utility items.

Sample Component:

utililycomponent.cmp

<aura:component implements="flexipage:availableForAllPageTypes,lightning:backgroundUtilityItem" access="global" >

    <lightning:workspaceAPI aura:id="workspace" />     

    <aura:handler event="lightning:tabCreated" action="{! c.onTabCreated }"/>       

</aura:component>

utililycomponentcontroller.js

{

    onTabCreated: function (component, event, helper) {

        var newTabId = event.getParam('tabId');

        var workspaceAPI = component.find("workspace");

        workspaceAPI.getTabInfo({

            tabId: newTabId

        }).then(function (response) {

               var recId = response.recordId;    

workspaceAPI.setTabLabel({tabId:newTabId, label:'Salesforce Techbook'});

        });

    }

})

This components should include the interface lightning:backgroundUtilityItem to make the lightning component as background utility component.

Here we are using static label. We can make it dynamic as well by calling custom apex class or lightning data service based on record id we are getting in the above code.

Reference:

https://developer.salesforce.com/docs/atlas.en-us.api_console.meta/api_console/sforce_api_console_lightning_setTabLabel.htm




Wednesday 17 March 2021

Sample package.xml

 Hi,

Here we are going to learn how to build package.xml with different components.


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>*</members>
        <name>ApexClass</name>
    </types>

    <types>
        <members>*</members>
        <name>ApexPage</name>
    </types>

    <types>
        <members>*</members>
        <name>ApexTrigger</name>
    </types>

    <types>
        <members>*</members>
        <name>AuraDefinitionBundle</name>
    </types>

    <types>
        <members>*</members>
        <name>CustomApplication</name>
    </types>

    <types>
        <members>*</members>
        <name>CustomField</name>
    </types>

    <types>
        <members>*</members>
        <name>CustomLabels</name>
    </types>

    <types>
        <members>*</members>
        <name>CustomObject</name>
    </types>

    <types>
        <members>*</members>
        <name>CustomTab</name>
    </types>
    <types>
        <members>EmailTemplateFolderName</members>
        <members>EmailTemplateFolderName/TemplateName</members>
        <members>EmailTemplateFolderName/TemplateName</members>
        <name>EmailTemplate</name>
    </types>

    <types>
        <members>*</members>
        <name>FlexiPage</name>
    </types>

    <types>
        <members>*</members>
        <name>Flow</name>
    </types>

    <types>
        <members>*</members>
        <name>GlobalValueSet</name>
    </types>

    <types>
        <members>*</members>
        <name>Layout</name>
    </types>

    <types>
        <members>*</members>
        <name>ListView</name>
    </types>

    <types>
        <members>*</members>
        <name>Profile</name>
    </types>

    <types>
        <members>*</members>
        <name>QuickAction</name>
    </types>

    <types>
        <members>*</members>
        <name>StaticResource</name>
    </types>

    <types>
        <members>*</members>
        <name>Workflow</name>
    </types>

    <types>
        <members>*</members>
        <name>WorkflowAlert</name>
    </types>
    <types>
        <members>DasboardFOlderName</members>
        <members>DasboardFOlderName/Dasboard1</members>
        <members>DasboardFOlderName/Dasboard1</members>
        <name>Dashboard</name>
    </types>
    <types>
        <members>ReportFolderName</members>
        <members>ReportFolderName/ReportName1</members>
        <members>ReportFolderName/ReportName2</members>        
        <name>Report</name>
    </types>
      <types>       
        <members>*</members>
        <name>ReportType</name>
    </types>    
     <types>
        <members>MyDocumentFolder/MyDocumentName</members>
        <name>Document</name>
    </types>
    <version>50.0</version>
</Package>


Reference:

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

Saturday 13 March 2021

HTTP request body for sample Mutation (GraphQL)

 Hi ,

Let's have small sample request body for a mutation.

Mutation:

mutation {

  changeorderstatus(orderId:"8010I0000052b6EQAQ",orderStatus:"Activated"){

    succeeded

  }

}

HTTP  request body:

{"query":"mutation{changeorderstatus(orderId:\\"8010I0000052b6EQAQ\\",'+
    +'orderStatus:\\"Activated\\"){succeeded}}"}



Reference:

Thursday 4 March 2021

Sample Post Install Script for a Push Upgrade

 Hi,

In this sample script, the package upgrade contains new Visualforce pages and a new permission set that grants access to those pages. The script performs the following actions.

  • Gets the Id of the Visualforce pages in the old version of the package
  • Gets the permission sets that have access to those pages
  • Gets the list of profiles associated with those permission sets
  • Gets the list of users who have those profiles assigned
  • Assigns the permission set in the new package to those users
Eg:
global class PostInstallClass implements InstallHandler {
    global void onInstall(InstallContext context) {

        //Get the Id of the Visualforce pages 
        List<ApexPage> pagesList = [SELECT Id FROM ApexPage WHERE NamespacePrefix =  
            'TestPackage' AND Name = 'vfpage1'];

        //Get the permission sets that have access to those pages
        List<SetupEntityAccess> setupEntityAccessList = [SELECT Id, 
            ParentId, SetupEntityId, SetupEntityType FROM SetupEntityAccess 
            WHERE SetupEntityId IN :pagesList];        
        Set<ID> PermissionSetList = new Set<ID> ();

        for (SetupEntityAccess sea : setupEntityAccessList) {
            PermissionSetList.add(sea.ParentId);
        }
        List<PermissionSet> PermissionSetWithProfileIdList = 
            [SELECT id, Name, IsOwnedByProfile, Profile.Name,
            ProfileId FROM PermissionSet WHERE IsOwnedByProfile = true 
            AND Id IN :PermissionSetList];   

        //Get the list of profiles associated with those permission sets
        Set<ID> ProfileList = new Set<ID> ();
        for (PermissionSet per : PermissionSetWithProfileIdList) {
            ProfileList.add(per.ProfileId);
        }

        //Get the list of users who have those profiles assigned
        List<User> UserList = [SELECT id FROM User where ProfileId IN :ProfileList];                

        //Assign the permission set in the new package to those users
        List<PermissionSet> PermissionSetToAssignList = [SELECT id, Name 
            FROM PermissionSet WHERE Name='TestPermSet' AND 
            NamespacePrefix = 'TestPackage'];
        PermissionSet PermissionSetToAssign = PermissionSetToAssignList[0];
        List<PermissionSetAssignment> PermissionSetAssignmentList = new List<PermissionSetAssignment>();
        for (User us : UserList) {
            PermissionSetAssignment psa = new PermissionSetAssignment();
            psa.PermissionSetId = PermissionSetToAssign.id;
            psa.AssigneeId = us.id;
            PermissionSetAssignmentList.add(psa);
        }
        insert PermissionSetAssignmentList;
    }
}

Test Class:
// Test for the post install class
@isTest
private class PostInstallClassTest {
    @isTest           
    public static void test() {
      PostInstallClass myClass = new PostInstallClass();
      Test.testInstall(myClass, null);
    }
}         


Referene:

Tuesday 2 March 2021

Comparison of 2GP and 1GP Managed Packages

 Hi,

If we are familiar with first-generation managed packages (1GP) and wonder how 2GP differs from 1GP, here are some key distinctions.


1GP Managed Packages2GP Managed Packages
The packaging org is the source of truth for the metadata in your package.Your version control system is the source of truth (source-driven system) for the metadata in your package.

And unlike 1GP, 2GP doesn’t use packaging or patch orgs.

The packaging org owns the package. The metadata in the package resides in the packaging org.The Dev Hub owns the package, but the Dev Hub doesn’t contain the packaged metadata.

We recommend that you enable Dev Hub in your Partner Business Org (PBO).

A packaging org can own only one managed package.A Dev Hub can own one or more packages.
The namespace of the managed package is created in the packaging org.The namespace of a managed package is created in a namespace org and linked to the Dev Hub.

See Namespaces for more details. And you can associate multiple namespaces to a single Dev Hub.

A namespace is linked with a 2GP when you run the force:package:create Salesforce CLI command. And you must specify the namespace in the sfdx-project.json file.

A namespace can be associated with only one package.Multiple packages can use the same namespace.
Global Apex is the only way to share code across packages.Multiple packages sharing the same namespace can share code using public Apex classes and methods with @namespaceAccessible annotation.
Some packaging operations, like package create and package uninstall, can’t be automated.All packaging operations can be automated using Salesforce CLI.
Package versioning is linear.Package versioning supports branches.
Patch versions can only be created in specialized orgs called patch orgs.Patch versions are created using Salesforce CLI. The version control system is the source of truth, and there are no patch orgs.

  • Despite these distinctions, 1GP and 2GP managed packages have many things in common. 
  • They share the key packaging concept of associating metadata with a package.
  • They both allow you to iterate and create package and patch versions, which can be installed and uninstalled in subscriber orgs. 
  • Both managed package types enable you to submit a package for AppExchange security review, and list your package on AppExchange. 
  • Both managed package types can use the License Management App, Subscriber Support Console, and Feature Management App.

Reference:

How to include a screen flow in a Lightning Web Component

 Hi, Assume  you have a flow called "Quick Contact Creation" and API Name for the same is "Quick_Contact_Creation". To i...